【问题标题】:Why is transform.data.table so much slower than transform.data.frame?为什么 transform.data.table 比 transform.data.frame 慢这么多?
【发布时间】:2013-08-13 18:37:36
【问题描述】:

我有一个小的 data.table 并且使用 transform 需要很长时间。这是一个可重现的示例:

library(data.table)
#data.table 1.8.8
set.seed(1) 

dataraw <- data.table(sig1 = runif(80000, 0, 9999),
                      sig2 = runif(80000, 0, 9999),
                      sig3 = runif(80000, 0, 9999))

system.time(transform(dataraw, d = 1))
#  user      system     elapsed 
#16.345       0.016      16.359 

dataraw2 <- as.data.frame(dataraw)

system.time(transform(dataraw2, d = 1))
# user      system     elapsed 
#0.002       0.002       0.005 

为什么transform 使用 data.table 比使用 data.frame 慢?

【问题讨论】:

  • 这是fixed long before(在 v1.8.10 中!!)。您能否接受答案并关闭此问题?谢谢。
  • 当然。虽然我将其改写为:“糟糕的实现。使用固定版本”

标签: r performance data.table


【解决方案1】:

更新:很久以前,在 v1.8.10 中已修复此问题。来自新闻:

o transform()data.table 上的缓慢已得到修复,#2599。但是,请使用:=


尽管从文档和?transform.data.table(也来自SenorO 的帖子)清楚地表明,惯用的方法是使用:=(通过引用分配),这非常快,但我认为知道它仍然很有趣为什么 transformdata.table 上速度较慢。就我目前所了解的情况而言,transform.data.table并不总是较慢

我会尝试在这里回答这个问题。 transform.data.table 本身似乎不是问题,而是对 data.table() 函数的调用。通过查看data.table:::transform.data.table,滞后来自于线路:

ans <- do.call("data.table", c(list(`_data`), e[!matched]))

所以,让我们用一个大的data.table 来对这条线进行基准测试,并按顺序排列值:

DT <- data.table(x=1:1e5, y=1:1e5, z=1:1e5)
system.time(do.call("data.table", c(list(DT), list(d=1))))
   user  system elapsed 
  0.003   0.003   0.026 

哦,这非常快!让我们进行相同的基准测试,但按顺序使用 not 值:

DT <- data.table(x=sample(1e5), y=sample(1e5), z=sample(1e5))
system.time(do.call("data.table", c(list(DT), list(d=1))))

   user  system elapsed 
  7.986   0.016   8.099 

# tested on 1.8.8 and 1.8.9

它变慢了。造成这种差异的原因是什么?为此,我们必须调试data.table() 函数。通过做

DT <- data.table(x=as.numeric(1:1e5), y=as.numeric(1:1e5), z=as.numeric(1:1e5))
debugonce(data.table)
transform(DT, d=1)

通过连续点击“enter”,你就可以找到导致这种缓慢的原因:

exptxt = as.character(tt) # roughly about 7.2 seconds

很明显as.character 成为问题所在。为什么?为此,请比较:

as.character(data.frame(x=1:10, y=1:10))
# [1] "1:10" "1:10"

as.character(data.frame(x=sample(10), y=sample(10)))
# [1] "c(9, 10, 4, 7, 6, 5, 1, 3, 8, 2)" "c(8, 5, 3, 7, 6, 10, 9, 1, 4, 2)"

在更大的数据上重复此操作,以查看采样 data.frame 上的 as.character 变得变慢

那么,问题就变成了,为什么不是

data.table(x = sample(1e5), y=sample(1e5))

耗时?这是因为,data.table() 函数的输入被替换(使用subsitute())。在这种情况下,tt 变为:

$x
sample(1e+05)

$y
sample(1e+05)

然后as.character(tt) 就变成了:

# [1] "sample(1e+05)" "sample(1e+05)"

这意味着,如果你这样做:

DT <- data.table(x = c(1,3,4,1,4,1,3,1,2...), y = c(1,1,4,1,3,4,1,1,3...))

我想这会花费很多时间(通常不会这样做,因此没有问题)。

【讨论】:

  • +1 用于查找问题的根源。我确实认为这是一个有效的问题,即使它变成了一场关于 R 背后哲学的奇怪斗争。
  • @SeñorO 我不想重新启动它,但这与 R 无关。一般原则是,在一个使用场景中失去速度的唯一充分理由是在其他用途​​中获得一些。由于通用转换不传达有关特定遍历的信息,因此对于任何语言都没有充分的理由降低性能。这只是常识。如果人们试图告诉你其他情况,他们就是骗子。
  • @nicolas,不惜一切代价,it is a known issue; FR#2599。这不是一个优先事项,因为transform 并不能真正与data.table 一起使用(或者更确切地说,与data.table 一起使用没有意义)。最终,它会得到修复。我建议您阅读文档和常见问题解答以最好地利用 data.table。如果您认为存在设计缺陷/任何其他缺陷,您应该将其提交给开发人员 - register on data.table mailing list
  • 我认为这是一件明智的事情:在不需要专门化的情况下,它使函数的代码独立于参数的实现。因此,我最初感到惊讶的是,我认为我错过了一些东西,我做到了:文档中的评论 + 错误报告。
【解决方案2】:

来自?transform.data.table

transform by group is particularly slow. Please use := by group instead.

within, transform and other similar functions in data.table are not just provided 
for users who expect them to work, but for non-data.table-aware packages to 
retain keys, for example. Hopefully the (much) faster and more convenient 
data.table syntax will be used in time. 

正如@Roland 建议的那样,您应该始终分解代码的组件以找出实际占用时间/资源的内容。在这种情况下,它不是log,而是transform。将:= 用于data.tables,transform 用于data.frames、列表等。

罪魁祸首不是log

> dt <- data.table(A=1:1000000)
> system.time(transform(as.data.frame(dt), B=A * 1))
   user  system elapsed 
   0.00    0.02    0.01 
> system.time(transform(dt, B=A * 1))
   user  system elapsed 
  14.61    0.00   14.61 

【讨论】:

    猜你喜欢
    • 2013-02-01
    • 2013-06-05
    • 2019-12-05
    • 2019-06-26
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 2011-02-28
    • 2018-02-18
    相关资源
    最近更新 更多