在R语言中基于时间与第三列传值至下一行并创建smk_R列
解决R语言中跨Type行传递吸烟状态的问题
我来帮你搞定这个需求!要实现把Type 0行的吸烟状态传递到同ID下后续最近的Type 1行,并标记两次Type 1评估间是否吸烟,用tidyverse工具链(dplyr+tidyr)就能轻松实现,逻辑清晰还容易维护。
核心思路拆解
- 先确保数据顺序正确:每个ID内的行必须按时间从小到大排序,不然状态传递会完全混乱。
- 给Type 1锚点分组:把每一个Type 1行,加上它之后到下一个Type 1行之前的所有Type 0行,归为同一个分组。
- 组内判断吸烟状态:在每个分组里检查是否存在
smk=1的记录,只要有一次吸烟,就给该分组对应的Type 1行标记smk_R=1,否则为0。
代码实现(附示例数据)
首先我们先模拟一份贴合你需求的示例数据,方便你测试:
library(tidyverse) # 生成示例数据:包含ID、时间、类型、吸烟状态 set.seed(123) df <- tibble( ID = rep(1:3, each = 5), time = rep(1:5, 3), Type = case_when( ID == 1 & time %in% c(1,4) ~ 1, # ID1的锚点是时间1和4 ID == 2 & time %in% c(2,5) ~ 1, # ID2的锚点是时间2和5 ID == 3 & time %in% c(1,3,5) ~ 1,# ID3有三个锚点 TRUE ~ 0 ), smk = sample(c(0,1), 15, replace = TRUE) )
接下来是核心处理代码:
df_final <- df %>% # 按ID分组,强制按时间排序 group_by(ID) %>% arrange(time, .by_group = TRUE) %>% # 给每个Type1锚点创建分组:每遇到一个Type1,组号+1 mutate(type1_group = cumsum(Type == 1)) %>% # 按ID+分组号再次分组,判断组内是否有吸烟记录 group_by(ID, type1_group) %>% mutate( # 组内只要有smk=1,就标记为1;否则0 smk_R = ifelse(any(smk == 1), 1, 0), # 只在Type1行保留标记,Type0行设为NA(可根据需求调整) smk_R = ifelse(Type == 1, smk_R, NA) ) %>% ungroup() # 查看处理结果 print(df_final, n = 15)
关键代码解释
arrange(time, .by_group = TRUE):这一步是基础中的基础!必须保证每个ID内的行按时间顺序排列,否则分组和状态传递都会出错。如果你的时间是日期格式,这个排序逻辑同样适用。type1_group = cumsum(Type == 1):用累加和生成分组ID,比如第一个Type1行是组1,后面的Type0都属于组1,直到下一个Type1出现,组号自动变成2,完美把两次Type1之间的行归为一组。any(smk == 1):检查分组内所有行的吸烟状态,只要有一次吸烟记录,就给该分组对应的Type1行标记为1,完全符合你"判断两次Type1评估间是否吸烟"的需求。- 最后那个
ifelse:如果不需要保留Type0行的smk_R,就把它们设为NA;如果需要保留传递的状态,也可以改成保留对应的值,灵活调整即可。
特殊情况处理
- 如果两个Type1行紧挨着(中间没有Type0):此时该分组只有这个Type1行,
smk_R会直接取它自己的smk值,逻辑完全合理。 - 如果需要传递的是最后一次Type0的吸烟状态(而不是只要有吸烟就标记):可以把
any(smk ==1)改成last(smk[Type==0]),再处理NA的情况就行。
内容的提问来源于stack exchange,提问作者swhusky
相关产品推荐
相关产品推荐

