【问题标题】:Get the difference between dates in terms of weeks, months, quarters, and years以周、月、季度和年的形式获取日期之间的差异
【发布时间】:2013-01-05 10:37:15
【问题描述】:

我有两个约会,比如说14.01.2013 和26.03.2014。

我想根据周(?)、月(在示例 14 中)、季度(4)和年(1)来计算这两个日期之间的差异。

你知道获得这个的最佳方法吗?

【问题讨论】:

  • 这几周我发现了以下 difftime(time1,time2,units="weeks")。不幸的是,这几个月、几个季度、几年都不起作用。

标签: r date


【解决方案1】:

这个呢:

# get difference between dates `"01.12.2013"` and `"31.12.2013"`

# weeks
difftime(strptime("26.03.2014", format = "%d.%m.%Y"),
strptime("14.01.2013", format = "%d.%m.%Y"),units="weeks")
Time difference of 62.28571 weeks

# months
(as.yearmon(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearmon(strptime("14.01.2013", format = "%d.%m.%Y")))*12
[1] 14

# quarters
(as.yearqtr(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearqtr(strptime("14.01.2013", format = "%d.%m.%Y")))*4
[1] 4

# years
year(strptime("26.03.2014", format = "%d.%m.%Y"))-
year(strptime("14.01.2013", format = "%d.%m.%Y"))
[1] 1

as.yearmon() 和 as.yearqtr() 在包 zoo 中。 year() 在包 lubridate 中。 你怎么看?

【讨论】:

  • 这个答案需要谨慎...它将认为 2013 年 12 月 31 日与第二天 2014 年 1 月 1 日相差 1 年。有时这是需要的,但通常不是。
  • 扩展 Gregor 的警告:year 将给出日历年的差异,因此如果您需要知道一年中某个部分的差异,则不适合.
  • 'format' + 默认值可以使输入更容易: difftime(format("2014-03-26"), format("2013-01-14"), units = "weeks")时差 62.28571 周
【解决方案2】:

所有现有的答案都是不完美的 (IMO),要么对期望的输出做出假设,要么不为期望的输出提供灵活性。

根据 OP 中的示例以及 OP 声明的预期答案,我认为这些是您正在寻找的答案(加上一些易于推断的其他示例)。

(这只需要base R,不需要zoo或lubridate)

转换为日期时间对象

date_strings = c("14.01.2013", "26.03.2014")
datetimes = strptime(date_strings, format = "%d.%m.%Y") # convert to datetime objects

天数差异

您可以在几天内使用差异来获得我们以后的一些答案

diff_in_days = difftime(datetimes[2], datetimes[1], units = "days") # days
diff_in_days
#Time difference of 435.9583 days

周数差异

周差是units = "weeks" in difftime() 的特例

diff_in_weeks = difftime(datetimes[2], datetimes[1], units = "weeks") # weeks
diff_in_weeks
#Time difference of 62.27976 weeks

请注意,这与将 diff_in_days 除以 7(一周 7 天)相同

as.double(diff_in_days)/7
#[1] 62.27976

年份差异

通过类似的逻辑,我们可以从 diff_in_days 推导出年份

diff_in_years = as.double(diff_in_days)/365 # absolute years
diff_in_years
#[1] 1.194406

您似乎期望年份的差异为“1”,所以我假设您只想计算绝对日历年或其他东西,您可以使用floor()轻松做到这一点

# get desired output, given your definition of 'years'
floor(diff_in_years)
#[1] 1

季度差异

# get desired output for quarters, given your definition of 'quarters'
floor(diff_in_years * 4)
#[1] 4

月差

可以将其计算为 diff_years 的转换

# months, defined as absolute calendar months (this might be what you want, given your question details)
months_diff = diff_in_years*12
floor(month_diff)
#[1] 14

我知道这个问题很老,但鉴于我刚才还必须解决这个问题,我想我会添加我的答案。希望能帮助到你。

【讨论】:

  • 我认为这不起作用,当months_diff 为
  • @timat 你能举一个具体的例子说明这对你不起作用的两个日期字符串吗?
  • date_strings = c("14.07.2014", "10.03.2015") 根据第一个定义,给-4 7个月..
  • @timat 你是对的!我不知道为什么当我写这篇文章时,我不只是直接从diff_in_years 计算月份,例如,在你的例子中,真正的答案是已经过去了将近 8 个月。您只需通过diff_in_years*12 = 7.857534 即可得到正确答案我更正了我的答案-谢谢。
  • 请记住,当您将天除以 365 以获得年份时,由于闰年,它仅适用于 4 年中的 3 年。除以365.25 会更精确,尤其是在计算年龄时。
【解决方案3】:

几周,你可以使用函数difftime:

date1 <- strptime("14.01.2013", format="%d.%m.%Y")
date2 <- strptime("26.03.2014", format="%d.%m.%Y")
difftime(date2,date1,units="weeks")
Time difference of 62.28571 weeks

但difftime 不适用于持续数周。
以下是在这些持续时间内使用 cut.POSIXt 的非常次优的解决方案,但您可以解决它:

seq1 <- seq(date1,date2, by="days")
nlevels(cut(seq1,"months"))
15
nlevels(cut(seq1,"quarters"))
5
nlevels(cut(seq1,"years"))
2

但是,这是您的时间间隔所跨越的月数、季度数或年数,而不是以月、季度、年表示的时间间隔的持续时间(因为它们没有固定的持续时间)。考虑到您对@SvenHohenstein 回答的评论,我认为您可以使用nlevels(cut(seq1,"months")) - 1 来实现您想要实现的目标。

【讨论】:

    【解决方案4】:

    我只是为了另一个问题而写的,然后在这里偶然发现。

    library(lubridate)
    
    #' Calculate age
    #' 
    #' By default, calculates the typical "age in years", with a
    #' \code{floor} applied so that you are, e.g., 5 years old from
    #' 5th birthday through the day before your 6th birthday. Set
    #' \code{floor = FALSE} to return decimal ages, and change \code{units}
    #' for units other than years.
    #' @param dob date-of-birth, the day to start calculating age.
    #' @param age.day the date on which age is to be calculated.
    #' @param units unit to measure age in. Defaults to \code{"years"}. Passed to \link{\code{duration}}.
    #' @param floor boolean for whether or not to floor the result. Defaults to \code{TRUE}.
    #' @return Age in \code{units}. Will be an integer if \code{floor = TRUE}.
    #' @examples
    #' my.dob <- as.Date('1983-10-20')
    #' age(my.dob)
    #' age(my.dob, units = "minutes")
    #' age(my.dob, floor = FALSE)
    age <- function(dob, age.day = today(), units = "years", floor = TRUE) {
        calc.age = interval(dob, age.day) / duration(num = 1, units = units)
        if (floor) return(as.integer(floor(calc.age)))
        return(calc.age)
    }
    

    用法示例:

    my.dob <- as.Date('1983-10-20')
    
    age(my.dob)
    # [1] 31
    
    age(my.dob, floor = FALSE)
    # [1] 31.15616
    
    age(my.dob, units = "minutes")
    # [1] 16375680
    
    age(seq(my.dob, length.out = 6, by = "years"))
    # [1] 31 30 29 28 27 26
    

    【讨论】:

    • 'new_interval' 已弃用;改用“间隔”。在版本“1.5.0”中已弃用。
    • 我只是为了另一个问题写了这篇文章,然后在这里偶然发现。我也是!小建议:在 if 语句后使用大括号:if (floor) { ... } 并且仅在函数中途返回某些内容时使用 return。最后一行应该是calc.age。
    • @MSBerends 这些只是风格指南。我更喜欢用我的函数明确地return - 我发现它更清晰。当然,在你自己的代码中,使用任何适合你的风格。
    • 非常正确。关于函数:在这种情况下它不起作用:1950-01-17 和 2015-01-01。它返回65,但此人在 2015 年 1 月 17 日之前不会满 65 岁……知道为什么吗?
    • 这很奇怪!我会更多地研究它。问题似乎是2013年,如果你定义yy = seq.Date(from = as.Date("2010-01-01"), to = as.Date("2015-01-01"), by = "year")然后尝试age(dob = as.Date("1950-01-17"), age.day = yy),结果会跳过62。而且只有DOB在1949和1952之间。很奇怪...
    【解决方案5】:

    这里有一个解决方案:

    dates <- c("14.01.2013", "26.03.2014")
    
    # Date format:
    dates2 <- strptime(dates, format = "%d.%m.%Y")
    
    dif <- diff(as.numeric(dates2)) # difference in seconds
    
    dif/(60 * 60 * 24 * 7) # weeks
    [1] 62.28571
    dif/(60 * 60 * 24 * 30) # months
    [1] 14.53333
    dif/(60 * 60 * 24 * 30 * 3) # quartes
    [1] 4.844444
    dif/(60 * 60 * 24 * 365) # years
    [1] 1.194521
    

    【讨论】:

    • 感谢您,但您的解决方案并非在所有情况下都有效。例如,如果您选择日期
    • 虽然仍然不准确,但我建议使用 365.242 来表示一年中的天数,而不是 365。
    【解决方案6】:

    这里仍然缺少lubridate 的答案(尽管Gregor's function 是建立在这个包上的)

    lubridate timespan documentation 非常有助于理解期间和持续时间之间的区别。我也喜欢lubridate cheatsheet 和this very useful thread

    library(lubridate)
    
    dates <- c(dmy('14.01.2013'), dmy('26.03.2014'))
    
    span <- dates[1] %--% dates[2] #creating an interval object
    
    #creating period objects 
    as.period(span, unit = 'year') 
    #> [1] "1y 2m 12d 0H 0M 0S"
    as.period(span, unit = 'month')
    #> [1] "14m 12d 0H 0M 0S"
    as.period(span, unit = 'day')
    #> [1] "436d 0H 0M 0S"
    

    期间不接受以周为单位。但是您可以将 durations 转换为周:

    as.duration(span)/ dweeks(1)
    #makes duration object (in seconds) and divides by duration of a week (in seconds)
    #> [1] 62.28571
    

    由reprex package (v0.3.0) 于 2019 年 11 月 4 日创建

    【讨论】:

      【解决方案7】:

      试试这个几个月的解决方案

      StartDate <- strptime("14 January 2013", "%d %B %Y") 
      EventDates <- strptime(c("26 March 2014"), "%d %B %Y") 
      difftime(EventDates, StartDate) 
      

      【讨论】:

      • 嗨,瑞秋,谢谢你,但这不起作用。当我运行 strptime("14 January 2013", "%d %B %Y") 我得到 NA。
      • Same here.. 如果我使用这一步,我会得到 NAs
      • 此解决方案仅适用于英语语言环境。使用 %m 和数字月份(例如 1 表示一月)而不是 %B 更安全。
      【解决方案8】:

      更“精确”的计算。也就是说,不完整的周/月/季度/年的周/月/季度/年数是该周/月/季度/年中日历天数的分数。例如,2016-02-22 和 2016-03-31 之间的月数为 8/29 + 31/31 = 1.27586

      内嵌代码的解释

      #' Calculate precise number of periods between 2 dates
      #' 
      #' @details The number of week/month/quarter/year for a non-complete week/month/quarter/year 
      #'     is the fraction of calendar days in that week/month/quarter/year. 
      #'     For example, the number of months between 2016-02-22 and 2016-03-31 
      #'     is 8/29 + 31/31 = 1.27586
      #' 
      #' @param startdate start Date of the interval
      #' @param enddate end Date of the interval
      #' @param period character. It must be one of 'day', 'week', 'month', 'quarter' and 'year'
      #' 
      #' @examples 
      #' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "month"), 15/29 + 1)
      #' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "quarter"), (15 + 31)/(31 + 29 + 31))
      #' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "year"), (15 + 31)/366)
      #' 
      #' @return exact number of periods between
      #' 
      numPeriods <- function(startdate, enddate, period) {
      
          numdays <- as.numeric(enddate - startdate) + 1
          if (grepl("day", period, ignore.case=TRUE)) {
              return(numdays)
      
          } else if (grepl("week", period, ignore.case=TRUE)) {
              return(numdays / 7)
          }
      
          #create a sequence of dates between start and end dates
          effDaysinBins <- cut(seq(startdate, enddate, by="1 day"), period)
      
          #use the earliest start date of the previous bins and create a breaks of periodic dates with
          #user's period interval
          intervals <- seq(from=as.Date(min(levels(effDaysinBins)), "%Y-%m-%d"), 
              by=paste("1",period), 
              length.out=length(levels(effDaysinBins))+1)
      
          #create a sequence of dates between the earliest interval date and last date of the interval
          #that contains the enddate
          allDays <- seq(from=intervals[1],
              to=intervals[intervals > enddate][1] - 1,
              by="1 day")
      
          #bin all days in the whole period using previous breaks
          allDaysInBins <- cut(allDays, intervals)
      
          #calculate ratio of effective days to all days in whole period
          sum( tabulate(effDaysinBins) / tabulate(allDaysInBins) )
      } #numPeriods
      

      如果您发现上述解决方案不起作用的更多边界情况,请告诉我。

      【讨论】:

        【解决方案9】:

        这是使用lubridate 包找出年份差异的简单方法:

        as.numeric(as.Date("14-03-2013", format = "%d-%m-%Y") %--% as.Date("23-03-2014", format = "%d-%m-%Y"), "years")
        

        这将返回 1.023956

        如果你不想要小数,你可以使用floor()。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-11
          • 1970-01-01
          • 2019-02-05
          • 2022-01-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多