如何在data.table中结合聚合、过滤与分组操作?批量处理多列的高效实现方案
嘿,你说得没错,data.table里确实有更优雅高效的实现方式,完全不用像这样逐列重复写代码、再手动合并结果~给你分享两种简洁的解法,都能达到和你现有代码一样的效果,但代码量少得多,效率也更高:
解法1:直接用lapply + .SDcols
这种方法直接在分组操作里一次性处理所有x列,一步到位得到最终的宽表:
set.seed(123) foo <- data.table(id = rep(LETTERS[1:10], each = 10), code = sample(c(111, 222, 333, 444), 100, replace = TRUE), x1 = sample(0:1, 100, replace = TRUE), x2 = sample(0:1, 100, replace = TRUE), x3 = sample(0:1, 100, replace = TRUE), x4 = sample(0:1, 100, replace = TRUE), x5 = sample(0:1, 100, replace = TRUE), x6 = sample(0:1, 100, replace = TRUE)) # 核心代码一行搞定 out <- foo[, lapply(.SD, function(col) uniqueN(code[col == 1])), by = id, .SDcols = paste0("x", 1:6)]
代码解释:
.SDcols = paste0("x", 1:6):指定我们要处理的列是x1到x6;lapply(.SD, function(col) ...):对每个x列执行自定义函数,函数里只保留当前列值为1的行对应的code,然后用uniqueN计算唯一值的数量;by = id:按id分组聚合,最终直接生成和你预期一致的宽表,完全不需要后续的setkey和合并操作。
解法2:用melt转长表 + dcast转回宽表
如果觉得长表处理更直观,可以用这种“转长-处理-转宽”的思路,代码同样简洁:
# 先把宽表转成包含变量名的长表 foo_melted <- melt(foo, id.vars = c("id", "code"), measure.vars = paste0("x", 1:6)) # 过滤值为1的行,再按id和变量名聚合计算唯一code数,转回宽表 out <- dcast(foo_melted[value == 1], id ~ variable, fun.aggregate = uniqueN, value.var = "code")
代码解释:
melt:把x1-x6这些列转成两列——variable(存储x1/x2等列名)和value(存储对应列的0/1值);foo_melted[value == 1]:只保留需要计算的行(值为1的记录);dcast:把长表转回宽表,按id分组,对每个variable(x1-x6)用uniqueN聚合code列,得到最终结果。
验证结果
你可以用all.equal(out, f1[f2,][f3,])来验证,两种解法得到的结果和你原来的代码完全一致,而且不管你的x列有多少个(比如x1到x100),这两种方法都只需要修改paste0("x", 1:6)里的范围即可,扩展性极强。
内容的提问来源于stack exchange,提问作者Discus23
相关产品推荐
相关产品推荐

