更新:很久以前,在 v1.8.10 中已修复此问题。来自新闻:
o transform() 在data.table 上的缓慢已得到修复,#2599。但是,请使用:=。
尽管从文档和?transform.data.table(也来自SenorO 的帖子)清楚地表明,惯用的方法是使用:=(通过引用分配),这非常快,但我认为知道它仍然很有趣为什么 transform 在 data.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...))
我想这会花费很多时间(通常不会这样做,因此没有问题)。