如何用dplyr实现:累计值达阈值时保留行并重置累计
解决方案:用dplyr+purrr实现累计阈值触发的行保留逻辑
核心思路是通过状态跟踪式的累计计算,在每次累计值达到阈值时标记并保留当前行,同时重置累计计数;如果需要匹配你的预期输出,还可以强制保留最后一行(即使未达阈值)。
步骤1:加载依赖包
library(dplyr) library(purrr)
步骤2:定义累计逻辑函数
这个函数会跟踪两个状态:当前累计和、是否保留当前行。当累计和达到阈值时,标记保留并重置累计;否则继续累计。
threshold <- 1 # 设定你的阈值 accumulate_logic <- function(current_state, x_val) { new_total <- current_state$total + x_val if (new_total >= threshold) { # 达到阈值:保留该行,重置累计为0 list(total = 0, keep = TRUE) } else { # 未达阈值:不保留,继续累计 list(total = new_total, keep = FALSE) } }
步骤3:应用逻辑到数据集
全局累计(不按group分组)
如果需要按整个数据集的顺序累计:
result <- dat %>% # 生成累计标记 mutate( # 用accumulate逐行计算状态,提取keep标记 keep = accumulate( x, accumulate_logic, .init = list(total = 0, keep = FALSE) # 初始状态:累计0,不保留 ) %>% map_lgl("keep") %>% tail(-1) # 去掉初始状态对应的第一个空值 ) %>% # 强制保留最后一行(匹配你的预期输出,不需要可删除此步) mutate(keep = ifelse(row_number() == n(), TRUE, keep)) %>% # 过滤出保留的行 filter(keep) %>% # 移除辅助列 select(-keep) print(result)
运行结果会和你给出的预期输出一致:
id group age type x 2 2 B 21 type 2 0.294720447 4 4 B 18 type 4 2.404653389 6 6 B 30 type 6 0.799009249 7 7 A 24 type 7 1.147657009 10 10 B 28 type 10 0.411510833
按group分组累计
如果需要在每个group内独立累计(比如A组和B组各自计算累计),只需添加group_by(group):
result_grouped <- dat %>% group_by(group) %>% mutate( keep = accumulate( x, accumulate_logic, .init = list(total = 0, keep = FALSE) ) %>% map_lgl("keep") %>% tail(-1) ) %>% mutate(keep = ifelse(row_number() == n(), TRUE, keep)) %>% filter(keep) %>% select(-keep) %>% ungroup() print(result_grouped)
关键说明
accumulate函数是核心:它能逐行传递状态(累计和+保留标记),实现"累计-触发-重置"的循环逻辑,这是普通的cumsum做不到的。- 强制保留最后一行的逻辑是为了匹配你的预期输出,若业务不需要,可直接删除对应代码行。
内容的提问来源于stack exchange,提问作者reinoud
相关产品推荐
相关产品推荐

