R Data.Table实现保留每个组首次连续出现的全部行
问题
我有一个包含group(分组)和value(数值)的data.table,希望保留每个分组从表格顶部开始首次出现的所有条目(即后续再次出现的该分组行需删除)。
示例代码
set.seed(666) group = c(1,1,1,2,2,3,3,3,1,1,4,4,4,1,1,2) value = runif(16) DT = data.table(group,value)
原DT输出
group value 1: 1 0.77436849 2: 1 0.19722419 3: 1 0.97801384 4: 2 0.20132735 5: 2 0.36124443 6: 3 0.74261194 7: 3 0.97872844 8: 3 0.49811371 9: 1 0.01331584 10: 1 0.25994613 11: 4 0.77589308 12: 4 0.01637905 13: 4 0.09574478 14: 1 0.14216354 15: 1 0.21112624 16: 2 0.81125644
期望结果
删除行9、10、14、15、16(因group1和group2已出现过),得到:
group value 1: 1 0.77436849 2: 1 0.19722419 3: 1 0.97801384 4: 2 0.20132735 5: 2 0.36124443 6: 3 0.74261194 7: 3 0.97872844 8: 3 0.49811371 11: 4 0.77589308 12: 4 0.01637905 13: 4 0.09574478
尝试过的方法
DT[,.SD[1], by = "group", .SDcols = "value"]:只返回每个组的首行,不符合需求;DT[,.I, by = group]:获取了行索引,但不知道如何优雅识别分组的"断点";- 补充:用
maxRow = setnames(DT[, na.omit(which(diff(.I) > 1)[1]), by = "group"], "V1", "maxRow")获取了每个组的最大行号,但不知后续如何操作。
解决方案
方法1:用rleid标记连续分组块(最简洁高效)
rleid()是data.table内置函数,能给连续相同的group生成唯一的块编号。我们只保留每个group第一次出现的块:
# 给连续相同的group分配块编号 DT[, block := rleid(group)] # 找出每个group首次出现的块编号 first_blocks = DT[, .(first_block = first(block)), by = group] # 筛选出属于首次块的所有行 result = DT[block %in% first_blocks$first_block]
这个方法逻辑清晰,计算效率高,适合大数据量场景。
方法2:逐行跟踪已出现的分组
通过维护一个已出现分组的集合,逐行判断是否保留当前行:
DT[, keep := { seen = c() sapply(group, function(g) { if (!g %in% seen) { seen <<- c(seen, g) TRUE } else { FALSE } }) }] result = DT[keep]
这个方法直观,但逐行处理在数据量很大时效率不如方法1。
方法3:基于行索引的范围筛选
先定位每个group首次出现的连续行范围,再提取这些行:
# 计算每个group首次出现的连续行的最大索引 range_dt = DT[, .( start = min(.I), end = .I[which(c(diff(group) != 0, TRUE))[1]] ), by = group] # 提取所有在范围内的行 result = DT[unlist(Map(`:`, range_dt$start, range_dt$end))]
这个方法适合需要明确行索引范围的场景。
内容的提问来源于stack exchange,提问作者mri
相关产品推荐
相关产品推荐

