2017-07-17 2 views
6

J'essaie de cbind ou unnest ou as.data.table une liste partiellement imbriquée.cbind avec liste partiellement imbriquée

id <- c(1,2) 
A <- c("A1","A2","A3") 
B <- c("B1") 
AB <- list(A=A,B=B) 
ABAB <- list(AB,AB) 
nested_list <- list(id=id,ABAB=ABAB) 

Le length d'ID est identique à ABAB (2 dans ce cas). Je ne sais pas comment unlist une partie de cette liste (ABAB) et cbind une autre partie (id). Voici mon résultat désiré comme data.table:

data.table(id=c(1,1,1,2,2,2),A=c("A1","A2","A3","A1","A2","A3"),B=rep("B1",6)) 
    id A B 
1: 1 A1 B1 
2: 1 A2 B1 
3: 1 A3 B1 
4: 2 A1 B1 
5: 2 A2 B1 
6: 2 A3 B1 
+0

Je suppose Vous voulez une solution qui résout ce cas de manière générale ... – CPak

+0

@ChiPak Je l'ai simplifié avec l'exemple de données avec ABAB contenant deux fois la même liste (AB, AB). Mon exemple de vie réelle, contient différentes listes ('ABAB <- list (AB, CD)') –

Répondre

5

Je ne l'ai pas testé pour les cas plus généraux, mais cela fonctionne pour l'exemple OP:

library(data.table) 

as.data.table(nested_list)[, lapply(ABAB, as.data.table)[[1]], id] 
# id A B 
#1: 1 A1 B1 
#2: 1 A2 B1 
#3: 1 A3 B1 
#4: 2 A1 B1 
#5: 2 A2 B1 
#6: 2 A3 B1 

ou une autre option (ce qui est probablement plus rapide, mais est plus verbeux):

rbindlist(lapply(nested_list$ABAB, as.data.table), 
      idcol = 'id')[, id := nested_list$id[id]] 
5

C'est une R de base super laid, mais produit la sortie désirée.

Reduce(rbind, Map(function(x, y) setNames(data.frame(x, y), c("id", "A", "B")), 
        as.list(nested_list[[1]]), 
        lapply(unlist(nested_list[-1], recursive=FALSE), 
         function(x) Reduce(cbind, x)))) 
    id A B 
1 1 A1 B1 
2 1 A2 B1 
3 1 A3 B1 
4 2 A1 B1 
5 2 A2 B1 
6 2 A3 B1 

lapply prend la liste des deux éléments (chacun contenant A et variables B) et extrait avec de l'unlistrecursive=FALSE. Il renvoie une liste de matrices de caractères avec la variable B renseignée par recyclage. Une liste des variables d'identification individuelles de as.list(nested_list[[1]]) et la matrice de matrices sont alimentées à Map qui convertit les paires correspondantes en un nom de données et donne aux colonnes les noms désirés et renvoie une liste de noms de données. Enfin, cette liste de data.frames est alimentée à Reduce, ce qui rbind s les résultats à un seul data.frame. Le Reduce(rbind, final pourrait être remplacé par data.table s rbindlist si désiré.

3

Voici une autre solution hideuse

max_length = max(unlist(lapply(nested_list, function(x) lapply(x, lengths)))) 
data.frame(id = do.call(c, lapply(nested_list$id, rep, max_length)), 
      do.call(rbind, lapply(nested_list$ABAB, function(x) 
       do.call(cbind, lapply(x, function(y) { 
        if(length(y) < max_length) { 
         rep(y, max_length) 
        } else { 
         y 
        } 
       }))))) 
# id A B 
#1 1 A1 B1 
#2 1 A2 B1 
#3 1 A3 B1 
#4 2 A1 B1 
#5 2 A2 B1 
#6 2 A3 B1 
2

Et un de plus, aussi inelegant- mais allé trop loin `d le temps que je voyais les autres réponses.

restructure <- function(nested_l) { 
    ids <- as.numeric(max(unlist(lapply(unlist(nested_l, recursive = FALSE), function(x){ 
    lapply(x, length) 
    })))) 

    temp = data.frame(rep(nested_l$id, each = ids), 
      sapply(1:length(nested_l$id), function(x){ 
       out <-unlist(lapply(nested_l[[2]], function(y){ 
       return(y[x]) 
       })) 
      })) 
    names(temp) <- c("id", unique(substring(unlist(nested_l[2]), first = 1, last = 1))) 
    return(temp) 
} 

> restructure(nested_list) 
    id A B 
1 1 A1 B1 
2 1 A2 B1 
3 1 A3 B1 
4 2 A1 B1 
5 2 A2 B1 
6 2 A3 B1 
1

Rejoindre la partie:

library(tidyverse) 
temp <- map(nested_list,~map(.x,~expand.grid(.x))) 
df <- map_df(1:2,~cbind(temp$id[[.x]],temp$ABAB[[.x]])) 

    Var1 A B 
1 1 A1 B1 
2 1 A2 B1 
3 1 A3 B1 
4 2 A1 B1 
5 2 A2 B1 
6 2 A3 B1