Какова более высокопроизводительная альтернатива циклам for для подстановки данных по идентификатору группы?

Парадигма повторяющегося анализа, с которой я сталкиваюсь в своем исследовании, заключается в необходимости поднабора на основе всех различных значений идентификатора группы, выполнения статистического анализа по каждой группе по очереди и помещения результатов в выходную матрицу для дальнейшей обработки / суммирования.

Как я обычно делаю это в 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»). Есть чуть более 50000 групп. Ниже мой исходный код (который очень похож на выше):

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 для подстановки всех идентификаторов и записи результатов вручную, они, похоже, дали мне те же ответы (хотя мне придется проверить это чуть более подробно). Это выглядит как довольно большое увеличение скорости.

Обновление 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  

Ответы на вопрос(4)

Ваш ответ на вопрос