2010-01-03 2 views

risposta

37

Stavo per dire che è semplice, ma si ferma a difftime() settimane. Che strano.

Quindi una possibile risposta potrebbe essere quella di incidere qualcosa:

# turn a date into a 'monthnumber' relative to an origin 
R> monnb <- function(d) { lt <- as.POSIXlt(as.Date(d, origin="1900-01-01")); \ 
          lt$year*12 + lt$mon } 
# compute a month difference as a difference between two monnb's 
R> mondf <- function(d1, d2) { monnb(d2) - monnb(d1) } 
# take it for a spin 
R> mondf(as.Date("2008-01-01"), Sys.Date()) 
[1] 24 
R> 

sembra sul punto giusto. Uno potrebbe avvolgere questo in una semplice struttura di classe. O lasciarlo come un hack :)

Edit: sembra anche lavorare con i tuoi esempi di The MathWorks:

R> mondf("2000-05-31", "2000-06-30") 
[1] 1 
R> mondf(c("2002-03-31", "2002-04-30", "2002-05-31"), "2002-06-30") 
[1] 3 2 1 
R> 

Aggiunta la bandiera EndOfMonth viene lasciato come esercizio per il lettore :)

Modifica 2: Forse difftime lascia fuori perché non esiste un modo affidabile per esprimere la differenza frazionale che sarebbe coerente con il comportamento difftime per le altre unità.

+0

Grazie, Dirk. Questo è un trucco intelligente. Parlando di unità, ho scoperto che questo funziona anche .. > as.numeric (as.Date ('2009-10-1') - as.Date ('2009-8-01'), units = 'settimane') > as.numeric (as.Date ('2009-10-1') - as.Date ('2009-8-01'), units = 'days') ma si ferma a 'settimane ' anche. – knguyen

+0

Penso che sia solo un intelligente sovraccarico dell'operatore - per invocare la stessa funzione 'difftime()'. "No Free Lunch" come dice il proverbio :) –

5

C'è un messaggio proprio come il tuo nella mailing list di R-Help (in precedenza ho menzionato un elenco CRAN).

Qui the link. Ci sono due soluzioni suggerite:

  • ci sono una media di 365,25/12 giorni al mese in modo dalla seguente espressione indica il numero di mesi tra d1 e d2:
#test data 
d1 <- as.Date("01 March 1950", "%d %B %Y")  
d2 <- as.Date(c("01 April 1955", "01 July 1980"), "%d %B %Y") 
# calculation 
round((d2 - d1)/(365.25/12)) 
  • Un'altra possibilità è ottenere la lunghezza di seq.Dates in questo modo:
as.Date.numeric <- function(x) structure(floor(x+.001), class = "Date") 
sapply(d2, function(d2) length(seq(d1, as.Date(d2), by = "month")))-1 
+0

Questa sarebbe la mailing list di R-Help. CRAN è un repository di pacchetti. – Sharpie

30

una semplice funzione ...

elapsed_months <- function(end_date, start_date) { 
    ed <- as.POSIXlt(end_date) 
    sd <- as.POSIXlt(start_date) 
    12 * (ed$year - sd$year) + (ed$mon - sd$mon) 
} 

Esempio ...

>Sys.time() 
[1] "2014-10-29 15:45:44 CDT" 
>elapsed_months(Sys.time(), as.Date("2012-07-15")) 
[1] 27 
>elapsed_months("2002-06-30", c("2002-03-31", "2002-04-30", "2002-05-31")) 
[1] 3 2 1 

Per me ha senso pensare a questo problema come semplicemente sottraendo due date, e dal minuend − subtrahend = difference (wikipedia), ho inserito la data successiva prima nell'elenco dei parametri.

Si noti che tutto funziona bene per le date che precedono 1900 nonostante quelle date avendo rappresentazioni interne dell'anno come negativo, grazie alle regole per sottrarre i numeri negativi ...

> elapsed_months("1791-01-10", "1776-07-01") 
[1] 174 
24

