我想使用
lubridate
根据出生日期和今天的日期来计算年龄。现在我有这个:
library(lubridate)
today<-mdy(08312015)
dob<-mdy(09071982)
today-dob
这给了我他们的年龄(以天为单位)。
这是我会采取的
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"
请注意,我使用
%/%
进行除法,使用 %%
作为模数,以得到减去整年/周后剩余的周/天。
这是一个老问题,但我仍然缺少以下干净的方法。 (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
as.duration(interval(dob,today)) %/% as.duration(years(1))
应该毫无错误地完成工作。
as.period(today - dob, unit = "years")
这将给出一条消息,表明这只是一个估计值,因为它没有考虑确切的开始日期和结束日期。
另一种 Tidyverse 方法(代码量最短)是
library(tidyverse)
library(lubridate)
today<-mdy(08312015)
dob<-mdy(09071982)
dob %--% today / ddays(365.25)
另一个答案,速度要快得多。请参阅下面的速度测试
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
这得到了基数 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))