作为对Arun答案的补充,这里有一个与 OP(350 万行,80K ID)大小相似的数据集,表明键控/非键控聚合并没有太大的不同。所以加速可能是由于避免了$
运营商。
set.seed(10)
eg <- function(x) data.table(id=sample(8e4,x,replace=TRUE),timestamp=as.POSIXct(runif(x,min=ISOdatetime(2013,1,1,0,0,0) - 60*60*24*30, max=ISOdatetime(2013,1,1,0,0,0)),origin="1970-01-01"))
df <- eg(3.5e6)
dfk <- copy(df)
setkey(dfk,id)
require(microbenchmark)
microbenchmark(
unkeyed = df[,min(timestamp),by=id][,table(weekdays(V1))]
,keyed = dfk[,min(timestamp),by=id][,table(weekdays(V1))]
,times=5
)
#Unit: seconds
# expr min lq median uq max
#1 keyed 7.330195 7.381879 7.476096 7.486394 7.690694
#2 unkeyed 7.882838 7.888880 7.924962 7.927297 7.931368
从马修编辑。
实际上以上几乎完全与 type 有关POSIXct
。
> system.time(dfk[,min(timestamp),by=id])
user system elapsed
8.71 0.02 8.72
> dfk[,timestamp:=as.double(timestamp)] # discard POSIXct type to demonstrate
> system.time(dfk[,min(timestamp),by=id])
user system elapsed
0.14 0.02 0.15 # that's more like normal data.table speed
恢复到 POSIXct 并使用 Rprof 显示它在min()
该类型内部的 97%(即与 无关data.table
):
$by.total
total.time total.pct self.time self.pct
system.time 8.70 100.00 0.00 0.00
[.data.table 8.64 99.31 0.12 1.38
[ 8.64 99.31 0.00 0.00
min 8.46 97.24 0.46 5.29
Summary.POSIXct 8.00 91.95 0.86 9.89
do.call 5.86 67.36 0.26 2.99
check_tzones 5.46 62.76 0.20 2.30
unique 5.26 60.46 2.04 23.45
sapply 3.74 42.99 0.46 5.29
simplify2array 2.38 27.36 0.16 1.84
NextMethod 1.28 14.71 1.28 14.71
unique.default 1.10 12.64 0.92 10.57
lapply 1.10 12.64 0.76 8.74
unlist 0.60 6.90 0.28 3.22
FUN 0.24 2.76 0.24 2.76
match.fun 0.22 2.53 0.22 2.53
is.factor 0.18 2.07 0.18 2.07
parent.frame 0.14 1.61 0.14 1.61
gc 0.06 0.69 0.06 0.69
duplist 0.04 0.46 0.04 0.46
[.POSIXct 0.02 0.23 0.02 0.23
注意 的对象大小dfk
:
> object.size(dfk)
40.1 Mb
data.table
对于这个小尺寸,什么都不应该花 7 秒!它需要大 100 倍(4GB),并且没有缺陷j
,然后您才能看到 keyed by 和 ad hoc by 之间的区别。
蓝色魔导师编辑:
考虑到 Matthew Dowle 的回答,键控/非键控命令之间存在差异。
df <- eg(3.5e6)
df[,timestamp := as.double(timestamp)]
dfk <- copy(df)
setkey(dfk,id)
require(microbenchmark)
microbenchmark(
unkeyed = df[,min(timestamp),by=id][,table(weekdays(as.POSIXct(V1,origin="1970-01-01")))]
,keyed = dfk[,min(timestamp),by=id][,table(weekdays(as.POSIXct(V1,origin="1970-01-01")))]
,times=10
)
#Unit: milliseconds
# expr min lq median uq max
#1 keyed 340.3177 346.8308 348.7150 354.7337 358.1348
#2 unkeyed 886.1687 888.7061 901.1527 945.6190 1036.3326