2015-07-02 7 views
13

Ho il seguente data.frame:disimballaggio e fondendo gli elenchi in una colonna in data.frame

id  name altNames 
1001 Joan character(0)  
1002 Jane c("Janie", "Janet", "Jan") 
1003 John Jon 
1004 Bill Will 
1005 Tom character(0)  

La colonna altNames potrebbe essere vuota (cioè caratteri (0)), hanno un solo nome, o un elenco di nomi. Quello che voglio è una data.frame (o una lista) in cui appare ogni voce da name e/o altNames solo una volta insieme con il corrispondente id, in questo modo:

id  name 
1001 Joan 
1002 Jane 
1002 Janie 
1002 Janet 
1002 Jan 
1003 John 
1003 Jon 
1004 Bill 
1004 Will 
1005 Tom 

Qual è il modo più efficiente di farlo? Ancora meglio è dplyr viene utilizzato. Grazie

Edit: Ecco i dati:

df <- data_frame(
    id = c("1001", "1002","1003", "1004", "1005"), 
    name = c("Joan", "Jane", "John", "Bill", "Tom"), 
    altNames = list(character(0), c("Janie", "Janet", "Jan"), "Jon", "Will", character(0)) 
) 
+2

Questo è il lavoro di 'tidyr :: unnest', ma è attualmente rotto per questo esempio: https://github.com/hadley/tidyr/issues/91 – hadley

+0

Ottengo errori utilizzando il tuo dput, quindi l'ho cambiato per l'esempio di @ hadley. ecco un altro modo in base r tra l'altro 'do.call ('rbind', lapply (1: nrow (df), function (x) data.frame (id = df [x, 1], name = unlist (df [x, -1]), row.names = NULL))) ' – rawr

risposta

14

Ecco un possibile approccio data.table

library(data.table) 
setDT(dat)[, .(name = c(name, unlist(altNames))), by = id] 
#  id name 
# 1: 1001 Joan 
# 2: 1002 Jane 
# 3: 1002 Janie 
# 4: 1002 Janet 
# 5: 1002 Jan 
# 6: 1003 John 
# 7: 1003 Jon 
# 8: 1004 Bill 
# 9: 1004 Will 
# 10: 1005 Tom 
+0

Non sono sicuro di come l'OP abbia avuto un' data.frame' valido per assomigliare a lui. Doveva essere un 'data.table' ad un certo punto (o un ritorno da una funzione davvero hokey). – hrbrmstr

+0

@hrbrmstr era l'output di 'jsonlite' – Armin

+0

ugh. è pessimo come avere 'NULL's in' data.frame's. dovresti decisamente spostare l'output 'jsonlite' in' data.table' molto presto dopo averlo letto se i tuoi dati sorgente genereranno questo molto. – hrbrmstr

10

Una versione base R (utilizzando il df aggiunto da @rawr)

with(df, { 
    ns <- mapply(c, name, altNames) 
    data.frame(id = rep(id, times=lengths(ns)), name=unlist(ns), row.names=NULL) 
}) 
#  id name 
#1 1001 Joan 
#2 1002 Jane 
#3 1002 Janie 
#4 1002 Janet 
#5 1002 Jan 
#6 1003 John 
#7 1003 Jon 
#8 1004 Bill 
#9 1004 Will 
#10 1005 Tom 
+0

Non funziona per me. Mi dà l'id sbagliato e duplica. Strano. – Armin

+0

@Amin non lo so, 'lengths 'è per R 3.2+, ma ciò significherebbe solo lanciare un errore non dare risultati errati. – jenesaisquoi

4

utilizzando tidyr, a poppa er la pulizia dei dati con data.table:

In primo luogo, fissare i dati:

library(data.table) 
dat<-setDT(dat) 
dat$altNames[sapply(dat$altNames, length) == 0] <- NA 

Ora unnest da tidyr e alcuni dplyr:

library(dplyr) 
library(tidyr) 
dat %>% unnest(altNames) %>% 
     group_by(id) %>% 
     do(unique(c(.[["name"]],.[["altNames"]]))) 

    id V1 
1 1001 Joan 
2 1001 NA 
3 1002 Jane 
4 1002 Janie 
5 1002 Janet 
6 1002 Jan 
7 1003 John 
8 1003 Jon 
9 1004 Bill 
10 1004 Will 
11 1005 Tom 
12 1005 NA 

ha le AN, ma sono facilmente rimosso con %>% na.omit.

Penso che data.table sia il vincitore su questo.

5

Ecco un dplyr + tidyr soluzione completa, il modo in cui mi piacerebbe affrontarlo:

library(dplyr) 
library(tidyr) 

df <- data_frame(
    id = c("1001", "1002","1003", "1004", "1005"), 
    name = c("Joan", "Jane", "John", "Bill", "Tom"), 
    altNames = list(character(0), c("Janie", "Janet", "Jan"), "Jon", "Will", character(0)) 
) 

# Need some way to concatenate a list of vectors with a vectors 
# in a "rowwise" way 
vector_c <- function(...) { 
    Map(c, ...) 
} 

df %>% 
    mutate(
    names = vector_c(name, altNames), 
    altNames = NULL, 
    name = NULL 
) %>% 
    unnest(names) 
#> Source: local data frame [10 x 2] 
#> 
#>  id names 
#> 1 1001 Joan 
#> 2 1002 Jane 
#> 3 1002 Janie 
#> 4 1002 Janet 
#> 5 1002 Jan 
#> 6 1003 John 
#> 7 1003 Jon 
#> 8 1004 Bill 
#> 9 1004 Will 
#> 10 1005 Tom 

La maggior parte del duro lavoro è fatto da tidyr::unnest(): è stato progettato per richiedere frame di dati con un elenco-colonna e unnest esso, ripetendo le altre colonne secondo necessità.