You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于多列条件创建同前缀随访年份列(R语言求助)

问题描述

我有如下包含患者出生日期(DOB)、出生年份(Birth_Year)、死亡日期(DOD)、死亡年份(Death_Year)的数据集:

ID      DOB       Birth_Year    DOD      Death_Year
1    2016-10-01     2016     2019-10-15     2019
2    2017-07-01     2019     2022-01-10     2022
3    2017-04-15     2017     2020-08-15     2020

需要为2015至2020年的每一年创建随访时间列(Year_2015至Year_2020),规则如下:

  • 若为出生年份,随访时间为DOB到当年年末的时长(按年计算,保留小数)
  • 若为死亡年份,随访时间为当年年初到DOD的时长(按年计算,保留小数)
  • 年份不在出生年份至死亡年份区间内,随访时间为0
  • 年份在区间内且非首尾年份,随访时间为1年

预期输出数据集如下:

ID      DOB       Birth_Year    DOD      Death_Year Year_2015 Year_2016 Year_2017 Year_2018 Year_2019 Year_2020
1    2016-10-01     2016     2019-06-30     2019        0        0.25       1         1        0.5        0
2    2017-07-01     2019     2022-01-10     2022        0         0        0.5        1         1         1
3    2017-04-15     2017     2020-08-15     2020        0         0        0.3        1         1        0.8

我尝试用dplyr的case_when函数或带if-else的循环创建这些列,但在循环中处理动态列名和多条件遍历时遇到问题,也考虑过用apply函数,求解决方法。以下是我尝试的循环代码:

for (i in 2015:2020) {
  FoUp_Year <- paste0('Year_', i) 
  df[, Birth_Year_end] <- make_date(year = Birth_Year, month = 12, day = 31)
  df[, Death_Year_start] <- make_date(year = Death_Year, month = 1, day = 1)
  if (i<df[, Birth_Year] | i>df[, Death_Year]) { 
    df[,FoUp_Year] <- 0
  }
  else if(i==df[, Birth_Year] && i<df[, Death_Year]) {
    df[,FoUp_Year] <- df[, Birth_Year_end]-df[,DOB]
  }
  else if(i==df[, Death_Year] && i>df[, Birth_Year]) {
    df[,FoUp_Year] <- df[, DOD]-df[,Death_Year_start]
  }
  else if(i==df[, Birth_Year] && i==df[, Death_Year]) {
    df[,FoUp_Year] <- df[, DOD]-df[,DOB]
  }
  else if(i>df[, Birth_Year] && i<df[, Death_Year]) {
    df[,FoUp_Year] <- 1
  }
}
解决方法

方法一:用dplyr+tidyr的向量化操作(推荐)

这种方法避免循环,利用tidyverse的函数实现简洁高效的向量化运算,步骤如下:

  1. 先将日期列转为Date类型,处理无效日期
  2. 用crossing展开每行数据与目标年份的组合
  3. 按规则计算随访时间,最后转回宽格式

代码示例:

library(dplyr)
library(tidyr)
library(lubridate)

# 读取并预处理数据
df <- tibble(
  ID = 1:3,
  DOB = ymd(c("2016-10-01", "2017-07-01", "2017-04-15")),
  Birth_Year = c(2016, 2019, 2017),
  DOD = ymd(c("2019-06-30", "2022-01-10", "2020-08-15")),
  Death_Year = c(2019, 2022, 2020)
)

# 生成宽格式随访列
result <- df %>%
  # 展开每行数据与2015-2020年的组合
  crossing(Year = 2015:2020) %>%
  # 计算随访时间
  mutate(
    Follow_Time = case_when(
      Year < Birth_Year | Year > Death_Year ~ 0,
      Year == Birth_Year & Year == Death_Year ~ (DOD - DOB) / dyears(1),
      Year == Birth_Year ~ (ymd(paste0(Year, "-12-31")) - DOB) / dyears(1),
      Year == Death_Year ~ (DOD - ymd(paste0(Year, "-01-01"))) / dyears(1),
      TRUE ~ 1
    )
  ) %>%
  # 转回宽格式
  pivot_wider(names_from = Year, values_from = Follow_Time, names_prefix = "Year_") %>%
  # 保持原始列顺序
  select(ID, DOB, Birth_Year, DOD, Death_Year, starts_with("Year_"))

# 查看结果
print(result, digits = 2)

方法二:修正后的循环方法

你的原始循环存在两个核心问题:一是用标量if处理向量数据(R中if不支持向量运算);二是动态列名赋值方式不规范。以下是修正后的代码:

library(lubridate)

# 预处理日期列
df$DOB <- ymd(df$DOB)
df$DOD <- ymd(df$DOD)

# 预计算年末、年初日期(避免循环内重复计算)
df$Birth_Year_end <- ymd(paste0(df$Birth_Year, "-12-31"))
df$Death_Year_start <- ymd(paste0(df$Death_Year, "-01-01"))

# 循环生成随访列
for (i in 2015:2020) {
  col_name <- paste0("Year_", i)
  df[[col_name]] <- case_when(
    i < df$Birth_Year | i > df$Death_Year ~ 0,
    i == df$Birth_Year & i == df$Death_Year ~ (df$DOD - df$DOB) / dyears(1),
    i == df$Birth_Year ~ (df$Birth_Year_end - df$DOB) / dyears(1),
    i == df$Death_Year ~ (df$DOD - df$Death_Year_start) / dyears(1),
    TRUE ~ 1
  )
}

# 可选:删除临时辅助列
df <- df %>% select(-Birth_Year_end, -Death_Year_start)

# 查看结果
print(df, digits = 2)

关键修正点:

  • 用case_when替代嵌套if-else,支持向量运算
  • 用df[[col_name]]赋值动态列名,比df[,col_name]更稳定
  • 将日期预处理放在循环外,提升效率
  • 用dyears(1)计算年数比例,确保结果为按年的小数(如93天≈0.25年)

内容的提问来源于stack exchange,提问作者Yanting Luo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:12:04