【问题标题】:data.table performance questions : get vs [[]], in place computationsdata.table 性能问题:get vs [[]],就地计算
【发布时间】:2020-05-27 07:43:16
【问题描述】:

考虑以下代码 sn-p:

foo <- function(dt, num) {
    expect_equal(class(num), "numeric")
    col <- paste("b", num, sep = "_")
    col2 <- paste("b", num + 1, sep = "_")

    condition <- dt$a > 0

    st <- nanotime(Sys.time())
    dt[condition, a := a - get(col) ]
    dt[condition, a := a - get(col2) ]
    et <- nanotime(Sys.time())
    diff <- (et - st) / 1e9
    message(diff)

    st <- nanotime(Sys.time())
    tmp <- dt$a - dt[[col]]
    tmp <- tmp - dt[[col2]]
    dt[condition, a := tmp[condition]]
    et <- nanotime(Sys.time())
    diff <- (et - st) / 1e9
    message(diff)

    st <- nanotime(Sys.time())
    dt[, tmp := a - get(col)]
    dt[, tmp := a - get(col2)]
    dt[condition, a := tmp]
    et <- nanotime(Sys.time())
    diff <- (et - st) / 1e9
    message(diff)
}

dt <- data.table(c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, i = 0, a = -15000:15000, b_1 = 1L, b_2 = 1L)
foo(dt, 1)

输出
0.002342
0.001131
0.002389

查询
1. get(col)dt[[col]] 慢吗?
2. 向量化,无论多么复杂,对整个数据的计算是否比子集更好?
3. 是否应该在数据表之外进行一系列的计算,最终设置在列中而不是原地计算?

【问题讨论】:

    标签: r performance data.table


    【解决方案1】:

    如果我采用所有内部结构并使用microbenchmark 运行,它将执行每个(以随机顺序)并报告一些良好的统计数据。我会预先计算tmpcondition

    microbenchmark::microbenchmark(
      a = {
        dt[condition, a := a - get(col) ]
        dt[condition, a := a - get(col2) ]
      },
      b = {
        tmp <- dt$a - dt[[col]]
        tmp <- tmp - dt[[col2]]
        dt[condition, a := tmp[condition]]
      },
      b2 = {
        tmp <- dt$a - dt[[col]]
        tmp <- tmp - dt[[col2]]
        set(dt, i = which(condition), j = "a", value = tmp[condition])
      },
      c = {
        dt[, tmp := a - get(col)]
        dt[, tmp := a - get(col2)]
        dt[condition, a := tmp]
      },
      times = 1000
    )
    # Unit: microseconds
    #  expr      min       lq      mean   median       uq       max neval
    #     a 2871.501 2965.951 3429.7118 3058.701 3640.551  9190.800  1000
    #     b  660.601  679.701  788.5797  696.451  805.801  3675.201  1000
    #    b2  166.001  176.801  251.9144  180.201  187.501 39527.302  1000
    #     c 1391.001 1502.901 1633.2692 1530.150 1664.101  3638.701  1000
    
    1. 在您的原始候选组中似乎有一个明显的赢家:dt[[col]] 比他们都强。 编辑:然而,正如@jangorecki(contributordata.table 的重要来源)评论的那样,data.table::set 更快。

    2. 这里没有经过真正的测试,但它确实取决于子集的数量以及计算的“昂贵”程度。在这种情况下,计算是相当微不足道的,所以我预计不会有太大差异。

    3. 您总是在可读性和可维护性与速度和效率之间取得平衡。在我的一些对速度敏感的东西(长度 2-4M)中,我倾向于在原始向量中做所有事情,但这个决定涉及几个因素,而不仅仅是那些涉及 data.table 的因素。一旦你开始获取重要的数据副本(R 做了很多)和data.table 的许多巨大优势(分组、不等式连接等),那么在表中执行它会变得更快,对我来说更重要的是,更多可维护和可读。

    【讨论】:

    • @jangorecki 我们如何将语句d[condition, num := num + 5] 转换为set。我们必须set(d, which(condition), d$num[condition] + 5)。与第一个方法不同,我们需要再次指定条件并通过数据表限定 num
    • 是的,所以注意计算condition 一次,而不是两次。请注意,您错过了一个指定列的 set 参数,在您的示例中使用“num”值。
    猜你喜欢
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    相关资源
    最近更新 更多