如何在不使用split()的情况下按id与连续category分组data.table
问题:无需拆分id的data.table连续批次标识生成方法
需求说明
现有data.table格式数据,id与category为分组变量,数据已按指定顺序排列。需要在每个id组内,为category的连续出现批次生成group标识:同一id下,同一category的非连续连续段需标记不同的group值,同一连续段内标记相同值。例如id=1中B100有两次连续出现(第1-2行、第5行),需分别标记为1和2。
当前实现使用两次data.table::rleid(),但第二步需要按id拆分数据,现寻求无需拆分id的实现方法。
原始数据示例
id category 1: 1 B100 2: 1 B100 3: 1 D300 4: 1 D300 5: 1 B100 6: 2 B100 7: 2 F500 8: 2 F500 9: 2 E600 10: 2 E600 11: 3 T400 12: 3 B100 13: 3 T400 14: 3 T400
期望输出
id category group 1: 1 B100 1 # 第1次连续出现B100(id=1),长度2 2: 1 B100 1 3: 1 D300 1 4: 1 D300 1 5: 1 B100 2 # 第2次连续出现B100(id=1),长度1 6: 2 B100 1 7: 2 F500 1 8: 2 F500 1 9: 2 E600 1 10: 2 E600 1 # id=2无重复连续category,故均为1 11: 3 T400 1 12: 3 B100 1 13: 3 T400 2 # 第2次连续出现T400(id=3),长度2 14: 3 T400 2
当前实现(需拆分id)
library(data.table) DT[, group := rleid(category), by = id] DT <- split(DT, by = "id") DT <- lapply(DT, \(x) x[, group := rleid(group), by = category]) DT <- rbindlist(DT)
无需拆分id的解决方案
直接利用data.table的分组嵌套操作,无需拆分数据,两步即可完成:
方法代码
library(data.table) setDT(DT) # 第一步:为每个id内的连续category段生成临时标识 DT[, temp_rleid := rleid(category), by = id] # 第二步:针对每个id+category组合,对临时标识生成批次序号 DT[, group := rleid(temp_rleid), by = .(id, category)] # 清理临时列(可选) DT[, temp_rleid := NULL]
简洁链式写法
也可以合并为一行链式操作,省去临时列:
setDT(DT)[, group := rleid(rleid(category)), by = .(id, category)]
原理说明
- 内层
rleid(category), by = id:为每个id内的连续category段生成唯一标识,比如id=1中两次B100连续段会得到不同的标识值。 - 外层
rleid(...) , by = .(id, category):针对每个id+category组合,对上述连续段标识再次生成rleid,即可得到该category在对应id内的连续批次序号。
可复制测试数据
DT <- data.frame(id = c(rep(1,5), rep(2,5), rep(3,4)), category = c("B100","B100","D300","D300","B100", "B100","F500","F500","E600","E600", "T400","B100","T400","T400")) # 期望结果对照 Output <- data.frame(id = c(rep(1,5), rep(2,5), rep(3,4)), category = c("B100","B100","D300","D300","B100", "B100","F500","F500","E600","E600", "T400","B100","T400","T400"), group = c(1,1,1,1,2,1,1,1,1,1,1,1,2,2))
内容的提问来源于stack exchange,提问作者diomedesdata
相关产品推荐
相关产品推荐

