基于data.table按组过滤不符合递减规则的多行数据
解决data.table中删除违反递减规则的父级所有行的问题
看起来你已经找对了方向,用shift来比较前后批次的总和,不过我们需要调整一下思路——不是标记单个异常行,而是判断整个父级是否存在违反规则的情况,然后一次性移除该父级的所有数据。下面是高效的解决方案:
步骤解析
- 先确保数据按
parent_id和batch_id排序,保证批次的顺序正确(避免批次乱序导致判断错误); - 对每个父级,检查其批次总和序列是否严格非递增(即后一批总和≤前一批);
- 仅保留没有违反规则的父级的所有行。
完整代码
library(data.table) # 你的示例数据 dt <- data.table( son_id = c("a1", "a2", "a3", "b4", "b5", "b6", "b7", "c8", "c9", "ck"), batch_id = c(1, 1, 2, 1, 2, 3, 4, 1, 2, 2), parent_id = c(rep("p1", 3), rep("p2", 4), rep("p3", 3)), amount = c(2e6, 1e6, 2e6, 3e6, 3e6, 3e6, 3e6, 3e6, 1e6, 3e6) ) dt[, sum_amount := sum(amount), by = c("parent_id", "batch_id")] # 核心处理逻辑:过滤掉存在异常的父级 result_dt <- dt[order(parent_id, batch_id)][ , if (!any(shift(sum_amount, type = "lag") < sum_amount, na.rm = TRUE)) .SD, by = parent_id ] # 查看结果 print(result_dt)
代码解释
order(parent_id, batch_id):强制每个父级下的批次按从小到大排序,确保我们是按正确的顺序比较前后批次总和;shift(sum_amount, type = "lag"):获取当前批次的前一个批次总和,和当前批次总和比较;如果当前总和 > 前一个总和,就违反了递减规则;any(..., na.rm = TRUE):检查该父级下是否存在任何一次违反规则的情况(na.rm = TRUE是忽略第一个批次的NA值,因为它没有前序批次);if (!is_bad) .SD:只保留没有违反规则的父级的所有行(.SD代表当前分组的全部数据)。
为什么你的原代码遇到困境?
你之前的代码dt[, .SD[, shift(sum_amount, type="lead") > sum_amount] , by = parent_id]返回的是每行对应的逻辑值(标记当前行的下一批总和是否大于当前),但我们需要的是判断整个父级是否存在异常,而不是单个行。上面的方案直接在分组逻辑中完成判断和过滤,更符合data.table的高效分组操作风格。
内容的提问来源于stack exchange,提问作者Will
相关产品推荐
相关产品推荐

