【问题标题】:Calculate ages in R计算 R 中的年龄
【发布时间】:2010-08-31 16:55:52
【问题描述】:

我在 R 中有两个数据框。一个框有一个人的出生年份:

YEAR
/1931
/1924

然后另一列显示更近的时间。

RECENT
09/08/2005
11/08/2005

我想要做的是减去年份,以便我可以计算他们的年龄,但是我不知道如何处理这个问题。有什么帮助吗?

【问题讨论】:

标签: r


【解决方案1】:

以下函数采用 Date 对象的向量并计算年龄,正确考虑闰年。似乎比其他任何答案都更简单。

age = function(from, to) {
  from_lt = as.POSIXlt(from)
  to_lt = as.POSIXlt(to)

  age = to_lt$year - from_lt$year

  ifelse(to_lt$mon < from_lt$mon |
         (to_lt$mon == from_lt$mon & to_lt$mday < from_lt$mday),
         age - 1, age)
}

【讨论】:

  • 清晰、快速且仅使用基本函数。还可以正确处理闰年。应该是票数最高的答案。
  • avoid ifelseout &lt;- integer(length(year)); out[idx &lt;- to_lt$mon &lt; from_lt$mon] &lt;- age - 1; out[!idx] &lt;- age]
  • @MichaelChirico 请在提交前检查您的语法。有一个] 太多,即使那样,它也不能正常工作。想象一下,您出生于 1980 年 12 月,现在是 2018 年 12 月……您自己算算。
【解决方案2】:

您可以使用 lubridate 软件包解决此问题。

> library(lubridate)

我不认为 /1931 是一个常见的日期类。所以我假设所有的条目都是字符串。

> RECENT <- data.frame(recent = c("09/08/2005", "11/08/2005"))
> YEAR <- data.frame(year = c("/1931", "/1924"))

首先,让我们通知 R 最近的日期是日期。我假设日期是月/日/年的顺序,所以我使用 mdy()。如果它们按日/月/年的顺序排列,只需使用 dmy()。

> RECENT$recent <- mdy(RECENT$recent)
      recent
1 2005-09-08
2 2005-11-08

现在,让我们把年份变成数字,这样我们就可以用它们做一些数学运算了。

> YEAR$year <- as.numeric(substr(YEAR$year, 2, 5))

现在算一下。 year() 提取最近日期的年份值。

> year(RECENT$recent) - YEAR
  year
1   74
2   81

附言如果您的年份条目实际上是完整日期,则可以使用

获得年份差异
> YEAR1 <- data.frame(year = mdy("01/08/1931","01/08/1924"))
> as.period(RECENT$recent - YEAR1$year, units = "year")
[1] 74 years and 8 months   81 years and 10 months

【讨论】:

  • 按照你的例子我得到days而不是years
  • 文档说是as.period(..., unit = "years"),而不是units = "year"
【解决方案3】:

我使用了一个自定义函数,见下面的代码,在 mutate 中使用很方便并且非常灵活(你需要 lubridate 包)。

示例

get_age("2000-01-01")
# [1] 17
get_age(lubridate::as_date("2000-01-01"))
# [1] 17
get_age("2000-01-01","2015-06-15")
# [1] 15
get_age("2000-01-01",dec = TRUE)
# [1] 17.92175
get_age(c("2000-01-01","2003-04-12"))
# [1] 17 14
get_age(c("2000-01-01","2003-04-12"),dec = TRUE)
# [1] 17.92176 14.64231

功能

#' Get age
#' 
#' Returns age, decimal or not, from single value or vector of strings
#' or dates, compared to a reference date defaulting to now. Note that
#' default is NOT the rounded value of decimal age.
#' @param from_date vector or single value of dates or characters
#' @param to_date date when age is to be computed
#' @param dec return decimal age or not
#' @examples
#' get_age("2000-01-01")
#' get_age(lubridate::as_date("2000-01-01"))
#' get_age("2000-01-01","2015-06-15")
#' get_age("2000-01-01",dec = TRUE)
#' get_age(c("2000-01-01","2003-04-12"))
#' get_age(c("2000-01-01","2003-04-12"),dec = TRUE)
get_age <- function(from_date,to_date = lubridate::now(),dec = FALSE){
  if(is.character(from_date)) from_date <- lubridate::as_date(from_date)
  if(is.character(to_date))   to_date   <- lubridate::as_date(to_date)
  if (dec) { age <- lubridate::interval(start = from_date, end = to_date)/(lubridate::days(365)+lubridate::hours(6))
  } else   { age <- lubridate::year(lubridate::as.period(lubridate::interval(start = from_date, end = to_date)))}
  age
}

