我在研究中遇到的一个反复出现的分析范式是需要根据所有不同的组 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"])
user system elapsed 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"))))
user system elapsed 250.25 7.35 272.09