保存 foreach dopar 循环的多个输出
Posted
技术标签:
【中文标题】保存 foreach dopar 循环的多个输出【英文标题】:Saving multiple outputs of foreach dopar loop 【发布时间】:2013-11-16 11:33:34 【问题描述】:我想知道是否/如何将多个输出作为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
的结果会从循环中返回。
有没有办法在两个单独的列表中返回或保存两个输出?
【问题讨论】:
为什么不简单地return(c(i+2,i+3))
?如果您确实需要将它们放在单独的列表中,您可以在 foreach
返回后执行此操作。
这是一个非常简单的例子。在我的实际(真实)示例中,这两个操作的结果具有不同的结构(矩阵和向量)或(列表和标量)。那就不行了..
在您的问题中提及这会很有用...在这种情况下,请使用return(list(i+2,i+3))
。
【参考方案1】:
这个玩具示例展示了如何从 %dopar% 循环返回多个结果。
这个例子:
启动 3 个内核。 在每个核心上呈现图表。 返回图表和附加消息。 打印图表并将其附加消息。我发现这对于加快使用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)
【讨论】:
这个解决方案对我来说非常有效。我确实将最后一个foreach
循环更改为常规的 for
循环,因为它在我的 R Notebook 上表现不佳。【参考方案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】:不要尝试对 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]]
请注意,此 combine 函数需要使用 .init
参数来设置 x
的值,以便首次调用 combine 函数。
【讨论】:
在这个上大加 1。我花了很长时间才弄清楚.init=list()
的部分。没有它就不能很好地工作!
答案并没有解释它在 data.frames、矩阵或向量场景中的一般功能以上是关于保存 foreach dopar 循环的多个输出的主要内容,如果未能解决你的问题,请参考以下文章
R在HPC MPIcluster上运行foreach dopar循环
为啥将 %dopar% 与 foreach 一起使用导致 R 无法识别包?