如何基于捕获日期计算并填充tibble中的动物年龄列
问题描述
我有一组动物活体捕获数据,释放前会提取一颗小臼齿用于年龄测定,但部分个体仅能在重捕时完成测龄。示例如下:
首次捕获时测龄
| ID | exit | ageindays | age |
|---|---|---|---|
| 3 | 67 | 341 | 0.934 |
| 3 | 79 | NA | NA |
| 3 | 86 | NA | NA |
| 3 | 101 | NA | NA |
| 3 | 119 | NA | NA |
重捕时测龄
| ID | exit | ageindays | age |
|---|---|---|---|
| 4 | 428 | NA | NA |
| 4 | 435 | NA | NA |
| 4 | 441 | 1145 | 3.14 |
| 4 | 462 | NA | NA |
| 4 | 470 | NA | NA |
| 4 | 471 | NA | NA |
我希望基于个体测龄日期前后的天数,填充每个个体的age列(年龄,单位:年)。尝试了以下代码:
df <- df %>% group_by(ID) %>% mutate(ageindays=case_when(!is.na(ageindays) ~ ageindays, is.na(ageindays) ~ first(ageindays) + exit)) %>% ungroup() %>% mutate(age=as.numeric(ageindays/365))
但first()的使用有误,计算似乎基于整个数据框的第一行,输出中前几行计算正确,但切换新ID后(如个体3)计算出错。
示例数据结构:
structure(list(ID = c("5", "5", "5", "6", "6", "6", "6", "6", "6", "6", "6" ), exit = c(408, 409, 437, 679, 682, 688, 694, 702, 716, 723, 729), AgeDay = c(408, 408, 408, 679, 679, 679, 679, 679, 679, 679, 679), ageindays = c(317, 317, 317, 588, 588, 588, 588, 588, 588, 588, 588)), row.names = c(NA, -11L), class = c("tbl_df", "tbl", "data.frame"))
解决方案
你的核心问题有两个:一是误用了first(),二是年龄计算的逻辑错误——正确的公式应该是测龄当天的ageindays + (当前exit日期 - 测龄当天的exit日期),而非直接将first(ageindays)与exit相加。
以下是修正后的代码:
library(dplyr) df <- df %>% group_by(ID) %>% # 提取每个个体的测龄日期(ageindays非NA对应的exit值)和基准年龄天数 mutate( age_exit = exit[!is.na(ageindays)][1], base_ageindays = ageindays[!is.na(ageindays)][1] ) %>% # 填充缺失的ageindays mutate( ageindays = case_when( !is.na(ageindays) ~ ageindays, TRUE ~ base_ageindays + (exit - age_exit) ) ) %>% # 转换为以年为单位的年龄 mutate(age = ageindays / 365) %>% # 清理临时变量 select(-age_exit, -base_ageindays) %>% ungroup()
关键逻辑说明
- 分组提取基准值:按ID分组后,
exit[!is.na(ageindays)][1]会获取当前个体第一次测龄的日期;base_ageindays则是该个体在测龄当天的年龄天数。如果一个个体有多次测龄记录,若要取最近一次的测龄数据,将[1]替换为last()即可。 - 计算缺失值:对于ageindays为空的行,用基准年龄天数加上当前捕获日期与测龄日期的天数差,就能得到当前捕获时的准确年龄天数。
- 单位转换:最后将ageindays除以365,转换为以年为单位的年龄。
示例验证
以个体4为例:
- 测龄日exit为441,对应的ageindays是1145
- 当exit=428时,ageindays = 1145 + (428-441) = 1132,age≈3.10
- 当exit=471时,ageindays=1145 + (471-441)=1175,age≈3.22,完全符合时间推移的逻辑
内容的提问来源于stack exchange,提问作者logical-curve7345
相关产品推荐
相关产品推荐

