如何用dplyr按RunID分组并将组计算结果传递至下一组
我来给你一个更优雅的tidyverse风格解决方案,不需要写显式循环就能实现组间值传递的需求,完美匹配你的预期输出:
完整代码实现
library(dplyr) library(data.table) # 你的示例数据 condition <- c(0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0) col_a <- c(0,0,0,2,3,4,0,0,0,2,4,5,6,0,0,0,0,0,0,0,0,1,2,0) col_b <- c(0,0,0,10,131,14,0,0,0,22,64,75,96,0,0,0,0,0,0,0,0,41,52,0) df <- data.frame(condition,col_a,col_b) # 核心处理逻辑 output <- df %>% # 生成连续分组ID,标记每组的condition类型 mutate(RunID = rleid(condition), group_type = condition) %>% # 按分组计算每个condition=1组的最终值,标记组内首行 group_by(RunID) %>% mutate( group_calc = ifelse(group_type == 1 & row_number() == n(), first(col_a)*last(col_b), NA), is_first_in_group = row_number() == 1 ) %>% ungroup() %>% # 提取前一个condition=1组的计算值,映射到当前组 mutate(prev_group_calc = lag(na.omit(group_calc))[match(RunID, na.omit(RunID[group_type == 1]))]) %>% # 更新condition=1组的首行col_a值 mutate(col_a = ifelse(group_type == 1 & is_first_in_group & !is.na(prev_group_calc), prev_group_calc, col_a)) %>% # 重新计算最终的calculation列 group_by(RunID) %>% mutate(calculation = ifelse(group_type == 0, 0, ifelse(row_number() == n(), first(col_a)*last(col_b), 0))) %>% ungroup() %>% # 清理辅助列 select(-RunID, -group_type, -group_calc, -is_first_in_group, -prev_group_calc) # 查看前15行结果 head(output,15)
关键步骤解释
- 分组标记:用
data.table::rleid生成连续的分组IDRunID,把相同连续condition值的行归为一组; - 组内预计算:按
RunID分组后,先算出每个condition=1组的最终计算值group_calc,同时标记组内的第一行; - 组间值传递:用
lag(na.omit(group_calc))获取前一个condition=1组的计算结果,再通过match把这个值映射到当前condition=1组的首行; - 更新列与最终计算:替换
condition=1组首行的col_a值后,重新计算每组的calculation列,确保用了更新后的col_a; - 清理辅助列:移除过程中用到的临时列,得到干净的输出。
运行结果
condition col_a col_b calculation 1 0 0 0 0 2 0 0 0 0 3 0 0 0 0 4 1 2 10 0 5 1 3 131 0 6 1 4 14 28 7 0 0 0 0 8 0 0 0 0 9 0 0 0 0 10 1 28 22 0 11 1 4 64 0 12 1 5 75 0 13 1 6 96 2688 14 0 0 0 0 15 0 0 0 0
这个方案全程用管道操作,没有显式循环,代码更简洁易读,而且能轻松扩展到更多连续的condition=1组的场景。
内容的提问来源于stack exchange,提问作者Andrew Bannerman
相关产品推荐
相关产品推荐

