通过group-id对数据进行子集化的for循环的更高性能替代是什么?

Ada*_*dam 7 performance loops r subset data.table

我在研究中遇到的反复分析范例是需要根据所有不同的组id值进行子集,依次对每个组进行统计分析,并将结果放在输出矩阵中以便进一步处理/总结.

我通常在R中执行此操作的方式如下:

data.mat <- read.csv("...")  
groupids <- unique(data.mat$ID)  #Assume there are then 100 unique groups

results <- matrix(rep("NA",300),ncol=3,nrow=100)  

for(i in 1:100) {  
  tempmat <- subset(data.mat,ID==groupids[i])  

  # Run various stats on tempmat (correlations, regressions, etc), checking to  
  # make sure this specific group doesn't have NAs in the variables I'm using  
  # and assign results to x, y, and z, for example.  

  results[i,1] <- x  
  results[i,2] <- y  
  results[i,3] <- z  
}
Run Code Online (Sandbox Code Playgroud)

这最终为我工作,但根据数据的大小和我正在使用的组的数量,这可能需要长达三天.

除了分支到并行处理之外,还有什么"技巧"可以让这样的东西运行得更快吗?例如,将循环转换为其他东西(类似于使用包含我想在循环中运行的统计数据的函数的应用),或者不需要实际将数据子集分配给变量?

编辑:

也许这只是常识(或抽样错误),但我尝试在我的一些代码中使用括号进行子集化而不是使用子命令,它似乎提供了一些让我感到惊讶的性能提升.我使用了一些代码并使用与上面相同的对象名称输出如下:

system.time(for(i in 1:1000){data.mat[data.mat$ID==groupids[i],]})  
Run Code Online (Sandbox Code Playgroud)
   user  system elapsed  
 361.41   92.62  458.32
Run Code Online (Sandbox Code Playgroud)
system.time(for(i in 1:1000){subset(data.mat,ID==groupids[i])})  
Run Code Online (Sandbox Code Playgroud)
   user  system elapsed   
 378.44  102.03  485.94
Run Code Online (Sandbox Code Playgroud)

更新:

在其中一个答案中,jorgusch建议我使用data.table包来加速我的子集化.所以,我将它应用于我本周早些时候运行的一个问题.在一个略多于1,500,000行和4列(ID,Var1,Var2,Var3)的数据集中,我想计算每组中的两个相关性(由"ID"变量索引).有超过50,000个团体.下面是我的初始代码(与上面的代码非常相似):

data.mat <- read.csv("//home....")  
groupids <- unique(data.mat$ID)

results <- matrix(rep("NA",(length(groupids) * 3)),ncol=3,nrow=length(groupids))  

for(i in 1:length(groupids)) {  
  tempmat <- data.mat[data.mat$ID==groupids[i],] 

  results[i,1] <- groupids[i]  
  results[i,2] <- cor(tempmat$Var1,tempmat$Var2,use="pairwise.complete.obs")  
  results[i,3] <- cor(tempmat$Var1,tempmat$Var3,use="pairwise.complete.obs")    

}  
Run Code Online (Sandbox Code Playgroud)

我现在正在重新计算这个问题需要花费多长时间,但是从我记忆中来看,当我早上进入办公室并且在下午中午某个时间结束时,我开始运行它.图5-7小时.

重构我的代码以使用data.table ....

data.mat <- read.csv("//home....")  
data.mat <- data.table(data.mat)  

testfunc <- function(x,y,z) {  
  temp1 <- cor(x,y,use="pairwise.complete.obs")  
  temp2 <- cor(x,z,use="pairwise.complete.obs")  
  res <- list(temp1,temp2)  
  res  
}  

system.time(test <- data.mat[,testfunc(Var1,Var2,Var3),by="ID"])  
Run Code Online (Sandbox Code Playgroud)
 user  system  elapsed  
16.41    0.05    17.44  
Run Code Online (Sandbox Code Playgroud)

使用data.table将结果与使用for循环到所有ID和手动记录结果的结果进行比较,它们似乎给了我相同的答案(尽管我必须更彻底地检查一下).这似乎是一个非常大的速度提升.

更新2:

使用子集运行代码最终再次完成:

   user     system   elapsed  
17575.79  4247.41   23477.00
Run Code Online (Sandbox Code Playgroud)

更新3:

我想看看是否有任何不同的方法使用了推荐的plyr包.这是我第一次使用它,所以我可能做得有些低效,但它仍然有助于与带有子集的for循环相比.

使用与以前相同的变量和设置......

data.mat <- read.csv("//home....")  
system.time(hmm <- ddply(data.mat,"ID",function(df)c(cor(df$Var1,df$Var2,  use="pairwise.complete.obs"),cor(df$Var1,df$Var3,use="pairwise.complete.obs"))))  
Run Code Online (Sandbox Code Playgroud)
  user  system elapsed  
250.25    7.35  272.09  
Run Code Online (Sandbox Code Playgroud)

had*_*ley 6

这几乎就是plyr包装设计的目的.然而,它不太可能使事情变得更快 - 大部分时间可能花在做统计上.