lubridate 的年时差?

问题描述 投票:0回答:7

我想使用

lubridate
根据出生日期和今天的日期来计算年龄。现在我有这个:

library(lubridate)
today<-mdy(08312015)
dob<-mdy(09071982)
today-dob

这给了我他们的年龄(以天为单位)。

r lubridate
7个回答
68
投票

这是我会采取的

lubridate
方法:

interval(dob, today) / years(1)

产生

32
年的答案。

请注意,该函数会抱怨它无法表达一年中剩余的部分。这是因为年份不是一个固定的概念,闰年是366,非闰年是365。您可以获得有关周数和天数的更详细答案:

interval_period = interval(dob, today)
full_year = interval_period %/% years(1)
remaining_weeks = interval_period %% years(1) %/% weeks(1)
remaining_days = interval_period %% years(1) %% weeks(1) %/% days(1)
sprintf('Your age is %d years, %d weeks and %d days', full_year, remaining_weeks, remaining_days)
# [1] "Your age is 32 years, 51 weeks and 1 days"

请注意,我使用

%/%
进行除法,使用
%%
作为模数,以得到减去整年/周后剩余的周/天。


13
投票

这是一个老问题,但我仍然缺少以下干净的方法。 (Tidyverse 仅适用于

%>%
运算符。)

library(tidyverse)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

interval(dob, today) %>%
  as.numeric('years')

# 32.98015 - you have to decide how to deal with the fraction of a year

6
投票
as.duration(interval(dob,today)) %/% as.duration(years(1))

应该毫无错误地完成工作。


4
投票
as.period(today - dob, unit = "years")

这将给出一条消息,表明这只是一个估计值,因为它没有考虑确切的开始日期和结束日期。


3
投票

另一种 Tidyverse 方法(代码量最短)是

library(tidyverse)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

dob %--% today / ddays(365.25)


0
投票

另一个答案,速度要快得多。请参阅下面的速度测试

as.numeric(today - dob) / 365.25

比较所有答案

library(dplyr)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

interval(dob, today) / years(1)
> 32.98082

as.duration(interval(dob,today)) %/% as.duration(years(1))
> 32

interval(dob, today) %>% as.numeric('years')
> 32.98015

dob %--% today / ddays(365.25)
> 32.98015

as.numeric(today - dob) / 365.25
> 32.98015

我不确定

32.98082
还是
32.98015
更正确。请参阅https://stackoverflow.com/a/32313487/4745348

速度测试

microbenchmark::microbenchmark(
  interval(dob, today) / years(1),
  as.duration(interval(dob,today)) %/% as.duration(years(1)),
  interval(dob, today) %>% as.numeric('years'),
  dob %--% today / ddays(365.25),
  as.numeric(today - dob) / 365.25
)

> Unit: microseconds
>                                                       expr      min        lq       mean    median       uq      max neval
>                              interval(dob, today)/years(1) 1913.601 1996.1510 2172.96001 2059.1005 2102.851 6037.201   100
>  as.duration(interval(dob, today))%/%as.duration(years(1))  749.700  799.1010  912.30394  823.1510  863.751 5078.601   100
>               interval(dob, today) %>% as.numeric("years")  439.701  464.0510  485.31708  480.3010  501.101  591.000   100
>                               dob %--% today/ddays(365.25)  394.501  427.5510  450.37502  443.7010  463.301  620.601   100
>                             as.numeric(today - dob)/365.25   17.400   25.9005   30.66293   32.7515   36.151   52.700   100

0
投票

这得到了基数 R 中年份之间的底差:

> d1=as.Date("2020-02-01");d2=as.Date("2022-01-31")
> as.numeric(format(d2,"%Y"))-as.numeric(format(d1,"%Y"))-as.numeric(format(d2,"%m-%d")<format(d1,"%m-%d"))
[1] 1

这很相似,但在我的长日期向量基准测试中,速度慢了一倍多:

> as.numeric(substr(d2,1,4))-as.numeric(substr(d1,1,4))-as.numeric(substr(d2,5,10)<substr(d1,5,10))
[1] 1

这两个 Lubridate 选项与我的第一个基本 R 选项一样慢:

> floor(interval(d1,d2)/years())
[1] 1
> d1%--%d2%/%years()
[1] 1

这两个 Lubridate 选项将每年的长度视为 365.25 天,因此根据您想要如何处理闰年,它们可能会产生错误的结果:

> floor(lubridate::time_length(difftime(d2,d1),"years"))
[1] 2
> floor(as.numeric(lubridate::interval(d1,d2),"years"))
[1] 2

这与之前的两个选项类似,但在我的基准测试中稍快一些:

> as.numeric(d2-d1)%/%365.25
[1] 2

基准:

d1=as.Date(sample(1e5),"1970-1-1")
d2=as.Date(sample(1e5),"1970-1-1")

bench=\(times,...){
  arg=match.call(expand.dots=F)$...
  l=length(arg);
  out=double(times*l)
  rand=sample(rep(1:l,times))
  n=1
  for(x in arg[rand]){t1=Sys.time();eval.parent(x);t2=Sys.time()-t1;out[n]=t2;n=n+1}
  setNames(out,sapply(arg[rand],\(x)gsub("  +"," ",paste(deparse(x),collapse=" "))))
}

b=bench(10,
  floor(interval(d1,d2)/years()),
  d1%--%d2%/%years(),
  floor(time_length(difftime(d2,d1),"years")),
  as.numeric(d2-d1)%/%365.25,
  floor(as.numeric(interval(d1,d2),"years")),
  as.numeric(substr(d2,1,4))-as.numeric(substr(d1,1,4))-as.numeric(substr(d2,5,10)<substr(d1,5,10)),
  as.numeric(format(d2,"%Y"))-as.numeric(format(d1,"%Y"))-as.numeric(format(d2,"%m-%d")<format(d1,"%m-%d"))
)

o=sort(tapply(b,names(b),median))
writeLines(sprintf("%.1f %s",o/min(o),names(o)))

输出显示相对于最快选项的十次运行的中位时间:

1.0 as.numeric(d2 - d1)%/%365.25
1.1 floor(time_length(difftime(d2, d1), "years"))
1.2 floor(as.numeric(interval(d1, d2), "years"))
82.5 d1 %--% d2%/%years()
82.8 floor(interval(d1, d2)/years())
102.2 as.numeric(format(d2, "%Y")) - as.numeric(format(d1, "%Y")) - as.numeric(format(d2, "%m-%d") < format(d1, "%m-%d"))
227.5 as.numeric(substr(d2, 1, 4)) - as.numeric(substr(d1, 1, 4)) - as.numeric(substr(d2, 5, 10) < substr(d1, 5, 10))
© www.soinside.com 2019 - 2024. All rights reserved.