variable[1]
或将该变量包括在by
语句中并使用.BY$variable
。假设我想返回的值包括variable
作为列。从以下测试中可以清楚地看出:将其他变量放入
by
语句会减慢速度,即使排除通过这些额外变量来键入(或使用诡计告诉data.table
不需要进行额外键入)的成本。为什么其他已经键入的by
变量会减慢速度?我曾希望包括已经键入的
by
变量是一个方便的语法技巧,用于在j
语句中返回数据表时无需显式命名这些变量,但似乎这是不可取的,因为即使它们已经键入,额外的by
变量也会产生一定开销。那么我的问题是,是什么导致了这种开销?一些示例数据:
library(data.table)
n <- 1e8
y <- data.table(sample(1:5,n,replace=TRUE),rnorm(n),rnorm(n))
y[,sumV2:=sum(V2),keyby=V1]
时间表明,使用variable[1]
方法(在此情况下,sumV2[1]
)更快。
x <- copy(y)
system.time(x[, list(out=sum(V3*V2)/sumV2[1],sumV2[1]),keyby=V1])
system.time(x[, list(out=sum(V3*V2)/.BY$sumV2),keyby=list(V1,sumV2)])
我猜这并不奇怪,因为 data.table
不知道由 setkey(V1) 和 setkey(V1,sumV2) 定义的群组实际上是相同的。
让我感到惊讶的是,即使数据表以 setkey(V1,sumV2)
作为关键字(我们完全忽略设置新关键字所需的时间),使用 sumV2[1]
仍然更快。为什么呢?
x <- copy(y)
setkey(x,V1,sumV2)
system.time(x[, list(out=sum(V3*V2)/sumV2[1],sumV2[1]),by=V1])
system.time(x[, list(out=sum(V3*V2)/.BY$sumV2),by=list(V1,sumV2)])
此外,执行
setkey(x,V2,sumV2)
所需的时间是不可忽略的。是否有办法欺骗data.table,告诉它键实际上并没有实质性地发生改变,从而跳过实际重新设置键值的步骤呢?x <- copy(y)
system.time(setkey(x,V1,sumV2))
回答我的问题,似乎当我们设置键时,只需分配“sorted”属性就可以跳过排序。这样做是否被允许?它会破坏什么吗?
x <- copy(y)
system.time({
setattr(x, "sorted", c("V1","sumV2"))
x[, list(out=sum(V3*V2)/.BY$sumV2),by=list(V1,sumV2)]
})
无论是不良做法还是可能会破坏东西,我都不知道。但使用
setattr
技巧比显式键入要快得多:x <- copy(y)
system.time({
setkey(x,V1,sumV2)
x[, list(out=sum(V3*V2)/.BY$sumV2),by=list(V1,sumV2)]
})
但即使使用setattr
的技巧,并将sumV2
与by语句结合使用,仍然不如完全省略sumV2
在by语句中时快:
x <- copy(y)
system.time(x[, list(out=sum(V3*V2)/sumV2[1],sumV2[1]),keyby=V1])
使用属性设置键并将sumV2作为每个组内变量的长度为1的向量,应该比仅针对V1进行键入并使用sumV2 [1] 更快。如果未将sumV2指定为by变量,则在每个组之前需要生成重复值向量的整个向量,然后将其子集化为sumV2 [1] 。相比之下,当sumV2是by变量时,在每个组中只有一个长度为1的向量。显然,我这里的推理是不正确的。有人能解释一下为什么吗?为什么sumV2 [1] 是最快的选项,即使使用setattr技巧将sumV2作为by变量?
顺便说一句,我很惊讶地发现使用attr < -与setattr一样快(都瞬间完成,表明根本没有复制)。这与我理解的基础R foo <-函数会复制数据相反。
x <- copy(y)
system.time(setattr(x, "sorted", c("V1","sumV2")))
x <- copy(y)
system.time(attr(x,"sorted") <- c("V1","sumV2"))
与此问题相关的SessionInfo()
用于:
data.table version 1.12.2
R version 3.5.3
:=
和keyby
创建sumV2
,所以在x[,sumV2:=sum(V2),keyby=V1]
之后,x
已经按V1
键入,并且data.table
知道这一点。您可以在调用setkey
之前调用key(x)
来证实,或者在调用setkey
(在您的最后一个示例中)时设置verbose=TRUE
。 - AlexissumV2
作为x的一列创建,而是在单个复合j
语句中创建sumV2
作为j中的临时变量,并用它来计算加权平均值。但是你可能同时想要在x
中重复sumV2
的值和另一个包含加权平均值的更短的data.table。 - Michael