R中用于从非常大的矩阵读取和写入列子集的最快方法是什么.我尝试使用data.table的解决方案,但需要一种快速的方法来提取一系列列?
简答:操作的昂贵部分是分配.因此,解决方案是坚持使用矩阵并使用Rcpp和C++来修改矩阵.下面有两个很好的答案和例子.[适用于其他问题的人一定要阅读解决方案中的免责声明!].滚动到问题的底部,了解更多经验教训.
这是我的第一个Stack Overflow问题 - 我非常感谢您抽出时间看看,如果我遗漏了任何内容,我会深表歉意.我正在研究一个R软件包,其中我有一个性能瓶颈,从子集化和写入矩阵的部分(统计学家的NB,应用程序在处理每个数据点后更新足够的统计数据).单个操作速度非常快,但是它们的数量绝对要求它尽可能快.该概念的最简单版本是尺寸K乘V的矩阵,其中K通常在5和1000之间,V可以在1000和1,000,000之间.
set.seed(94253)
K <- 100
V <- 100000
mat <-  matrix(runif(K*V),nrow=K,ncol=V)
Run Code Online (Sandbox Code Playgroud)
然后,我们最终对列的子集执行计算并将其添加到完整矩阵中.因此看起来很天真
Vsub <- sample(1:V, 20)
toinsert <- matrix(runif(K*length(Vsub)), nrow=K, ncol=length(Vsub))
mat[,Vsub] <- mat[,Vsub] + toinsert
library(microbenchmark)
microbenchmark(mat[,Vsub] <- mat[,Vsub] + toinsert)
Run Code Online (Sandbox Code Playgroud)
因为这样做很多次,由于R的复制变更语义,它可能会很慢(但是参见下面的经验教训,修改实际上可以在某些情况下发生).
对于我的问题,对象不必是一个矩阵(我对这里概述的差异很敏感.将一个矩阵分配给data.table的子集).我总是想要完整的列,因此数据框的列表结构很好.我的解决方案是使用Matthew Dowle的令人敬畏的data.table包.使用set()可以非常快速地完成写入.不幸的是,获得价值有点复杂.我们必须使用= FALSE调用变量设置,这会大大减慢速度.
library(data.table)
DT <- as.data.table(mat)  
set(DT, i=NULL, j=Vsub,DT[,Vsub,with=FALSE] + as.numeric(toinsert))
Run Code Online (Sandbox Code Playgroud)
在set()函数中使用i = NULL来引用所有行的速度非常快但是(可能是由于事物存储在引擎盖下的方式),j没有可比较的选项.@Roland在评论中指出,一个选项是转换为三重表示(行号,列号,值)并使用data.tables二进制搜索来加速检索.我手动测试了它,虽然速度很快,但它大约是矩阵内存需求的三倍.如果可能的话,我想避免这种情况.
在这里提出问题:从data.table和data.frame对象获取单个元素的时间.Hadley Wickham为单个索引提供了一个非常快速的解决方案
Vone <- Vsub[1]
toinsert.one <- toinsert[,1]
set(DT, i=NULL, j=Vone,(.subset2(DT, Vone) + toinsert.one))
Run Code Online (Sandbox Code Playgroud)
但是因为.subset2(DT,i)只是没有方法调度的DT [[i]],所以没有办法(据我所知)一次抓取几个列,尽管看起来它应该是可能的.与前一个问题一样,似乎因为我们可以快速覆盖这些值,所以我们应该能够快速读取它们.
有什么建议?如果这个问题有一个比data.table更好的解决方案,请告诉我.我在许多方面意识到它并不是真正的预期用例,但我试图避免将整个系列的操作移植到C.
以下是所讨论元素的一系列时序 - 前两个是所有列,后两个只是一列.
microbenchmark(mat[,Vsub] …Run Code Online (Sandbox Code Playgroud)