【问题标题】:Most efficient way to filter data.table based on an aggregated value根据聚合值过滤 data.table 的最有效方法
【发布时间】:2020-08-06 16:08:21
【问题描述】:

什么是推荐的/“最佳”方式(wrt 性能)来过滤 data.table 基于在这个表格的聚合形式上计算的一些标准。

reprex 会说 1000 多个单词:

library(data.table)
DT <- data.table(grp = rep(LETTERS[1:3], each = 3), x = 1:9)
setkey(DT, "grp")
DT[DT[, .(nok = any(x == 4)), by = grp][nok == FALSE]]
DT[DT[, .GRP[all(x != 4)], by = grp]]

我可以立即想到这两种解决方案,我的直觉告诉我第二种形式应该“更好”(存储较小的中间表,我不需要链接结果),但我想知道是否有这样做的规范形式?

也许我一开始就不需要使用连接,而是可以为 i 参数使用分组过滤器?

这显然不能按预期工作(by 显然只对j 有影响):

DT[all(x != 4), by = grp]

虽然SO answer 显示了另一种相同的做法,但我主要关心的是性能。因此,如果我想进一步处理过滤后的 data.table(即在过滤后的结果上使用另一个 j 表达式),我想知道这些选项中的哪些通常可以很好地扩展到大型表。

在我的真实案例中,我有大约 16 个 Mio 行,大约有 40k 个唯一键和 14 列。

因此,基准数据集可能如下所示:

bench <- data.table(keys = rep(paste0("k", 1:40000), 400))
bench[, paste0("cols", 1:13) := replicate(13, sample(40000 * 400, TRUE), 
                                          simplify = FALSE)]

虽然我正在寻找一个通用答案(如果可能的话),而不考虑最终选择的过滤器,但实际的过滤器将找出哪些组包含任何 NA 值。

【问题讨论】:

  • 这能回答你的问题吗? filter rows in data.table with `by`
  • 部分,是的,它会进行过滤,但它没有回答众多选项中的哪一个表现出最高的性能。
  • 然后我会用一些适合比较的实际基准和数据来更新问题。目前没有提到“性能”。
  • 我收回了这一点,但是从整个帖子中并不清楚。如果你能提供一个有代表性的数据集也很好 - 考虑到你有多少列/行/组以及不同的变化,“性能”可能是相当相对的......
  • 添加了基准数据集,明确了我真实案例场景的维度。

标签: r data.table


【解决方案1】:

我向this post学习

你可以这样做。

DT[DT[,.I[all(x!=4)],by=.(grp)]$V1,]

【讨论】:

    【解决方案2】:

    我认为没有适用于所有情况的通用方法。它取决于数据集的特征以及过滤标准。数据集可能有多个小组或整数键,而过滤可以使用更快的低级编程方法来实现。

    以下是针对您的实际问题的一些选项(即过滤其中一列中具有 NA 的组):

    DT_keys <- copy(DT)
    system.time(setkey(DT_keys, keys))
    #   user  system elapsed 
    #   1.50    0.67    1.32 
    
    DT_cols1 <- copy(DT)
    system.time(setkey(DT_cols1, cols1))
    #   user  system elapsed 
    #   4.21    0.21    1.30 
    
    microbenchmark::microbenchmark(times=1L,
        m0 = DT_keys[, keys[is.na(cols1)], keys]$keys,
        m1 = DT_keys[, if (anyNA(cols1)) keys, keys]$keys,
        m2 = DT_cols1[.(NA_integer_)]$keys
    )
    

    1600 万行虚拟数据的时序:

    Unit: milliseconds
     expr       min        lq      mean    median        uq       max neval
       m0 90.675005 90.675005 90.675005 90.675005 90.675005 90.675005     1
       m1 56.548620 56.548620 56.548620 56.548620 56.548620 56.548620     1
       m2  4.010301  4.010301  4.010301  4.010301  4.010301  4.010301     1
    

    对于您的实际数据集的大小而言,时间非常快。除非您运行过滤数百次,否则不会节省太多时间。也许您可能想在运行时之外保存一些其他类型的时间。

    数据:

    library(data.table)
    set.seed(0L)
    nk <- 4e4L
    nn <- 400L
    DT <- data.table(keys = rep(paste0("k", 1L:nk), nn))
    DT[, paste0("cols", 1L:13L) := 
        replicate(13L, sample(c(NA_integer_, 1L:nk), nk * nn, TRUE), simplify = FALSE)]
    

    【讨论】:

    • 感谢您详细的回答 +1。为了我的充分理解:您是否建议最终过滤原始数据集的“最佳”方法是使用此聚合表与原始表的连接 DT[DT_cols1[.(NA_integer_)]$keys]
    • 您会多次运行过滤吗?如果我们重复数据集两次,您会面临任何内存问题吗?
    • 实际上过滤只会运行一次。内存可能开始成为一个问题。所以也许有我们使用相同密钥的解决方案是更可取的。谢谢你的回答让我对data.table(我自己更像一个dplyr)有很大帮助。
    • 那么我建议setkey(DT, keys) 一次,然后使用DT[DT[, if (anyNA(cols1)) keys, keys]$keys]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-07
    • 2019-09-13
    • 2020-06-20
    • 2023-01-29
    • 2020-12-07
    相关资源
    最近更新 更多