R data.table使用.SD时聚合速度慢
我正在对data.table(优秀的软件包!!!)进行聚合,我发现.SD变量在很多方面都非常有用。但是,当有许多组时,使用它会显著降低计算速度。下面是一个例子:R data.table使用.SD时聚合速度慢,r,data.table,R,Data.table,我正在对data.table(优秀的软件包!!!)进行聚合,我发现.SD变量在很多方面都非常有用。但是,当有许多组时,使用它会显著降低计算速度。下面是一个例子: # A moderately big data.table x = data.table(id=sample(1e4,1e5,replace=T), code=factor(sample(2,1e5,replace=T)), z=runif(1e5)
# A moderately big data.table
x = data.table(id=sample(1e4,1e5,replace=T),
code=factor(sample(2,1e5,replace=T)),
z=runif(1e5)
)
setkey(x,id,code)
system.time(x[,list(code2=nrow(.SD[code==2]), total=.N), by=id])
## user system elapsed
## 6.226 0.000 6.242
system.time(x[,list(code2=sum(code==2), total=.N), by=id])
## user system elapsed
## 0.497 0.000 0.498
system.time(x[,list(code2=.SD[code==2,.N], total=.N), by=id])
## user system elapsed
## 6.152 0.000 6.168
我做错什么了吗?我应该避免使用.SD来支持单个列吗?提前感谢。尝试通过将计算分为两个步骤,然后合并生成的数据帧来解决此问题:
system.time({
x2 <- x[code==2, list(code2=.N), by=id]
xt <- x[, list(total=.N), by=id]
print(x2[xt])
})
我是否做错了什么事,即我是否应该避免.SD
,而支持单个列
是的,没错。如果您确实正在使用.SD
中的所有数据,请仅使用.SD
。您还可能会发现对nrow()
的调用和对[.data.table
的子查询调用也是罪魁祸首:使用Rprof
进行确认
见以下最后几句:
常见问题解答2.1如何避免编写很长的j表达式?您说过我应该使用列名,但我有很多列。分组时,
j
表达式可以使用列名作为变量,如下所示:
您知道,但它也可以使用一个保留符号.SD
,它表示
每个组(不包括分组)的Data.table的子集
列),所以总结一下你所有的列
DT[,lappy(.SD,sum),by=grp]
。这可能看起来很棘手,但很快就能解决
编写并快速运行。注意,您不必创建匿名
功能。有关与其他功能的比较,请参见计时小插曲和wiki
方法:.SD
对象在内部高效地实现,并且
比将参数传递给函数更有效。请不要这样做
虽然:DT[,sum(.SD[,“sales”,with=FALSE]),by=grp]
。这很有效,但非常有用
低效和不雅观。这是我们的初衷:
DT[,sum(sales),by=grp]
可能快100倍。
另请参见常见问题3.1的第一个项目符号:
常见问题解答3.1我有20列和大量行。为什么一列的表达式如此快速?有几个原因:
--只有那一列是正确的 分组后,其他19个被忽略,因为
数据。表检查j
表达式,并意识到它不使用其他列
当data.table
检查j
并看到.SD
符号时,效率增益就消失了。它必须为每个组填充整个.SD
子集,即使您不使用其所有列。data.table
很难知道您选择了.SD
的哪些列实际上正在使用(j
可以包含if
s,例如)。但是,如果您仍然需要它们,当然这并不重要,例如在DT[,lapply(.SD,sum),by=…]
中。这是.SD
的理想用法
因此,是的,尽可能避免使用.SD
。直接使用列名来提供数据。表对j
的优化是最好的机会。在j
中仅存在符号.SD
是很重要的
这就是引入.SDcols
的原因。因此,如果只需要子集,您可以告诉数据.table
哪些列应该在.SD
中。否则,数据.table
将使用所有列填充.SD
,以防j
需要它们。(+1)我会用x2
替换x2
,实际上这比x[,list(sum(code==2),.N),by=id]
(有问题的示例2)快得多,不是吗!可能是因为你避免了对每个组重复调用=
(这些小向量的相关分配等)。@Arun你确定吗?x2[xt][is.na(code2
有一些行。你只是得到了NA
而不是0
。可能是错误的,只是看得很快。@MatthewDowle,不是,我只是说在x2
的步骤中,x2
有9921行而不是9999行。当然,在最后,用x[y],这些行得到了NA。我想用J(关键列)进行索引
应该比逻辑索引快
…我弄错了吗?@Arun对单个列的单个向量扫描是一种特殊情况:unique(id)
调用无论如何也必须扫描每个项,然后再加上加入的时间。请检查一下,x2
有9999行,而不是9921行,这不是一个问题?(这就是你上面的评论所暗示的)。非常感谢!我被短语“该.SD对象在内部有效地实现,并且比将参数传递给函数更有效”所欺骗,并且不理解“尽管如此,请不要这样做:DT[,sum(.SD[,“sales”,with=FALSE]),by=grp]”由于with=FALSE。这将大大加快我的代码的速度!@vsalmendra啊,是的,它可能会更清晰。这是过去留给社区讨论的事情。最终我们希望改进j
优化,这样用户就不需要知道这样的事情了。@vsalmendra我现在为下一版本改进了FAQ 2.1。
id code2 total
1: 1 6 14
2: 2 8 10
3: 3 7 13
4: 4 5 13
5: 5 9 18
---
9995: 9996 4 9
9996: 9997 3 6
9997: 9998 6 10
9998: 9999 3 4
9999: 10000 3 6
user system elapsed
0.05 0.00 0.04