【问题标题】:data.table subset on two columns where either key matches任一键匹配的两列上的 data.table 子集
【发布时间】:2017-12-10 19:07:05
【问题描述】:

我是data.table 的新手,似乎遗漏了一些明显的东西。我有一张桌子:

DT = data.table(A = c("x","y","y","z"), B = c("y","x","x","z"), value = 1:4)
setkey(DT, A, B)

现在我想找到AB"y" 的所有行(使用二进制搜索,我的实际表更大,操作必须执行数百万次)。我无法在一个语句中弄清楚如何做到这一点,因为,

DT[.("y", "y"), nomatch=0]

只给我(A & B) == "y" 的行(但我想要(A | B) == "y")。我现在做的是:

uA <- unique(DT[, A])
rbind(DT[.(uA, "y"), nomatch=0], DT[.("y"), nomatch=0])

但我觉得一定有更直观的方法。

感谢您的帮助!


基准测试

包括改编自@Frank's comment on Binary search DT with key on two columns using alternative (OR) instead of a conjunction的代码

n = 1e6
DT = data.table(A = sample(letters, n, replace = TRUE), 
                B = sample(letters, n, replace = TRUE), value = 1:n)
setkey(DT, A, B)
uA <- unique(DT[, A])

library(microbenchmark)
Union = function(){
   mya = DT[A=="y", which=TRUE]
   myb = DT[B=="y", which=TRUE]
   DT[union(mya,myb)] 
} 
microbenchmark(
    "reduce" = DT[DT[, Reduce('|', lapply(.SD, '==', 'y')), .SDcols = A:B]],
    "rbind" = rbind(DT[.(uA, "y"), nomatch=0], DT[.("y"), nomatch=0]),
    "union" = Union()
)

Unit: milliseconds
   expr      min        lq      mean    median        uq       max neval
 reduce 9.922728 10.116613 11.422823 10.226871 11.803204 25.453557   100
  rbind 2.596139  2.734751  2.916620  2.850199  3.113995  3.453326   100
  union 5.393815  5.725917  6.221544  5.906222  6.758622 14.019206   100

【问题讨论】:

  • 您可以使用DT[DT[, Reduce('|', lapply(.SD, '==', 'y')), .SDcols = A:B]]
  • 非常感谢,这行得通! (而且它的速度是我的解决方案的两倍)。虽然没有更直观的方法可以做到这一点,但我感到很惊讶。
  • 感谢@Henrik,在我的研究中错过了那个
  • @Henrik 您可以发布该代码作为答案吗?我无法让该解决方案产生等效的输出。
  • @Hendrik 查看我的编辑。我让它工作了,但它似乎也慢了。

标签: r data.table


【解决方案1】:

我们可以使用Reduce| 来获得一个逻辑vector,它检查.SDcols 中提到的任一列是否具有值'y',并将其用于对行进行子集化

DT[DT[, Reduce('|', lapply(.SD, '==', 'y')), .SDcols = A:B]]

基准测试

set.seed(24)
DT = data.table(A = sample(letters, 1e7, replace = TRUE), 
                B = sample(letters, 1e7, replace = TRUE), value = 1:1e7)

DT1 <- copy(DT)
system.time({
      setkey(DT, A, B)
    uA <- unique(DT[, A])
    rbind(DT[.(uA, "y"), nomatch=0], DT[.("y"), nomatch=0])
     })
# user  system elapsed 
#  1.14    0.19    0.87 

system.time({
   DT1[DT1[, Reduce('|', lapply(.SD, '==', 'y')), .SDcols = A:B]]
   })
#  user  system elapsed 
#  0.17    0.02    0.19 

【讨论】:

  • 我刚刚又检查了一遍,这个解决方案实际上只对小桌子更快。对于较大的表,它的扩展性很差(它可能使用矢量扫描吗?)。查看我的问题的编辑
  • @FridolinLinder 您没有在基准测试中包含setkey 选项。另外,请尝试检查更多列数,即 15 左右
  • @FridolinLinder 我添加了 system.time,它也将考虑setkey。我没有使用微基准,因为一个setkey完成了,重复的动作会更快。此外,您可以考虑如果列数更多,则使用rbind 的方法可能会变得乏味
  • 这是否意味着您的解决方案不依赖于setkey()? (对不起,我不完全了解细节)。在这种情况下,我将失去对数据进行一次排序然后获得二分搜索加速的好处。这是特定于我的应用程序的,但另一方面,如果在每个子集操作上重复排序步骤,data.tables 二进制搜索永远不会为您提供加速。
  • @FridolinLinder 不,如果你检查我的解决方案,我使用的是“DT”的副本,它没有任何 keykey(DT)# [1] "A" "B"; key(DT1) #NULL 你肯定会加快速度,如果不考虑 setkey 时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多