我在研究中遇到的一个反复出现的分析范式是,需要基于所有不同的组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
}这最终对我有用,但取决于数据的大小和与我一起工作的组的数量,这可能需要三天的时间。
除了扩展到并行处理之外,还有什么“诀窍”可以让这样的事情跑得更快吗?例如,将循环转换为其他东西(类似于带有包含我希望在循环中运行的统计信息的函数的应用程序),还是不需要将数据子集实际分配给变量?
编辑:
也许这只是常识(或抽样错误),但我尝试在我的一些代码中添加括号,而不是使用子集命令,它似乎提供了一些性能上的提高,这让我感到惊讶。我使用了一些代码,并在下面使用与上面相同的对象名称输出:
system.time(for(i in 1:1000){data.mat[data.mat$ID==groupids[i],]}) user system elapsed 361.41 92.62 458.32
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多个团体。下面是我的初始代码(非常类似于上面的代码):
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..。
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循环的子设置。
使用和以前相同的变量和设置.
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
发布于 2010-03-27 01:31:15
这正是plyr包设计的目的是让它变得更容易。然而,它不太可能让事情变得更快--大部分时间可能都花在了统计数据上。
发布于 2010-03-27 11:59:42
除了plyr,您还可以尝试使用foreach包来排除显式循环计数器,但我不知道它是否会给您带来任何性能上的好处。
Foreach为您提供了一个非常简单的并行块处理接口,如果您有多核工作站(带有doMC/multicore包)(查看开始使用doMC和foreach获取详细信息),如果仅因为对学生来说不太容易理解并行处理,则会提供一个非常简单的接口。如果这不是唯一的原因,plyr是很好的解决方案IMHO。
发布于 2010-03-27 02:21:07
您已经建议将中间结果进行矢量化和避免不必要的复制,因此您肯定在正确的轨道上。让我提醒您不要做我所做的事情,只是假设矢量化总是会提高性能(就像在其他语言中一样,例如Python + NumPy,MATLAB)。
举个例子:
# 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”对上面的两个操作进行了计时:
z = time_this(rowMeans(a))
print(z) # returns => 0.03679609相对于“for”循环(和矢量化版本),改进的数量级。
应用程序族的其他成员不仅仅是本机“for”循环的包装器。
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”循环相比,如果我可以使用向量化函数,我认为我从来没有使用过循环--后者通常是较少的击键量,而且对于我来说,这是一种更自然的编码方式,这是一种不同的性能提升,我想。
https://stackoverflow.com/questions/2527713
复制相似问题