Преобразование вложенного цикла for в параллельный в R

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

celnum <- c(10,20,30)
t2 <- c(1,2,3)
allrepeat <- 10

samplefunction <- function(celnum,t2){

        x <- rnorm(100,celnum,t2)
        y = sample(x, 1)
        z = sample(x,1)

        result = y+z


        result 
}

Получение результатов традиционным способом:

z_grid <- matrix(, nrow = length(celnum), ncol = length(t2))

repetitions <- matrix(, nrow = allrepeat, ncol = 1)



set.seed=20
for(i in 1:length(celnum)){
        for (j in 1:length(t2)){
                for (k in 1:allrepeat) {
                        results <- samplefunction(celnum[i],t2[j]) 
                                repetitions[k] <- results
                                z_grid[i,j] <- mean(repetitions,na.rm=TRUE) 
                }  
        }
}

z_grid

Теперь пытаемся сделать то же самое с помощью foreach:

set.seed=20

library(foreach)
library(doSNOW)

cl <- makeCluster(3, type = "SOCK")
registerDoSNOW(cl)

set.seed=20
output <- foreach(i=1:length(celnum),.combine='cbind' ) %:% 
        foreach (j=1:length(t2), .combine='c') %:%   
                foreach (k = 1:allrepeat) %do% {
                        mean(samplefunction(celnum[i],t2[j]) )
}  
output

Это не работает так, как хотелось бы, так как возвращает матрицу размером 30x2 вместо 3x3. Я намерен смоделировать сценарий для комбинаций i и j k раз и хотел бы получить среднее значение этих k симуляций для каждой комбинации i и j.


person MIH    schedule 25.07.2016    source источник


Ответы (1)


ИЗМЕНИТЬ:

Вложенные циклы for должны выглядеть так. Обратите внимание, что вложен только один цикл foreach и два цикла for.

library(foreach)
library(doSNOW)

cl <- makeCluster(3, type = "SOCK")
registerDoSNOW(cl)

set.seed(20)
output <- foreach(k=1:allrepeat) %dopar% {
  df <- data.frame()
  for (i in 1:length(t2)) {
    for (j in 1:length(celnum)) {
      df[i,j] <- mean(samplefunction(celnum[i],t2[j]))
    }  
  }
  df
}

Результат output также является list. Чтобы вычислить значение ячейки, этот пост очень помог.

library(plyr)
aaply(laply(output, as.matrix), c(2,3), mean)

#   X2
# X1       V1       V2       V3
#  1 20.30548 21.38818 18.49324
#  2 40.09506 40.64564 40.34847
#  3 60.10946 59.68913 58.66209

кстати: вы должны...

stopCluster(cl)

... после.


Исходное сообщение:

Сначала вы должны определить, какой из циклов for вы хотите заменить циклом foreach.

В основном на это решение больше всего влияют результаты цикла и, следовательно, то, как эти результаты могут быть объединены. Поскольку вы передаете отдельные процессы отдельным процессорам вашего ПК, будет возвращен только последний элемент. Эти результаты будут объединены, как указано в параметре .combine (например, 'c', 'cbind' и т. д.). Поскольку вы пытаетесь создать два списка, это может быть не очень просто для первого запуска. Таким образом, я хотел бы предложить пример, который описывает функциональность цикла foreach, вложенного в другие циклы for.

library(foreach)
library(doSNOW)

dat1 <- c(15.2, 12.58, 4.25, 1.05, 6.78, 9.22, 11.20)
dat2 <- data.frame(matrix(1:15, ncol = 3))


cl <- makeCluster(3, type = "SOCK")
registerDoSNOW(cl)

for (i in 1:nrow(dat2)) {
  FEresult <- foreach(j = 1:ncol(dat2), .combine = c, .inorder = TRUE) %dopar% {
    tmp <- dat1 * dat2[i, j]
    data.frame(tmp)
  }
  FEresult
  if (i == 1) {
    res <- FEresult
  } else {
    res <- rbind(res, FEresult)
  }
}

res

Вы заметите, что результатом этого цикла является список.

person loki    schedule 25.07.2016
comment
Спасибо. Я решил немного упростить функцию (см. отредактированный пост). Не могли бы вы взглянуть и посмотреть, какая последняя настройка поможет ему работать? - person MIH; 27.07.2016
comment
когда я копирую цикл, он работает. возможно, вы отредактируете сообщение об ошибке в своем вопросе, чтобы я мог его посмотреть - person loki; 27.07.2016
comment
Нет сообщения об ошибке, но результаты не такие, как хотелось бы. Я хотел бы получить те же результаты в output, что и в z_grid. Первый представляет собой матрицу [3,3] из [i,j], где k симуляций этой матрицы суммируются как среднее значение в каждой записи матрицы. Имеет ли это смысл? Другими словами, я моделирую сценарий для комбинаций i и j k раз и хочу получить среднее значение этих k симуляций для каждой комбинации i и j. Текущий вывод с foreach дает мне выходной размер 30x3, и я даже не уверен, как именно они упорядочены. - person MIH; 27.07.2016
comment
спасибо, отлично работает на этом примере. однако, когда я заменяю эту функцию своей более сложной функцией (полностью работающей, все библиотеки установлены, работает с предыдущими циклами, которые у меня были и т. д.), каким-то образом с %dopar% я получаю сообщение об ошибке: Error in { : task 1 failed - "could not find function "SpatialPoints"" . Когда я делаю %do%, проблем нет, и инструкция выполняется. Вы когда-нибудь сталкивались с такой проблемой? - person MIH; 28.07.2016
comment
вам нужно добавить параметр foreach (..blabla.., .packages = c("sp", "rgdal") или любой другой пакет, который вы хотите использовать во время цикла foreach. Таким образом, пакеты загружаются в отдельные процессы. - person loki; 28.07.2016