2015-04-03 18 views
7

Supponiamo di avere un elenco nidificato:R: Sostituire valori in lista annidata

test <- list(
    list(a = 1, b = 2, c = NULL), 
    list(a = NULL, b = 2, c = 3)) 

Come faccio a sostituire tutti i valori NULL con, diciamo, NA per preservare la struttura dei dati? In modo che non finisca per perdere valori/struttura quando tento di rendere i dati fuori dalla lista. Come ad esempio:

data.frame(matrix(unlist(test), nrow = 2, byrow = T)) 

    X1 X2 
1 1 2 
2 2 3 

output desiderato è qualcosa di simile:

X1 X2 X3 
1 1 2 NA 
2 NA 2 3 

ci sono suggerimenti da fare in questo modo:

rbind.fill(lapply(test, function(f) { 
    as.data.frame(Filter(Negate(is.null), f)) 
})) 

Il che non è proprio come vettorializzare come vorrei . Ovviamente le dimensioni e le prestazioni sono un problema. Una soluzione che ricorda è la sostituzione di tutti i valori NULL allo stesso modo in cui può essere fatto per l'intero frame di dati in una sola volta. E poi unlist() e matrix() l'elenco.

Non sono sicuro del guadagno nella prestazione (se ce n'è affatto). Forse il buon vecchio lapply() non è poi così male.

+0

Per sostituire i valori NULL nell'intero elenco, dobbiamo accedere agli elementi all'interno. Usando 'data.table 1.9.5' puoi usare' rbindlist (lapply (....), fill = TRUE) 'nel tuo codice aggiornato. – akrun

+0

.. che richiede lapply e praticamente termina con le stesse prestazioni della tua risposta originale? –

+0

Non ho controllato i benchmark, ma penso che rbindlist sarebbe più veloce. – akrun

risposta

4

Possiamo usare stri_list2matrix

library(stringi) 
m1 <- matrix(as.numeric(t(sapply(test, stri_list2matrix))), ncol=3) 
m1 
# [,1] [,2] [,3] 
#[1,] 1 2 NA 
#[2,] NA 2 3 

Questo potrebbe essere convertito in data.frame

as.data.frame(m1) 
+0

Solo curioso: esiste un'opzione per sostituire i valori, invece di applicare funzioni di tipo loop? Ci sono anche altre opzioni come 'rbind.all' nel pacchetto' data.table' ... Raggiunge il risultato ma il percorso non è esattamente quello che mi aspettavo. –

+0

Dopo alcuni test su casi diversi, questo sembra essere l'approccio più universale. –

+0

@ A.Val. Hai provato alcuni benchmark usando questo metodo e il 'rbindlist' – akrun

1

Se si sa che è solo una lista a due livelli è possibile scorrere ogni elemento utilizzando il test due loop per is.null .

test <- list(
    list(a = 1, b = 2, c = NULL), 
    list(a = NULL, b = 2, c = 3) 
) 

# get structure fyi 
str(test) 
# tells you the highest level list length 
length(test) 

for(i in 1:length(test)) 
    for(j in 1:length(test[[i]])) # second level 
     if(is.null(test[[i]][[j]])) test[[i]][[j]]<-NA 

MODIFICA: quindi, naturalmente, fai quello che stavi facendo prima.

+0

Loop è una risposta ovvia che voglio evitare a tutti i costi, probabilmente dovrei ampliare la mia domanda un po 'per chiarire. –

2

Seguendo risposte trovate here, ecco un modo (bit contorto) per andare in base R:

data.frame(matrix(as.numeric(as.character(unlist(test, recursive=F))), nrow=length(test), byrow=T)) 

# X1 X2 X3 
#1 1 2 NA 
#2 NA 2 3 
+0

buon modo per andare pure! –

+0

@ColonelBeauvel, grazie, anche se l'unico credito che ho qui è di aver trovato la giusta pagina SO ;-) – Cath

0

Un approccio diverso tramite dput e dget e appunti.

test <- list(
    list(a = 1, b = 2, c = NULL), 
    list(a = NULL, b = 2, c = 3)) 

dput(test,file="clipboard-1024") 
tmp=dget(textConnection(gsub("NULL","NA",readChar(con="clipboard-1024",file.info("clipboard-1024")$size)))) 
data.frame(matrix(unlist(tmp), nrow = 2, byrow = T)) 

# X1 X2 X3 
#1 1 2 NA 
#2 NA 2 3 

Non ho idea di quanto sia veloce o lento.

Problemi correlati