web-dev-qa-db-fra.com

Comment créez-vous une barre de progression lorsque vous utilisez la fonction "foreach ()" dans R?

il y a quelques messages informatifs sur la création d'un compteur de boucles dans un programme R. Cependant, comment créez-vous une fonction similaire lorsque vous utilisez la version parallélisée avec "foreach ()"?

35
exl

Edit: Après un update vers le paquet doSNOW, il est devenu très simple d’afficher une barre de progression de Nice lorsqu’on utilise %dopar% et cela fonctionne sous Linux, Windows et OS X

doSNOW supporte maintenant officiellement les barres de progression via l'argument .options.snow.

library(doSNOW)
cl <- makeCluster(2)
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(max = iterations, style = 3)
progress <- function(n) setTxtProgressBar(pb, n)
opts <- list(progress = progress)
result <- foreach(i = 1:iterations, .combine = rbind, 
                  .options.snow = opts) %dopar%
{
    s <- summary(rnorm(1e6))[3]
    return(s)
}
close(pb)
stopCluster(cl) 

Un autre moyen de suivre les progrès, si vous gardez à l'esprit le nombre total d'itérations, consiste à définir .verbose = T, car cela affichera sur la console les itérations terminées.

Solution précédente pour Linux et OS X

Sous Ubuntu 14.04 (64 bits) et OS X (El Capitan), la barre de progression est affichée même si vous utilisez %dopar% si la fonction makeClusteroufile = "" est définie. Cela ne semble pas fonctionner sous Windows. De l'aide sur makeCluster:

outfile: Où diriger les sorties stdout et stderr de la sortie des travailleurs. "" n'indique aucune redirection (elle ne peut être utile que pour les travailleurs de la machine locale). La valeur par défaut est '/ dev/null' ('nul:' sous Windows).

Exemple de code:

library(foreach)
library(doSNOW)
cl <- makeCluster(4, outfile="") # number of cores. Notice 'outfile'
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(min = 1, max = iterations, style = 3)
result <- foreach(i = 1:iterations, .combine = rbind) %dopar% 
{
      s <- summary(rnorm(1e6))[3]
      setTxtProgressBar(pb, i) 
      return(s)
}
close(pb)
stopCluster(cl) 

Ceci est ce à quoi la barre de progression ressemble. Cela semble un peu étrange, car une nouvelle barre est imprimée pour chaque progression de la barre et parce qu'un travailleur risque de perdre un peu de temps, ce qui entraîne un basculement occasionnel de la barre de progression. 

32
thie1e

Ce code est une version modifiée de l’exemple doRedis , et créera une barre de progression même lors de l’utilisation de %dopar% avec un serveur parallèle:

#Load Libraries
library(foreach)
library(utils)
library(iterators)
library(doParallel)
library(snow)

#Choose number of iterations
n <- 1000

#Progress combine function
f <- function(){
  pb <- txtProgressBar(min=1, max=n-1,style=3)
  count <- 0
  function(...) {
    count <<- count + length(list(...)) - 1
    setTxtProgressBar(pb,count)
    Sys.sleep(0.01)
    flush.console()
    c(...)
  }
}

#Start a cluster
cl <- makeCluster(4, type='SOCK')
registerDoParallel(cl)

# Run the loop in parallel
k <- foreach(i = icount(n), .final=sum, .combine=f()) %dopar% {
  log2(i)
}

head(k)

#Stop the cluster
stopCluster(cl)

Vous devez connaître le nombre d'itérations et la fonction de combinaison à l'avance.

8
Zach

Ceci est maintenant possible avec le package parallel. Testé avec R 3.2.3 sur OSX 10.11, s’exécutant dans RStudio, à l’aide d’un cluster de type "PSOCK"-.

library(doParallel)

# default cluster type on my machine is "PSOCK", YMMV with other types
cl <- parallel::makeCluster(4, outfile = "")
registerDoParallel(cl)

n <- 10000
pb <- txtProgressBar(0, n, style = 2)

invisible(foreach(i = icount(n)) %dopar% {
    setTxtProgressBar(pb, i)
})

stopCluster(cl)

Étrangement, il ne s'affiche correctement qu'avec style = 3.

8
shadowtalker

Vous enregistrez l'heure de début avec Sys.time() avant la boucle. Boucle sur des lignes ou des colonnes ou quelque chose dont vous connaissez le total. Ensuite, à l’intérieur de la boucle, vous pouvez calculer le temps écoulé jusqu’à présent (voir difftime), le pourcentage d’achèvement, la vitesse et le temps estimé qui reste. Chaque processus peut imprimer ces lignes de progression avec la fonction message. Vous obtiendrez une sortie quelque chose comme

1/1000 complete @ 1 items/s, ETA: 00:00:45
2/1000 complete @ 1 items/s, ETA: 00:00:44

De toute évidence, l'ordre de bouclage affectera grandement l'efficacité de ce processus. Je ne connais pas foreach, mais avec multicore's mclapply, vous obtiendrez de bons résultats en utilisant mc.preschedule=FALSE, ce qui signifie que les éléments sont affectés aux processus un par un dans l'ordre d'achèvement des éléments précédents.

6
otsaw

Ce code implémente une barre de progression effectuant le suivi d'une boucle foreach parallélisée à l'aide du backend doMC et de l'excellent progress package dans R. Il suppose que tous les cœurs, spécifiés par numCores, effectuent une quantité de travail à peu près égale.

library(foreach)
library(doMC)
library(progress)

iterations <- 100
numCores <- 8

registerDoMC(cores=numCores)

pbTracker <- function(pb,i,numCores) {
    if (i %% numCores == 0) {
        pb$tick()
    }
}

pb <- progress_bar$new(
  format <- " progress [:bar] :percent eta: :eta",
  total <- iterations / numCores, clear = FALSE, width= 60)


output = foreach(i=1:iterations) %dopar% {
    pbTracker(pb,i,numCores)
    Sys.sleep(1/20)
}
0
marital_weeping

Vous pouvez également obtenir que cela fonctionne avec le paquetage progress

what it looks like

# loading parallel and doSNOW package and creating cluster ----------------
library(parallel)
library(doSNOW)

numCores<-detectCores()
cl <- makeCluster(numCores)
registerDoSNOW(cl)

# progress bar ------------------------------------------------------------
library(progress)

iterations <- 100                               # used for the foreach loop  

pb <- progress_bar$new(
  format = "letter = :letter [:bar] :elapsed | eta: :eta",
  total = iterations,    # 100 
  width = 60)

progress_letter <- rep(LETTERS[1:10], 10)  # token reported in progress bar

# allowing progress bar to be used in foreach -----------------------------
progress <- function(n){
  pb$tick(tokens = list(letter = progress_letter[n]))
} 

opts <- list(progress = progress)

# foreach loop ------------------------------------------------------------
library(foreach)

foreach(i = 1:iterations, .combine = rbind, .options.snow = opts) %dopar% {
  summary(rnorm(1e6))[3]
}

stopCluster(cl) 
0
Dewey Brooke