如何实现基于累计和阈值的动态窗口滚动求和分组?
高效实现累计和阈值分组(data.table版)
针对大型数据集的amount列,需要按累计和达到阈值时分组的需求,以下是两种远快于逐行循环的实现方案:
示例数据
library(data.table) dt <- data.table(amount = c(0.009288, 0.189386, 0.071265, 0.137387, 0.032742, 0.000003, 0.071265, 0.122247, 0.124920, 0.032743)) threshold <- 0.2
方案一:基于区间查找的矢量化方法
核心思路是先确定所有分组的阈值断点,再用findInterval一次性完成分组,避免逐行操作:
# 计算累计和 dt[, cum := cumsum(amount)] # 生成分组断点 breaks <- numeric(0) current_threshold <- threshold max_cum <- dt[, max(cum)] while (current_threshold <= max_cum) { # 找到当前阈值对应的第一个累计和位置 current_break <- dt[cum >= current_threshold, cum][1] breaks <- c(breaks, current_break) # 更新下一组的阈值 current_threshold <- current_break + threshold } # 补全断点首尾,用于findInterval breaks <- c(0, breaks, Inf) # 分配分组标识 dt[, indicator := findInterval(cum, breaks)]
方案二:基于组定位的快速循环
循环次数等于分组数(远小于数据行数),通过批量更新分组标识提升效率:
# 计算累计和 dt[, cum := cumsum(amount)] dt[, indicator := 0] current_group <- 1 current_threshold <- threshold max_cum <- dt[, max(cum)] while (current_threshold <= max_cum) { # 找到当前组的结束位置 end_pos <- dt[cum >= current_threshold, which = TRUE][1] if (current_group == 1) { # 第一组从第1行开始 dt[1:end_pos, indicator := current_group] } else { # 后续组从上一组结束位置的下一行开始 last_end <- dt[indicator == current_group - 1, max(.I)] dt[(last_end + 1):end_pos, indicator := current_group] } # 更新阈值和组号 current_threshold <- dt[end_pos, cum] + threshold current_group <- current_group + 1 } # 处理最后一组未达到阈值的剩余行 dt[indicator == 0, indicator := current_group]
效果验证
两种方案最终生成的indicator列均为:1,1,1,2,2,2,2,3,3,4,符合需求。相比原逐行循环方案,大数据集下效率可提升数十至数百倍。
内容的提问来源于stack exchange,提问作者lewkiz
相关产品推荐
相关产品推荐

