Сохранение нескольких выходов цикла foreach dopar

Я хотел бы знать, если / как можно будет возвращать несколько выходных данных как часть foreach dopar петля.

Давайте возьмем очень упрощенный пример. Давайте предположим, что я хотел бы сделать 2 операции как часть foreach цикл, и хотел бы вернуть или сохранить результаты обеих операций для каждого значения i,

Для возврата только одного вывода это будет так просто:

library(foreach)
library(doParallel)
cl <- makeCluster(3)
registerDoParallel(cl)

oper1 <- foreach(i=1:100000) %dopar% {
    i+2
}

oper1 будет список с 100000 элементов, каждый элемент является результатом операции i+2 для каждого значения я.

Предположим теперь, что я хотел бы вернуть или сохранить результаты двух разных операций отдельно, например i+2 а также i+3, Я попробовал следующее:

oper1 = list()
oper2 <- foreach(i=1:100000) %dopar% {
    oper1[[i]] = i+2
    return(i+3)
}

надеясь, что результаты i+2 будут сохранены в списке oper1и вот результаты второй операции i+3 будет возвращен foreach, Однако в списке ничего не заполняется oper1! В этом случае только результат i+3 возвращается из цикла.

Есть ли способ вернуть или сохранить оба выхода в двух отдельных списках?

3 ответа

Решение

Не пытайтесь использовать побочные эффекты с foreach или любым другим параллельным программным пакетом. Вместо этого верните все значения из тела цикла foreach в списке. Если вы хотите, чтобы ваш конечный результат был списком из двух списков, а не списком из 100 000 списков, укажите функцию объединения, которая транспонирует результаты:

comb <- function(x, ...) {
  lapply(seq_along(x),
    function(i) c(x[[i]], lapply(list(...), function(y) y[[i]])))
}

oper <- foreach(i=1:10, .combine='comb', .multicombine=TRUE,
                .init=list(list(), list())) %dopar% {
  list(i+2, i+3)
}

oper1 <- oper[[1]]
oper2 <- oper[[2]]

Обратите внимание, что эта функция объединения требует использования .init аргумент, чтобы установить значение x для первого вызова функции объединения.

Я предпочитаю использовать класс для хранения нескольких результатов для цикла%dopar%.

Этот пример раскручивает 3 ядра, вычисляет несколько результатов на каждом ядре, а затем возвращает список результатов в вызывающий поток.

Протестировано под RStudio, Windows 10, а также R v3.3.2,

library(foreach)
library(doParallel)

# Create class which holds multiple results for each loop iteration.
# Each loop iteration populates two properties: $result1 and $result2.
# For a great tutorial on S3 classes, see: 
# http://www.cyclismo.org/tutorial/R/s3Classes.html#creating-an-s3-class
multiResultClass <- function(result1=NULL,result2=NULL)
{
  me <- list(
    result1 = result1,
    result2 = result2
  )

  ## Set the name for the class
  class(me) <- append(class(me),"multiResultClass")
  return(me)
}

cl <- makeCluster(3)
registerDoParallel(cl)
oper <- foreach(i=1:10) %dopar% {
   result <- multiResultClass()
   result$result1 <- i+1
   result$result2 <- i+2
   return(result)
}
stopCluster(cl)

oper1 <- oper[[1]]$result1
oper2 <- oper[[1]]$result2

В этом игрушечном примере показано, как вернуть несколько результатов из цикла% dopar%.

Этот пример:

  • Раскручивает 3 ядра.
  • Визуализирует график для каждого ядра.
  • Возвращает график и прикрепленное сообщение.
  • Распечатывает графики и прикрепляет сообщение.

Я нашел это действительно полезным для ускорения использования Rmarkdown для печати 1800 графиков в PDF-документе.

Протестировано под Windows 10, RStudio, а также R v3.3.2,

Код R:

# Demo of returning multiple results from a %dopar% loop.
library(foreach)
library(doParallel)
library(ggplot2)

cl <- makeCluster(3)
registerDoParallel(cl)

# Create class which holds multiple results for each loop iteration.
# Each loop iteration populates two properties: $resultPlot and $resultMessage.
# For a great tutorial on S3 classes, see: 
# http://www.cyclismo.org/tutorial/R/s3Classes.html#creating-an-s3-class
plotAndMessage <- function(resultPlot=NULL,resultMessage="?")
{
  me <- list(
    resultPlot = resultPlot,
    resultMessage = resultMessage
  )

  # Set the name for the class
  class(me) <- append(class(me),"plotAndMessage")
  return(me)
}

oper <- foreach(i=1:5, .packages=c("ggplot2")) %dopar% {

  x <- c(i:(i+2))
  y <- c(i:(i+2))
  df <- data.frame(x,y)
  p <- ggplot(df, aes(x,y))
  p <- p + geom_point()

  message <- paste("Hello, world! i=",i,"\n",sep="")

  result <- plotAndMessage()
  result$resultPlot <- p
  result$resultMessage <- message
  return(result)
}

# Print resultant plots and messages. Despite running on multiple cores,
# 'foreach' guarantees that the plots arrive back in the original order.
foreach(i=1:5) %do% {
  # Print message attached to plot.
  cat(oper[[i]]$resultMessage)
  # Print plot.
  print(oper[[i]]$resultPlot)
}

stopCluster(cl)
Другие вопросы по тегам