web-dev-qa-db-fra.com

Enregistrement de plusieurs sorties de chaque boucle dopar

Je voudrais savoir si/comment il serait possible de renvoyer plusieurs sorties dans le cadre de foreach dopar boucle.

Prenons un exemple très simpliste. Supposons que je souhaite effectuer 2 opérations dans le cadre de la boucle foreach et que je souhaite retourner ou enregistrer les résultats des deux opérations pour chaque valeur de i.

Pour qu'une seule sortie soit renvoyée, ce serait aussi simple que:

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

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

oper1 serait une liste de 100 000 éléments, chaque élément est le résultat de l'opération i+2 pour chaque valeur de i.

Supposons maintenant que je souhaite retourner ou enregistrer séparément les résultats de deux opérations différentes, par exemple i+2 et i+3. J'ai essayé ce qui suit:

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

en espérant que les résultats de i+2 sera enregistré dans la liste oper1, et que les résultats de la deuxième opération i+3 sera retourné par foreach. Cependant, rien n'est renseigné dans la liste oper1! Dans ce cas, seul le résultat de i+3 est renvoyé de la boucle.

Existe-t-il un moyen de retourner ou d'enregistrer les deux sorties dans deux listes distinctes?

26
Mayou

N'essayez pas d'utiliser des effets secondaires avec foreach ou tout autre package de programme parallèle. À la place, retournez toutes les valeurs du corps de la boucle foreach dans une liste. Si vous souhaitez que votre résultat final soit une liste de deux listes plutôt qu'une liste de 100 000 listes, spécifiez une fonction de combinaison qui transpose les résultats:

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]]

Notez que cette fonction combine nécessite l'utilisation de .init argument pour définir la valeur de x pour le premier appel de la fonction combine.

35
Steve Weston

Je préfère utiliser une classe pour contenir plusieurs résultats pour une boucle% dopar%.

Cet exemple fait tourner 3 cœurs, calcule plusieurs résultats sur chaque cœur, puis renvoie la liste des résultats au thread appelant.

Testé sous RStudio, Windows 10, et 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

Cet exemple de jouet montre comment renvoyer plusieurs résultats d'une boucle% dopar%.

Cet exemple:

  • Fait tourner 3 cœurs.
  • Rend un graphique sur chaque cœur.
  • Renvoie le graphique et un message joint.
  • Imprime les graphiques et leur message est joint.

J'ai trouvé cela très utile pour accélérer en utilisant Rmarkdown pour imprimer 1800 graphiques dans un document PDF.

Testé sous Windows 10, RStudio et R v3.3.2.

Code 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