2015-10-12 11 views
6

Sto cercando di elaborare più voci di elenco in parallelo."foreach" restituisce ciclo parallelo <NA> s

Il mio obiettivo è: eseguire alcune funzioni di etichettatura su ogni colonna, in base ai suoi valori. Quindi restituire dataframe con il nome del nodo, il nome della colonna e l'etichetta elaborata

Il flusso di lavoro funziona correttamente utilizzando un ciclo normale. Tuttavia, quando provo a fare la stessa cosa in un ciclo foreach, i risultati restituiti sono (Nota: il seguente è solo un'astrazione del set di dati originale)

Non sono sicuro di cosa si sia incasinato esattamente in tra .. Se puoi aiutarmi a risolvere quella cosa sarebbe fantastico :-)

set.seed(12345) 
options(stringsAsFactors = F) 


# I. Random data generation (Original data is in data frame format) 
random.data = list() 
random.data[["one"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3)) 
random.data[["two"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3)) 
random.data[["three"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3)) 



# II. Some function applied to each column to label/classify the values 
valslabel = function(DataCOlumn) { 
    if(mean(DataCOlumn) < 0.5) return("low") 
    return("high") 
} 



# III. Generating the desired output in a regular for loop : 

desiredOutput = list() 

for(frame.i in seq_along(random.data)) { 

    frame = random.data[[frame.i]] 
    frame.name = names(random.data)[frame.i] 
    frame.results = data.frame(frame.name = character(0), 
        mappedField = character(0), label = character(0)) 

    for(col.i in 1:ncol(frame)) { 
    frame.results[col.i, "frame.name"] = frame.name 
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i] 
    frame.results[col.i, "label"] = valslabel(frame[,col.i]) 
    } 

    desiredOutput[[frame.name]] = frame.results 
} 


print(desiredOutput) 

# $one 
# frame.name mappedField label 
# 1  one   V1 high 
# 2  one   V2 high 
# 3  one   V3 low 
# 
# $two 
# frame.name mappedField label 
# 1  two   V1 low 
# 2  two   V2 high 
# 3  two   V3 low 
# 
# $three 
# frame.name mappedField label 
# 1  three   V1 low 
# 2  three   V2 high 
# 3  three   V3 high 




# IV. Using the "foreach" parallel execution 

library(foreach) 
library(doParallel) 

cl = makeCluster(6) 
registerDoParallel(cl) 

output = foreach(frame.i = seq_along(random.data), .verbose = T) %dopar% { 

    frame = random.data[[frame.i]] 
    frame.name = names(random.data)[frame.i] 
    frame.results = data.frame(frame.name = character(0), mappedField = character(0), label = character(0)) 

    for(col.i in 1:ncol(frame)) { 
    frame.results[col.i, "frame.name"] = frame.name 
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i] 
    frame.results[col.i, "label"] = valslabel(frame[,col.i]) 
    } 

    return(frame.results) 
} 


print(output) 

# [[1]] 
# frame.name mappedField label 
# 1  <NA>  <NA> <NA> 
# 2  <NA>  <NA> <NA> 
# 3  <NA>  <NA> <NA> 
# 
# [[2]] 
# frame.name mappedField label 
# 1  <NA>  <NA> <NA> 
# 2  <NA>  <NA> <NA> 
# 3  <NA>  <NA> <NA> 
# 
# [[3]] 
# frame.name mappedField label 
# 1  <NA>  <NA> <NA> 
# 2  <NA>  <NA> <NA> 
# 3  <NA>  <NA> <NA> 

Grazie!

risposta

3

Il problema è legato al modo in cui si inizializza la cornice di dati, e il fatto che all'interno dell'ambiente foreach, l'opzione stringsAsFactors non è impostata su FALSE. Quello che sta accadendo in ogni foreach ciclo è qualcosa di simile

options(stringsAsFactors = FALSE) 
d <- data.frame(x =character(0)) 
d[1, "x"] <- "a" 
#Warning message: 
#In `[<-.factor`(`*tmp*`, iseq, value = "a") : 
# invalid factor level, NA generated 
d 
#  x 
#1 <NA> 

Si noti che questo dà solo un avvertimento, e non un errore in modo che il ciclo non si ferma. Se si imposta stringsAsFactors a FALSE prima non c'è nessun problema (come avete fatto quando non è in esecuzione roba in parallelo)

options(stringsAsFactors = FALSE) 
d <- data.frame(x =character(0)) 
d[1, "x"] <- "a" 
d 
# x 
#1 a 

Nel vostro ambiente globale è già stato configurato in modo da options(stringsAsFactors = FALSE) il ciclo %do% funzionato. Tuttavia, questa opzione non viene trasferita nell'ambiente locale di ciascun processo parallelo e pertanto il ciclo %dopar% viene eseguito nel problema sopra riportato.

Guardate ad esempio all'uscita delle seguenti

options(stringsAsFactors = FALSE) 
.Options$stringsAsFactors 
#[1] FALSE 
foreach(i = 1:3) %dopar% .Options$stringsAsFactors 
#[[1]] 
#[1] TRUE 
# 
#[[2]] 
#[1] TRUE 
# 
#[[3]] 
#[1] TRUE 

Quindi la soluzione è quella di impostare l'opzione stringsAsFactors = FALSE all'interno del ciclo foreach.

Per inciso, è molto meglio creare il frame di dati utilizzando l'intero vettore di colonne anziché riga per fila, quando possibile. Nel tuo esempio è possibile sostituire

frame.results = data.frame(frame.name = character(0), mappedField = character(0), label = character(0)) 
for(col.i in 1:ncol(frame)) { 
    frame.results[col.i, "frame.name"] = frame.name 
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i] 
    frame.results[col.i, "label"] = valslabel(frame[,col.i]) 
} 

con

frame.results <- data.frame( 
    frame.name = frame.name, 
    mappedField = colnames(frame), 
    label = valslabel1(colMeans(frame))) 

dove la funzione valslabel è stata sostituita da una versione Vectorised

valslabel1 <- function(x) { 
    ifelse(x < 0.5, "low", "high") 
} 
+0

Grazie Konvas. Molto ben preso !! –

+0

Interessante ... Devo tenerlo a mente! – cryo111

+2

@Deena @ cryo111 A proposito, ho notato che se si registra il back-end parallelo usando l'argomento 'core', ovvero' registerDoParallel (core = 6) 'l'opzione _è_ passata all'ambiente locale di ogni lavoro ... Non sicuro perché questo è e se è specifico per piattaforma – konvas