Ci può essere un modo più semplice. Non è una funzione ma è solo una riga.

length(seq(from=date1, to=date2, by='month')) - 1 

, ad es.

> length(seq(from=Sys.Date(), to=as.Date("2020-12-31"), by='month')) - 1 

produce:

[1] 69 

Questo calcola il numero di mesi interi tra le due date. Rimuovi il -1 se desideri includere il mese/resto corrente che non è un mese intero.

+1

Questa è una grande idea, grazie Dominic – Victor

+1

Questo non sempre dà risultati attesi. lunghezza '(seq (da = as.Date (" 2015-01-31 "), a = as.Date (" 2015-03-31 "), da = 'month')) = 3' Inoltre, lunghezza '(seq (da = as.Date (" 2015-01-31 "), a = as.Date (" 2015-04-30 "), da = 'month')) = 3' – Octave1

4
library(lubridate) 

case1: funzione ingenuo

mos<-function (begin, end) { 
     mos1<-as.period(interval(ymd(begin),ymd(end))) 
     mos<[email protected]*[email protected] 
     mos 
} 

case2: se avete bisogno di prendere in considerazione solo 'Mese' a prescindere 'Day'

mob<-function (begin, end) { 
     begin<-paste(substr(begin,1,6),"01",sep="") 
     end<-paste(substr(end,1,6),"01",sep="") 
     mob1<-as.period(interval(ymd(begin),ymd(end))) 
     mob<[email protected]*[email protected] 
     mob 
} 

Esempio:

mos(20150101,20150228) # 1 
mos(20150131,20150228) # 0 
# you can use "20150101" instead of 20150101 

mob(20150131,20150228) # 1 
mob(20150131,20150228) # 1 
# you can use a format of "20150101", 20150101, 201501 
+0

intervallo (mdy (20150101), mdy (20150228))% /% months (1) Basta usare l'intervallo di funzione nativa del lubridato. – mtelesha

+0

grazie mtelesha. Tuttavia, nella mia azienda, ho bisogno di calcolare "conteggio mensile" di 2 giorni spesso. Ad esempio, i mesi tra 20150131 e 20150201 devono essere 1. Quindi, ho utilizzato la funzione definita dall'utente "mob". Sai come farlo usando il lubridate? –

+0

Penso che potresti usare il mese (ymd) - month (ymd). Oppure puoi creare tre colonne una per anno, mese e giorno. – mtelesha

12

Penso che questa sia una risposta più vicina alla domanda ha chiesto in termini di parità con funzione di MathWorks

MathWorks mesi funzione

MyMonths = months(StartDate, EndDate, EndMonthFlag) 

Il mio codice R

library(lubridate) 
interval(mdy(10012015), today()) %/% months(1) 

uscita (come quando il codice è stato eseguito nel mese di aprile 2016)

[1] 6 

Lubridat e [pacchetto] fornisce strumenti che semplificano l'analisi e la modifica delle date. Questi strumenti sono raggruppati sotto per uno scopo comune. Ulteriori informazioni su ciascuna funzione sono disponibili nella documentazione della guida.

intervallo {lubridate} crea un oggetto di classe Interval con le date di inizio e fine specificate. Se la data di inizio si verifica prima della data di fine, l'intervallo sarà positivo. In caso contrario, sarà negativo

oggi {lubridate} La data corrente

mesi{base} Estrarre il mese Queste sono funzioni generiche: i metodi per le classi data-ora interni sono documentate Qui.

% /% {basamento} indica la divisione intera AKA (x% /% y) (fino a errore di arrotondamento)

3
library(lubridate) 
date1 = "1 April 1977" 
date2 = "7 July 2017" 

date1 = dmy(date1) 
date2 = dmy(date2) 
number_of_months = (year(date1) - year(date2)) * 12 + month(date1) - month(date2) 

Differenza di mesi = 12 * differenza di anni + differenza in mesi

In seguito potrebbe essere necessario essere corretta mediante ifelse condizioni per le sottrazioni mese