为什么transform.data.table比transform.data.frame慢得多?

时间:2013-08-13 18:37:36

标签: r performance data.table

我有一个小的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 

与使用data.frame相比,为什么transform使用data.table会这么慢?

2 个答案:

答案 0 :(得分:14)

更新:在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 

哦,这太快了!我们的基准测试相同,但值的顺序为:

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)"

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

现在,问题变成了,为什么不是

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 :(得分:11)

来自?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,list等。

罪魁祸首不是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