两个日期之间的月数


Answers:


63

我只是说这很简单,但要等difftime()上几个星期。真奇怪

因此,一个可能的答案是修改一些东西:

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

似乎正确。可以将其包装到一些简单的类结构中。或将其作为hack :)

编辑:似乎也与您的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> 

EndOfMonth作为练习留给读者添加标记:)

编辑2:也许difftime没有考虑,因为没有可靠的方式来表达分数差异,这与difftime其他单位的行为是一致的。


谢谢,德克。这是一个聪明的把戏。说到单位,我发现这也有效。>> as.numeric(as.Date('2009-10-1')-as.Date('2009-8-01'),units ='weeks') > as.numeric(as.Date('2009-10-1')-as.Date('2009-8-01'),unit ='days'),但它也会在'weeks'处停止。
knguyen 2010年

我认为-运算符的聪明重载可以调用相同的difftime()函数。俗话说“没有免费的午餐” :)
Dirk Eddelbuettel

我发现对于大型数据集,这非常慢。下面的Manoel Galdino的答案要快得多。
anotherfred '18

1
@anotherfred并可能是错误的。快速示例是日期2017-01-31和2018-03-01。给“13”在这里与他的第一个答案(一旦我们添加缺少的as.numeric(),而“12”与第二个答案我的回答给了“14”作为一个希望。
德克Eddelbuettel

@DirkEddelbuettel对不起,应该澄清-我的意思是他的第一种方法对像我这样的情况很有用,我的数据集很大,但是所有日期都是该月的1号。
anotherfred

49

一个简单的功能...

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

例...

>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

对我来说,将这个问题考虑为简单地减去两个日期是有意义的,并且由于minuend − subtrahend = difference(wikipedia),我将后面的日期放在参数列表中。

请注意,由于减去负数的规则,尽管日期内部具有年份的负数,但对于1900年之前的日期它仍然可以正常工作...

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

44

我认为这是对与MathWorks函数奇偶性有关的问题的较接近答案

MathWorks月功能

MyMonths = months(StartDate, EndDate, EndMonthFlag)

我的R代码

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

输出(如代码在2016年4月运行时)

[1] 6

Lubridate [package]提供的工具使解析和操作日期变得更加容易。这些工具按通用目的分类在下面。有关每个功能的更多信息,请参见其帮助文档。

interval {lubridate}创建一个具有指定开始日期和结束日期的Interval-class对象。如果开始日期早于结束日期,则间隔为正。否则将为负

今天{lubridate}当前日期

months {Base}提取month这些是通用功能:内部日期时间类的方法在此处记录。

%/%{base}表示整数除法AKA(x%/%y)(最大舍入误差)


这对我有用,而其他一些则没有。
以示例方式学习统计数据(

41

可能有一种更简单的方法。它不是一个函数,但仅一行。

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

例如

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

产生:

[1] 69

这将计算两个日期之间的整个月数。如果要包括当前月份/非整个月的余数,请删除-1。


4
这并不总是能给出预期的结果。 length(seq(from=as.Date("2015-01-31"), to=as.Date("2015-03-31"), by='month')) = 3此外, length(seq(from=as.Date("2015-01-31"), to=as.Date("2015-04-30"), by='month')) = 3
Octave1年

15

R-Help邮件列表中有一条与您一样的消息(以前我提到过CRAN列表)。

这里的链接。有两种建议的解决方案:

  • 每月平均365.25 / 12天,因此以下表达式给出了d1和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))
  • 另一种可能性是得到这样的长度seq.Dates
as.Date.numeric <- function(x) structure(floor(x+.001), class = "Date")
sapply(d2, function(d2) length(seq(d1, as.Date(d2), by = "month")))-1

2
假定所有日期之间的整数都是整数,但很有用。
anotherfred

5
library(lubridate)

案例1:朴素的功能

mos<-function (begin, end) {
      mos1<-as.period(interval(ymd(begin),ymd(end)))
      mos<-mos1@year*12+mos1@month
      mos
}

案例2:如果您只考虑“月”而不考虑“天”

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<-mob1@year*12+mob1@month
      mob
}

范例:

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

interval(mdy(20150101),mdy(20150228))%/%月(1)只需使用lubridate的本机函数间隔。
mtelesha

谢谢mtelesha。但是,在我的业务中,我需要经常计算2天的“月数”。例如,20150131到20150201之间的月份必须为1。因此,我曾经使用“ mob”用户定义功能。您知道如何使用lubridate制成吗?
hyunwoo jeong

我认为您可以使用month(ymd)-month(ymd)。或者,您可以将三列分别设置为年,月和日。
mtelesha

可以,但是如果年份不同,它将无法正常工作。例如,month(ymd(20170101))-month(ymd(20161231))= -11不是1
hyunwoo jeong

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

月差异= 12 *年差异+月差异。

以下情况可能需要使用ifelse月份减去的条件进行校正


也许值得一提的是,这导致了负数-因此,要么更改date1 / date2要么使用abs(etc) 。我认为这是一个非常简单,直接的解决方案,只需花guestimate几个月的时间
tjebo

3

对我来说,这是有效的:

library(lubridate)

Pagos$Datediff <- (interval((Pagos$Inicio_FechaAlta), (Pagos$Inicio_CobFecha)) %/% months(1))

输出是两个日期之间的月数,并存储在Pagos数据框的列中。


感谢您的贡献,但这不会给小数位。有什么建议吗?
Mohamed Rahouma '20

恐怕这只会给您几个月的时间。但不要用逗号。我的观点是,应该以天为单位进行计算。对于您的情况,我可能会更精确。
EDUARDO ROLDAN

2

您可以从zoo包中使用as.yearmon()函数。此函数将Date类型变量转换为Year-month类型变量。您可以减去两个Year-month类型变量,然后乘以12以得到月份的差异,如下所示:

12 * (as.yearmon(Date1) - as.yearmon(Date2))

2
通常不鼓励仅使用代码的答案。您能否添加一个解释,说明为什么这种方法行之有效或为什么它比其他解决方案更好?
杰森

@Jason刚刚做到了!
郑丰

这是可行的,但是as.yearmon()zoo包的一部分,而不是R base的一部分。如果您安装了动物园:12 * (zoo::as.yearmon(Date1) - zoo::as.yearmon(Date2))
马克·艾格

0

日期差异(以月为单位)

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

0

另一个简便的方法是:

day1 <- as.Date('1991/04/12')
day2 <- as.Date('2019/06/10')
round(as.numeric(day2 - day1)/30.42)

[1] 338

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.