web-dev-qa-db-ja.com

foreach doparループの複数の出力を保存する

foreach doparループの一部として複数の出力を返すことが可能かどうか/どのように知りたいのですか。

非常に単純な例を見てみましょう。 foreachループの一部として2つの操作を行い、iの各値に対して両方の操作の結果を返すか保存したいとします。

返す出力が1つだけの場合、次のように簡単になります。

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

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

oper1は、100,000個の要素を持つリストになります。各要素は、iの各値に対するi+2操作の結果です。

ここで、2つの異なる操作の結果を個別に返したり保存したりするとします。 i+2およびi+3。私は次を試しました:

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

i+2の結果がリストoper1に保存され、2番目の操作i+3の結果がforeachによって返されることを期待しています。ただし、リストoper1には何も入力されません。この場合、i+3の結果のみがループから返されます。

両方の出力を2つの個別のリストに返す、または保存する方法はありますか?

24
Mayou

Foreachまたはその他の並列プログラムパッケージで副作用を使用しようとしないでください。代わりに、foreachループの本体からすべての値をリストで返します。最終結果を100,000個のリストではなく2つのリストのリストにする場合は、結果を転置する結合関数を指定します。

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の値を設定する必要があることに注意してください。

35
Steve Weston

クラスを使用して、%dopar%ループの複数の結果を保持することを好みます。

この例では、3つのコアをスピンアップし、各コアで複数の結果を計算し、結果のリストを呼び出し元スレッドに返します。

RStudioWindows 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
6
Contango

このおもちゃの例は、%dopar%ループから複数の結果を返す方法を示しています。

この例:

  • 3つのコアをスピンアップします。
  • 各コアでグラフをレンダリングします。
  • グラフと添付メッセージを返します。
  • グラフを印刷し、添付されたメッセージを出力します。

Rmarkdown を使用して1,800個のグラフをPDFドキュメントに印刷するために使用して高速化するのに非常に便利です。

Windows 10RStudio、および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)
1
Contango