如何处理含日期的复杂变量:计算眼部影像随访年数
问题描述
我有一个tibble,每行代表一张眼部影像,包含变量:patientId、laterality(左/右)、date、imageId。需要将其处理为另一个tibble,展示每只眼睛(按patientId、laterality分组)的followUpYears,规则如下:
- 要满足某一年的随访要求,该年度内必须有两个不同的影像日期:第1年为基线日起0-365天,第2年为366-730天,以此类推,
followUpYears为整数。 - 同一日期仅计一次影像。
- 一旦某一年不满足“两个影像日期”的要求,随访即终止:若第1年仅1个影像日期,无论后续有多少影像,
followUpYears均为0。 - 眼睛拥有n年随访,不要求首次与末次影像日期间隔至少n年。
示例数据:
data <- tibble(patientId = c('A','A','A','A','A','A','B','B','B','B','B','B','B'), laterality = c('L','L','L','L','L','L','R','R','R','R','L','L','L'), date = as.Date(c('2000-05-05','2000-05-05','2001-05-06','2001-05-07','2002-05-06','2002-05-07','2000-09-08','2001-09-07','2001-09-09','2001-09-10','2000-09-08','2001-09-07','2001-09-10')), imageId = 1:13)
期望输出:
expected_output <- tibble(patientId = c('A','B','B'), laterality = c('L','R','L'), followUpYears = c(0, 2, 1))
患者A左眼followUpYears为0(符合规则2、3);患者B右眼为2(符合规则4);患者B左眼为1(第2年未满足要求)。熟悉基础dplyr动词,但不知如何构建该变量,患者可能单眼/双眼纳入,部分随访超10年,忽略闰年按每年365天计算,求解决方案。
解决方案
可以通过分组去重、计算随访年份区间、逐年份验证条件,最终确定连续满足要求的最大随访年份数。代码如下:
library(dplyr) library(tidyr) result <- data %>% # 按患者+眼别分组,去除同一日期的重复影像 group_by(patientId, laterality) %>% distinct(date, .keep_all = TRUE) %>% # 计算每个日期与基线(本组最早日期)的间隔天数 mutate(baseline = min(date), days_since_baseline = as.numeric(date - baseline)) %>% # 划分该日期所属的随访年份(第1年=0-365天,以此类推) mutate(year = floor(days_since_baseline / 365) + 1) %>% # 统计每个年份的不同日期数量 count(year) %>% # 补全连续的年份序列,避免遗漏中间未满足要求的年份 complete(year = seq(1, max(year, na.rm = TRUE)), fill = list(n = 0)) %>% # 标记年份是否满足要求,并找到第一个不满足的年份位置 mutate(meets_requirement = n >= 2, first_fail = which(!meets_requirement)[1] %||% (n() + 1)) %>% # 根据规则计算最终随访年份 summarise(followUpYears = ifelse(first_fail == 1, 0, first_fail - 1)) %>% ungroup() # 查看结果 result
代码说明
- 日期去重:使用
distinct(date, .keep_all = TRUE)去掉同一日期的重复影像,符合规则2。 - 基线与天数计算:以每组最早日期为基线,计算各日期到基线的间隔天数,为划分年份做准备。
- 随访年份划分:通过
floor(days_since_baseline / 365) + 1将日期映射到对应的随访年份。 - 年份日期数统计:用
count(year)统计每个年份的有效日期数,再通过complete补全连续年份,确保不会漏掉中间未满足要求的年份。 - 结果计算:标记每个年份是否满足“至少2个不同日期”的要求,找到第一个不满足的年份位置:
- 若第一个不满足的是第1年,
followUpYears为0; - 否则,
followUpYears为第一个不满足年份减1,即连续满足要求的最大年份数。
- 若第一个不满足的是第1年,
运行代码后结果与期望输出一致:
# A tibble: 3 × 3 patientId laterality followUpYears <chr> <chr> <dbl> 1 A L 0 2 B L 1 3 B R 2
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

