31

我想知道是否/如何将多个输出作为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+2i 的每个值的运算结果。

假设现在我想分别返回或保存两个不同操作的结果,例如i+2i+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从循环返回的结果。

有没有办法在两个单独的列表中返回或保存两个输出?

4

3 回答 3

44

不要尝试对 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组合函数的第一次调用的值。

于 2013-11-05T23:24:26.797 回答
10

我更喜欢使用一个类来保存 %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
于 2017-05-21T20:59:47.327 回答
3

这个玩具示例展示了如何从 %dopar% 循环返回多个结果。

这个例子:

  • 旋转 3 个核心。
  • 在每个核心上呈现图表。
  • 返回图形和附加消息。
  • 打印图表并将其附加消息。

我发现这对于加速使用Rmarkdown将 1,800 个图形打印到 PDF 文档中非常有用。

Windows 10在、RStudio和下测试R v3.3.2

代码:

# 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)
于 2017-05-21T20:52:49.563 回答