2010-01-03 29 views

Respuesta

37

estaba a punto de decir que es simple, pero difftime() se detiene en las semanas. Que extraño.

Así que una respuesta posible sería la de cortar algo:

# 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> 

parece correcto. Uno podría envolver esto en una estructura de clase simple. O dejarlo como un truco :)

Editar: parece también para trabajar con sus ejemplos de los 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> 

añadido la indicación EndOfMonth se deja como ejercicio para el lector :)

Editar 2: Tal vez difftime lo omite, ya que no hay una manera confiable de expresar la diferencia de fracciones que sería consistente con el comportamiento difftime para otras unidades.

+0

Gracias, Dirk. Eso es un truco inteligente. Hablando de unidades, descubrí que esto también funciona .. > as.numeric (as.Date ('2009-10-1') - as.Date ('2009-8-01'), units = 'semanas') > as.numeric (as.Date ('2009-10-1') - as.Date ('2009-8-01'), units = 'days') pero se detiene en 'weeks ' también. – knguyen

+0

Creo que es solo una sobrecarga inteligente del operador - para invocar la misma función 'difftime()'. "No hay almuerzo gratis" como dice el refrán :) –

5

Hay un mensaje como el suyo en la lista de correo de R-Help (anteriormente mencioné una lista de CRAN).

Aquí the link. Hay dos soluciones sugeridas:

  • Hay un promedio de 365.25/12 días por mes por lo que la siguiente expresión da el número de meses entre D1 y 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)) 
  • Otra posibilidad es obtener la longitud de seq.Dates de esta manera:
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

Esa sería la lista de correo de R-Help. CRAN es un repositorio de paquetes. – Sharpie

30

una función simple ...

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) 
} 

Ejemplo ...

>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 

Para mí tiene sentido pensar en este problema como simplemente restando dos fechas, y desde minuend − subtrahend = difference (wikipedia), puse la fecha posterior primero en la lista de parámetros.

Tenga en cuenta que funciona bien para las fechas anteriores a 1900 a pesar de esas fechas que tiene representaciones internas de años como negativos, gracias a las reglas para restar números negativos ...

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

Puede haber una manera más sencilla. No es una función, pero es solo una línea.

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

e.g.

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

Produce:

[1] 69 

Este calcula el número de meses enteros entre las dos fechas. Quite el -1 si desea incluir el mes/resto actual que no es un mes completo.

+1

Esta es una gran idea, gracias a Dominic – Victor

+1

Esto no siempre da los resultados esperados. 'length (seq (from = as.Date (" 2015-01-31 "), to = as.Date (" 2015-03-31 "), by = 'month')) = 3' Además, 'length (seq (from = as.Date (" 2015-01-31 "), to = as.Date (" 2015-04-30 "), by = 'mes')) = 3' – Octave1

4
library(lubridate) 

Caso 1: la función ingenua

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

Caso 2: Si es necesario considerar solamente 'Mes', independientemente de 'Día'

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 
} 

Ejemplo:

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

intervalo (mdy (20150101), mdy (20150228))% /% months (1) Simplemente use el intervalo de función nativo de lubridate. – mtelesha

+0

gracias mtelesha. Sin embargo, en mi negocio, necesito calcular 'recuento de meses' de 2 días a menudo. Por ejemplo, los meses entre 20150131 y 20150201 tienen que ser 1. Entonces, solía hacer la función definida por el usuario 'mob'. ¿Sabes cómo hacer esto usando lubridato? –

+0

Creo que podrías usar month (ymd) - month (ymd). O puede hacer tres columnas una por año, mes y día. – mtelesha

12

Creo que esta es una respuesta más cercana a la pregunta preguntó en términos de la paridad con la función de MathWorks

MathWorks función meses

MyMonths = months(StartDate, EndDate, EndMonthFlag) 

Mi código R

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

de salida (como cuando el código se ejecuta en abril de 2016)

[1] 6 

Lubridat e [paquete] proporciona herramientas que facilitan el análisis y la manipulación de las fechas. Estas herramientas se agrupan a continuación por un propósito común. Se puede encontrar más información sobre cada función en su documentación de ayuda.

intervalo {lubridate} crea un objeto Interval-class con las fechas de inicio y finalización especificadas. Si la fecha de inicio ocurre antes de la fecha de finalización, el intervalo será positivo. De lo contrario, será negativo

hoy {lubridate} La fecha actual

meses{base} Extraer el mes Estas son funciones genéricas: los métodos de las clases de fecha y hora internas están documentadas aquí.

% /% {de base} indica división entera AKA (x% /% y) (hasta error de redondeo)

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) 

Diferencia en meses = 12 * diferencia en años + diferencia en meses.

siguiente puede necesitar corregirse utilizando ifelse condición de las sustracciones mes

Cuestiones relacionadas