我想知道是否/如何在foreach dopar
循环中返回多个输出。
让我们举一个非常简单的例子。假设我想在foreach
循环中执行2个操作,并希望为i
的每个值返回或保存两个操作的结果。
只返回一个输出,它将如下所示:
library(foreach)
library(doParallel)
cl <- makeCluster(3)
registerDoParallel(cl)
oper1 <- foreach(i=1:100000) %dopar% {
i+2
}
oper1
将是一个包含100000个元素的列表,每个元素都是i的每个值的操作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
的结果从循环中返回。
有没有办法在两个单独的列表中返回或保存两个输出?
答案 0 :(得分:31)
不要尝试使用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
参数来设置第一次调用combine函数时x
的值。
答案 1 :(得分:5)
我更喜欢使用一个类来保存%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
答案 2 :(得分:1)
此玩具示例显示如何从%dopar%循环返回多个结果。
这个例子:
我发现这对于加快使用Rmarkdown将1,800个图形打印到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)