【讨论】:

  • 这是一个不错的功能。但是,您为什么决定将/dyears(1) 用于if(dec)?年龄不是更像一个时期,需要/years(1) 吗?
  • 这是一个错误,但你的建议也不起作用,我把它改成了(lubridate::days(365)+lubridate::hours(6))
  • days()hours() 结合起来很有趣。请问您为什么选择hours(6)?啊,哎呀,我想我明白了。把它变成小数。不过,这会考虑闰年吗?
  • 这个想法是十进制年龄是“真实年龄”,它考虑了一年的恒定长度。所以要考虑闰年,一年应该是 365.25 天,0.25 天是 6 小时 :)。我对dyears 在做什么有错误的理解。试试:lubridate::interval(start = "2000-01-01", end = "2001-01-01")/(lubridate::days(365)+lubridate::hours(6)) 然后lubridate::interval(start = "2000-03-01", end = "2001-03-01")/(lubridate::days(365)+lubridate::hours(6))
【解决方案4】:

你可以做一些格式化:

as.numeric(format(as.Date("01/01/2010", format="%m/%d/%Y"), format="%Y")) - 1930

使用您的数据:

> yr <- c(1931, 1924)
> recent <- c("09/08/2005", "11/08/2005")
> as.numeric(format(as.Date(recent, format="%m/%d/%Y"), format="%Y")) - yr
[1] 74 81

由于您的数据在 data.frame 中(我假设它被称为 df),它会更像这样:

as.numeric(format(as.Date(df$recent, format="%m/%d/%Y"), format="%Y")) - df$year

【讨论】:

  • 适用于我在此处发布的数据,但我的数据集实际上有更多行。有没有办法通过调用数据框本身来实现这一点?
  • 以示例方式。只需将最近和年替换为您的 df 列。
【解决方案5】:

鉴于您示例中的数据:

> m <- data.frame(YEAR=c("/1931", "/1924"),RECENT=c("09/08/2005","11/08/2005"))
> m
   YEAR     RECENT
1 /1931 09/08/2005
2 /1924 11/08/2005

使用strptime 函数提取年份:

> strptime(m[,2], format = "%m/%d/%Y")$year - strptime(m[,1], format = "/%Y")$year
[1] 74 81

【讨论】:

  • 为什么?面向对象编程的美妙之处在于拥有识别日期对象的方法,因此您不必这样做。
【解决方案6】:

根据上一个答案,将列转换为日期对象并减去。字符和数字之间的一些类型转换是必要的:

> foo=data.frame(RECENT=c("09/08/2005","11/08/2005"),YEAR=c("/1931","/1924"))
> foo
      RECENT  YEAR
1 09/08/2005 /1931
2 11/08/2005 /1924
> foo$RECENTd = as.Date(foo$RECENT, format="%m/%d/%Y")
> foo$YEARn = as.numeric(substr(foo$YEAR,2,999))
> foo$AGE = as.numeric(format(foo$RECENTd,"%Y")) - foo$YEARn
> foo
      RECENT  YEAR    RECENTd YEARn AGE
1 09/08/2005 /1931 2005-09-08  1931  74
2 11/08/2005 /1924 2005-11-08  1924  81

请注意,我假设您的年份列中有斜线。

此外,在询问有关日期的问题时,提示要包括 12 日之后的一天,以便我们知道您是月/日/年的人还是日/月/年的人。

【讨论】:

    【解决方案7】:

    我认为这可能更直观一点,不需要格式化或剥离:

    as.numeric(as.Date("2002-02-02") - as.Date("1924-08-03")) / 365
    

    给出输出:

    77.55342
    

    然后你可以使用 floor()、round() 或 ceiling() 来四舍五入到整数。

    【讨论】:

    • 这不考虑闰年。
    • 你可以做 365.25,应该足够接近。如果您要查找年龄,实际(天数)年龄不是比日历年龄更重要吗?
    • 有时,实际的生存天数非常好(也许更好),但在其他情况下,您确实需要已过去的日历年数。尽管两个 65 岁的人(根据通用定义)可能活了不同的天数,但我们通常不想做出这种区分。例如,如果您要计算某人是否有资格退休,几乎每个人都会使用整年而不是几天来进行计算。
    【解决方案8】:

    使用lubridate 包也支持向量的非常可靠的方法:

    age <- function(date.birth, date.ref = Sys.Date()) {
      if (length(date.birth) > 1 & length(date.ref) == 1) {
        date.ref <- rep(date.ref, length(date.birth))
      }
    
      date.birth.monthdays <- paste0(month(date.birth), day(date.birth)) %>% as.integer()
      date.ref.monthdays <- paste0(month(date.ref), day(date.ref)) %>% as.integer()
    
      age.calc <- 0
    
      for (i in 1:length(date.birth)) {
        if (date.birth.monthdays[i] <= date.ref.monthdays[i]) {
          # didn't had birthday
          age.calc[i] <- year(date.ref[i]) - year(date.birth[i])
        } else {
          age.calc[i] <- year(date.ref[i]) - year(date.birth[i]) - 1
        }
      }
      age.calc
    }
    

    这也考虑了闰年。我只是检查是否有人已经过生日。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-26
      • 1970-01-01
      • 1970-01-01
      • 2020-12-28
      • 2016-03-20
      相关资源
      最近更新 更多