Сохранение нескольких выходов foreach dopar loop
Я хотел бы знать, можно ли/возвращать несколько выходов как часть цикла 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.
Предположим теперь, что я хотел бы вернуть или сохранить результаты двух разных операций отдельно, например. 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
.
Есть ли способ возврата или сохранения обоих выходов в двух отдельных списках?
Ответы
Ответ 1
Не пытайтесь использовать побочные эффекты с 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
для первого вызова функции объединения.
Ответ 2
Я предпочитаю использовать класс для хранения нескольких результатов для цикла% 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
Ответ 3
В этом примере игрушек показано, как вернуть несколько результатов из цикла% 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)