R语言按SID分组:为PHASE列填充经期前后标识
问题描述与解决方案
需求说明
对按SID分组的DataFrame执行以下操作:
- 将
PHASE列中所有Day_1行的**前7行(未被Day_*值占用的)**填充为Before - 将经期最后一天(任意
Day_*行,周期天数不固定)的**后7行(未被Day_*值占用的)**填充为After - 核心限制:不得覆盖
PHASE列已有的Day_1至Day_*值
此前尝试的问题
尝试的两种方法均存在缺陷:
- 全局索引赋值法:出现跨SID覆盖问题,未考虑分组边界
- 循环处理单个SID法:在边界场景失效,比如
Day_1出现在分组前6行、经期最后一天出现在分组末尾6行时,无法正确填充
用户尝试的代码如下:
# 方法1:全局索引赋值 datat$PHASE[pmin(nrow(data), unique(unlist(lapply(which(data$PHASE == "Day_1"), `-`, 1:7))))] <- "Before"
# 方法2:循环处理单个SID for (i in patients) { tryCatch({ data.filt <- data %>% filter(SID == i) data.filt$PHASE[pmin(nrow(data.filt), unique(unlist(lapply(which(data.filt$PHASE == "Day_1"), `-`, 1:7))))] <- "Before" assign(paste0(i), data.filt) }, error = function(e){cat(paste0(i, "_ERROR :", conditionMessage(e), "\n"))}) }
有效解决方案
使用dplyr分组操作,通过标记周期首尾位置、扩散行号范围,精准完成填充需求:
data <- data %>% group_by(SID) %>% # 标记每个经期周期的起始行(当前行是Day_*,前一行是NA) mutate(Start = if_else(is.na(lag(PHASE)) & grepl("Day_", PHASE), row_number(), NA), # 标记每个经期周期的结束行(当前行是Day_*,后一行是NA) End = if_else(is.na(lead(PHASE)) & grepl("Day_", PHASE), row_number(), NA)) %>% # 向上填充起始行号,让周期前的所有行都能获取对应起始位置 fill(Start, .direction = "up") %>% # 向下填充结束行号,让周期后的所有行都能获取对应结束位置 fill(End, .direction = "down") %>% # 给周期起始行前7行的NA填充为Before mutate(PHASE = if_else(is.na(PHASE) & (Start - row_number() <= 7), "Before", PHASE)) %>% # 给周期结束行后7行的NA填充为After mutate(PHASE = if_else(is.na(PHASE) & (row_number() - End <= 7), "After", PHASE))
方案核心逻辑
group_by(SID):确保所有操作在单个分组内执行,彻底避免跨SID干扰Start/End列:精准定位每个经期周期的首尾行,为后续范围判断提供依据fill():将首尾行号扩散到对应区域,让周期前后的行都能关联到所属周期的边界- 两次
mutate():仅对PHASE为NA且符合距离条件的行填充,严格遵守不覆盖已有Day_*值的要求
内容的提问来源于stack exchange,提问作者AWillz
相关产品推荐
相关产品推荐

