为什么第二种方法会因增加data.table大小而变慢:
library(data.table)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
1:
DF1=DF2=DF
system.time(DF[y==6,"y"]<-10)
user system elapsed
2.793 0.699 3.497
2:
system.time(DF1$y[DF1$y==6]<-10)
user system elapsed
6.525 1.555 8.107
3:
system.time(DF2[y==6, y := 10]) # slowest!
user system elapsed
7.925 0.626 8.569
>sessionInfo()
R version 3.2.1 (2015-06-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 14.04.3 LTS
有没有更快的方法呢?
答案 0 :(得分:10)
在你的最后一个案例中,它是data.table
中自动索引功能的结果,因为v1.9.4 +。阅读更多全文: - )。
执行DT[col == .]
或DT[col %in% .]
时,首次运行时会自动生成索引。索引只是您指定的列的order
。索引的计算非常快(使用计数排序/真基数排序)。
该表是1.2亿行,大致需要:
# clean session
require(data.table)
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(data.table:::forderv(DF, "y"))
# 3.923 0.736 4.712
旁注:列
y
无需真正 double (订购时间更长)。如果我们将其转换为整数类型:DF[, y := as.integer(y)] system.time(data.table:::forderv(DF, "y")) # user system elapsed # 0.569 0.140 0.717
优势在于使用==
或%in%
的该列上的任何后续子集都将非常快速(Matt的Slides,R script,video&# 39;演讲)。例如:
# clean session, copy/paste code from above to create DF
system.time(DF[y==6, y := 10])
# user system elapsed
# 4.750 1.121 5.932
system.time(DF[y==6, y := 10])
# user system elapsed
# 4.002 0.907 4.969
哦等一下......它不快。但..索引..?!?我们每次都使用新值替换相同的列。这导致该列的顺序发生变化(从而删除索引)。让我们对y
进行子集化,但修改v
:
# clean session
require(data.table)
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(DF[y==6, v := 10L])
# user system elapsed
# 4.653 1.071 5.765
system.time(DF[y==6, v := 10L])
# user system elapsed
# 0.685 0.213 0.910
options(datatable.verbose=TRUE)
system.time(DF[y==6, v := 10L])
# Using existing index 'y'
# Starting bmerge ...done in 0 secs
# Detected that j uses these columns: v
# Assigning to 40000059 row subset of 120000000 rows
# user system elapsed
# 0.683 0.221 0.914
您可以看到计算索引的时间(使用二进制搜索)需要0秒。另请检查?set2key()
。
如果您不打算重复进行子集化,或者在您的情况下进行子集化和修改同一列,那么通过options(datatable.auto.index = FALSE)
,#1264来禁用该功能是有意义的。 }:
# clean session
require(data.table)
options(datatable.auto.index = FALSE) # disable auto indexing
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(DF[y==6, v := 10L])
# user system elapsed
# 1.067 0.274 1.367
system.time(DF[y==6, v := 10L])
# user system elapsed
# 1.100 0.314 1.443
这里的差异并不大。矢量扫描的时间是system.time(DF$y == 6)
= 0.448s
。
总而言之,在您的情况下,矢量扫描更有意义。但总的来说,我们的想法是,最好只支付一次惩罚并在该列的未来子集上获得快速结果,而不是每次都进行矢量扫描。
自动索引功能相对较新,并且会随着时间的推移而延长,并且可能已经过优化(可能还有一些地方我们没有看过)。在回答这个Q时,我意识到我们没有显示计算排序顺序的时间(使用
fsort()
,我想在那里度过的时间可能是时间非常接近的原因sessionInfo()
3}})。
至于你的第二个案例是缓慢的,不太清楚为什么。我怀疑这可能是由于R部分不必要的副本。您使用的是哪个版本的R?对于将来,请始终发布您的content/Language.properties
输出。