首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对于按组id设置数据的for -循环,有什么更高性能的替代方案?

对于按组id设置数据的for -循环,有什么更高性能的替代方案?
EN

Stack Overflow用户
提问于 2010-03-27 00:46:29
回答 4查看 2.6K关注 0票数 7

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

我通常如何在R中这样做,如下所示:

代码语言:javascript
复制
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  
}

这最终对我有用,但取决于数据的大小和与我一起工作的组的数量,这可能需要三天的时间。

除了扩展到并行处理之外,还有什么“诀窍”可以让这样的事情跑得更快吗?例如,将循环转换为其他东西(类似于带有包含我希望在循环中运行的统计信息的函数的应用程序),还是不需要将数据子集实际分配给变量?

编辑:

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

代码语言:javascript
复制
system.time(for(i in 1:1000){data.mat[data.mat$ID==groupids[i],]})  

user system elapsed 361.41 92.62 458.32

代码语言:javascript
复制
system.time(for(i in 1:1000){subset(data.mat,ID==groupids[i])})  

user system elapsed 378.44 102.03 485.94

更新:

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

代码语言:javascript
复制
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")    

}  

我现在正在重新计算它花了多长时间,但据我所记得,我早上到办公室的时候就开始跑了,午后的某个时候它就完成了。图5-7小时。

将我的代码重组为使用data.table..。

代码语言:javascript
复制
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"])  

用户系统运行时间16.41 0.05 17.44

将使用data.table的结果与我使用for循环对所有ID进行子集并手动记录结果所得到的结果进行比较,它们似乎给出了相同的答案(尽管我需要更彻底地检查)。这看起来是一个相当大的提速。

更新2:

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

user system elapsed 17575.79 4247.41 23477.00

更新3:

我想看看使用也推荐的plyr包是否有不同的效果。这是我第一次使用它,所以我可能已经做了一些低效率的事情,但它仍然在很大程度上帮助了for循环的子设置。

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

代码语言:javascript
复制
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"))))  

用户系统经过250.25 7.35 272.09

EN

回答 4

Stack Overflow用户

发布于 2010-03-27 01:31:15

这正是plyr包设计的目的是让它变得更容易。然而,它不太可能让事情变得更快--大部分时间可能都花在了统计数据上。

票数 6
EN

Stack Overflow用户

发布于 2010-03-27 11:59:42

除了plyr,您还可以尝试使用foreach包来排除显式循环计数器,但我不知道它是否会给您带来任何性能上的好处。

Foreach为您提供了一个非常简单的并行块处理接口,如果您有多核工作站(带有doMC/multicore包)(查看开始使用doMC和foreach获取详细信息),如果仅因为对学生来说不太容易理解并行处理,则会提供一个非常简单的接口。如果这不是唯一的原因,plyr是很好的解决方案IMHO。

票数 3
EN

Stack Overflow用户

发布于 2010-03-27 02:21:07

您已经建议将中间结果进行矢量化和避免不必要的复制,因此您肯定在正确的轨道上。让我提醒您不要做我所做的事情,只是假设矢量化总是会提高性能(就像在其他语言中一样,例如Python + NumPy,MATLAB)。

举个例子:

代码语言:javascript
复制
# small function to time the results:
time_this = function(...) {
  start.time = Sys.time(); eval(..., sys.frame(sys.parent(sys.parent()))); 
  end.time = Sys.time(); print(end.time - start.time)
}

# data for testing: a 10000 x 1000 matrix of random doubles
a = matrix(rnorm(1e7, mean=5, sd=2), nrow=10000)

# two versions doing the same thing: calculating the mean for each row
# in the matrix
x = time_this( for (i in 1:nrow(a)){ mean( a[i,] ) } )
y = time_this( apply(X=a, MARGIN=1, FUN=mean) )

print(x)    # returns => 0.5312099
print(y)    # returns => 0.661242

“应用”版本实际上是比“for”版本更慢的。(根据Inferno作者的说法,如果您正在这样做,您不是在矢量化,而是在“循环隐藏”。)

但是,您可以通过使用内置来提高性能。下面,我使用内置函数“rowMeans”对上面的两个操作进行了计时:

代码语言:javascript
复制
z = time_this(rowMeans(a))
print(z)    # returns => 0.03679609

相对于“for”循环(和矢量化版本),改进的数量级。

应用程序族的其他成员不仅仅是本机“for”循环的包装器。

代码语言:javascript
复制
a = abs(floor(10*rnorm(1e6)))

time_this(sapply(a, sqrt))
# returns => 6.64 secs

time_this(for (i in 1:length(a)){ sqrt(a[i])})
# returns => 1.33 secs

与“for”循环相比,5x慢一些。

最后,w/r/t向量化循环与“for”循环相比,如果我可以使用向量化函数,我认为我从来没有使用过循环--后者通常是较少的击键量,而且对于我来说,这是一种更自然的编码方式,这是一种不同的性能提升,我想。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2527713

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档