You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在data.table中按条件保留/删除分组?有无更优方法?

在data.table中按条件筛选分组的高效方法

当然有更高效的实现方式!不用新增临时列再删除,直接利用data.table的分组特性就能搞定,下面给你两种常用的高效写法,再附上性能对比结果。

示例数据

先看我们的测试数据集:

set.seed(0)
dt <- data.table(a = rep(1:3, rep(3, 3)), b = sample(1:5, 9, T))
dt
#    a b
# 1: 1 4
# 2: 1 1
# 3: 1 2
# 4: 2 1
# 5: 2 4
# 6: 2 2
# 7: 3 4
# 8: 3 3
# 9: 3 4

我们的需求是保留分组a中包含值2的所有行(也就是删除分组a=3的全部数据)。

方法1:分组内用if(条件) .SD直接返回

这是最简洁的写法之一,在分组操作中判断条件,符合要求就返回整个组的.SD(Subset of Data,即当前分组的所有行):

dt[, if(2 %in% b) .SD, by = a]
#    a b
# 1: 1 4
# 2: 1 1
# 3: 1 2
# 4: 2 1
# 5: 2 4
# 6: 2 2

方法2:用.I获取行索引后子集

另一种方式是先分组获取符合条件的行的索引,再用索引直接筛选原表:

dt[dt[, .I[2 %in% b], by = a]$V1]
#    a b
# 1: 1 4
# 2: 1 1
# 3: 1 2
# 4: 2 1
# 5: 2 4
# 6: 2 2

性能对比(基准测试)

我在2015款Macbook Pro上做了不同数据量级的基准测试,对比上面两种方法的性能:

测试代码:

library(microbenchmark)
library(purrr)

bench <- map(10^(4:7) , ~ {
 dt <- data.table(a = rep(1:.x, rep(10, .x)), b = sample(1:10, 10*.x, T))
 microbenchmark(
   "if+SD" = dt[, if(2 %in% b) .SD, a],
   ".I索引" = dt[dt[, .I[2 %in% b], a]$V1],
   times = 100
 )
})

测试结果

  • 数据量104个分组(共105行)
Unit: milliseconds
    expr      min       lq     mean   median       uq      max neval
   if+SD 13.04827 17.36046 21.15155 19.19119 22.94641  43.0452   100
 .I索引 17.32547 22.92023 27.09775 24.87586 28.39789 108.4760   100
  • 数据量105个分组(共106行)
Unit: milliseconds
    expr      min       lq     mean   median       uq      max neval
   if+SD 123.9118 143.7802 162.6719 154.4713 173.2986 428.4141   100
 .I索引 158.2975 177.3303 206.3611 193.4460 224.5091 435.3982   100
  • 数据量106个分组(共107行)
Unit: seconds
    expr      min       lq     mean   median       uq      max neval
   if+SD 1.233100 1.351067 1.448680 1.402827 1.517017 1.852797   100
 .I索引 1.587020 1.704344 1.826468 1.778590 1.947943 2.243176   100
  • 数据量107个分组(共108行)
Unit: seconds
    expr      min       lq     mean   median       uq      max neval
   if+SD 14.44317 14.65889 14.79806 14.78217 14.91571 15.29134   100
 .I索引 18.04774 18.36764 18.48804 18.45732 18.53073 20.73805   100

结论

从测试结果可以看到,if(条件) .SD的写法在所有数据量级下都比.I索引的方法更快,而且代码更简洁,完全不需要临时列,是这类分组筛选需求的最优解之一。对比你原来的“新增列→过滤→删列”的方法,这两种方式不仅代码更清爽,性能优势也会随着数据量增大而愈发明显。

内容的提问来源于stack exchange,提问作者IceCreamToucan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:45:01