【发布时间】:2015-09-16 13:38:30
【问题描述】:
在给定出生日期和任意日期的情况下,我面临着计算年龄(以年、月或周为单位)的常见任务。问题是我经常需要对许多记录(>3 亿)执行此操作,因此性能是这里的关键问题。
在 SO 和 Google 中快速搜索后,我找到了 3 个替代方案:
- 一个常用的算术过程 (/365.25) (link)
- 使用包
lubridate(link)中的函数new_interval()和duration() - 函数
age_calc()来自包eeptools(link,link,link)
所以,这是我的玩具代码:
# Some toy birthdates
birthdate <- as.Date(c("1978-12-30", "1978-12-31", "1979-01-01",
"1962-12-30", "1962-12-31", "1963-01-01",
"2000-06-16", "2000-06-17", "2000-06-18",
"2007-03-18", "2007-03-19", "2007-03-20",
"1968-02-29", "1968-02-29", "1968-02-29"))
# Given dates to calculate the age
givendate <- as.Date(c("2015-12-31", "2015-12-31", "2015-12-31",
"2015-12-31", "2015-12-31", "2015-12-31",
"2050-06-17", "2050-06-17", "2050-06-17",
"2008-03-19", "2008-03-19", "2008-03-19",
"2015-02-28", "2015-03-01", "2015-03-02"))
# Using a common arithmetic procedure ("Time differences in days"/365.25)
(givendate-birthdate)/365.25
# Use the package lubridate
require(lubridate)
new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years")
# Use the package eeptools
library(eeptools)
age_calc(dob = birthdate, enddate = givendate, units = "years")
我们稍后再谈准确性,首先关注性能。代码如下:
# Now let's compare the performance of the alternatives using microbenchmark
library(microbenchmark)
mbm <- microbenchmark(
arithmetic = (givendate - birthdate) / 365.25,
lubridate = new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years"),
eeptools = age_calc(dob = birthdate, enddate = givendate,
units = "years"),
times = 1000
)
# And examine the results
mbm
autoplot(mbm)
结果如下:
底线:lubridate 和 eeptools 函数的性能比算术方法差得多(/365.25 至少快 10 倍)。不幸的是,算术方法不够准确,我无法承受这种方法会犯的一些错误。
“因为现代公历的方式 构造,没有简单的算术 产生一个人的年龄的方法,根据 常用用法——常用用法意味着一个人的 年龄应该始终是一个整数,恰好增加 生日”。(link)
正如我在一些帖子中看到的,lubridate 和 eeptools 没有犯这样的错误(不过,我还没有查看代码/阅读更多关于这些函数的信息以了解它们使用哪种方法),这就是我想要的原因使用它们,但它们的性能不适用于我的实际应用程序。
关于计算年龄的有效且准确的方法有什么想法吗?
编辑
Ops,看来lubridate 也犯了错误。而且显然基于这个玩具示例,它比算术方法犯的错误更多(见第 3、6、9、12 行)。 (我是不是做错了什么?)
toy_df <- data.frame(
birthdate = birthdate,
givendate = givendate,
arithmetic = as.numeric((givendate - birthdate) / 365.25),
lubridate = new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years"),
eeptools = age_calc(dob = birthdate, enddate = givendate,
units = "years")
)
toy_df[, 3:5] <- floor(toy_df[, 3:5])
toy_df
birthdate givendate arithmetic lubridate eeptools
1 1978-12-30 2015-12-31 37 37 37
2 1978-12-31 2015-12-31 36 37 37
3 1979-01-01 2015-12-31 36 37 36
4 1962-12-30 2015-12-31 53 53 53
5 1962-12-31 2015-12-31 52 53 53
6 1963-01-01 2015-12-31 52 53 52
7 2000-06-16 2050-06-17 50 50 50
8 2000-06-17 2050-06-17 49 50 50
9 2000-06-18 2050-06-17 49 50 49
10 2007-03-18 2008-03-19 1 1 1
11 2007-03-19 2008-03-19 1 1 1
12 2007-03-20 2008-03-19 0 1 0
13 1968-02-29 2015-02-28 46 47 46
14 1968-02-29 2015-03-01 47 47 47
15 1968-02-29 2015-03-02 47 47 47
【问题讨论】:
-
如果有比
lubridate更快/更简单的方法,我会感到惊讶。如果您真的需要性能提升,我唯一的建议是先执行算术方法,然后使用 lubridate 方法重做所有“关闭调用”(例如,如果abs(floor(age) - age) < 0.01)然后使用 lubridate) -
谢谢。你是
lubridate的经验丰富的用户吗?,...,正如我在编辑的问题中所说的那样,我发现它会出错(可能不仅仅是算术方法),但我在几篇文章中读到了lubridate是确实是能够准确计算年龄的 R 软件包之一。所以现在我想知道我是否做错了什么。 (我认为不是,我基本上是按照示例进行的,这很简单,但只是仔细检查) -
不是解决方案,但
difftime(givendate, birthdate) / 365.25似乎比(givendate - birthdate) / 365.25)快 5% 左右。如果你最终得到算术,可能会很有用。 -
@Molx 好电话!
-.Date只是更稳健地调用difftime。(unclass(givendate) - unclass(birthdate)) / 365.25应该更快,因为这进一步跳过了difftime的开销。