Extraire les noms de colonnes d'une liste imbriquée de data.frames

10

J'ai une liste imbriquée de data.frames, quelle est la façon la plus simple d'obtenir les noms de colonne de tous les data.frames?

Exemple:

d = data.frame(a = 1:3, b = 1:3, c = 1:3)

l = list(a = d, list(b = d, c = d))

Résultat:

$a
[1] "a" "b" "c"

$b
[1] "a" "b" "c"

$c
[1] "a" "b" "c"
user680111
la source

Réponses:

7

Il y a déjà quelques réponses. Mais permettez-moi de laisser une autre approche. J'ai utilisé rapply2()dans le package rawr.

devtools::install_github('raredd/rawr')
library(rawr)
library(purrr)

rapply2(l = l, FUN = colnames) %>% 
flatten

$a
[1] "a" "b" "c"

$b
[1] "a" "b" "c"

$c
[1] "a" "b" "c"
jazzurro
la source
5

Voici une solution de base R.

Vous pouvez définir une fonction personnalisée pour aplatir votre liste imbriquée (qui peut traiter une liste imbriquée de n'importe quelle profondeur , par exemple, plus de 2 niveaux), c'est-à-dire,

flatten <- function(x){  
  islist <- sapply(x, class) %in% "list"
  r <- c(x[!islist], unlist(x[islist],recursive = F))
  if(!sum(islist))return(r)
  flatten(r)
}

puis utilisez le code suivant pour obtenir les noms de colonnes

out <- Map(colnames,flatten(l))

tel que

> out
$a
[1] "a" "b" "c"

$b
[1] "a" "b" "c"

$c
[1] "a" "b" "c"

Exemple avec une liste imbriquée plus profonde

l <- list(a = d, list(b = d, list(c = list(e = list(f= list(g = d))))))
> l
$a
  a b c
1 1 1 1
2 2 2 2
3 3 3 3

[[2]]
[[2]]$b
  a b c
1 1 1 1
2 2 2 2
3 3 3 3

[[2]][[2]]
[[2]][[2]]$c
[[2]][[2]]$c$e
[[2]][[2]]$c$e$f
[[2]][[2]]$c$e$f$g
  a b c
1 1 1 1
2 2 2 2
3 3 3 3

et vous obtiendrez

> out
$a
[1] "a" "b" "c"

$b
[1] "a" "b" "c"

$c.e.f.g
[1] "a" "b" "c"
ThomasIsCoding
la source
4

Voici une tentative de le faire aussi vectorisé que possible,

i1 <- names(unlist(l, TRUE, TRUE))
#[1] "a.a1" "a.a2" "a.a3" "a.b1" "a.b2" "a.b3" "a.c1" "a.c2" "a.c3" "b.a1" "b.a2" "b.a3" "b.b1" "b.b2" "b.b3" "b.c1" "b.c2" "b.c3" "c.a1" "c.a2" "c.a3" "c.b1" "c.b2" "c.b3" "c.c1" "c.c2" "c.c3"
i2 <- names(split(i1, gsub('\\d+', '', i1)))
#[1] "a.a" "a.b" "a.c" "b.a" "b.b" "b.c" "c.a" "c.b" "c.c"

Nous pouvons maintenant nous séparer i2de tout avant le point, ce qui donnera,

split(i2, sub('\\..*', '', i2))

#    $a
#    [1] "a.a" "a.b" "a.c"

#    $b
#    [1] "b.a" "b.b" "b.c"

#    $c
#    [1] "c.a" "c.b" "c.c"

Pour les nettoyer complètement, nous devons boucler et appliquer une simple expression régulière,

 lapply(split(i2, sub('\\..*', '', i2)), function(i)sub('.*\\.', '', i))

qui donne,

$a
[1] "a" "b" "c"

$b
[1] "a" "b" "c"

$c
[1] "a" "b" "c"

Le Code compacté

i1 <- names(unlist(l, TRUE, TRUE))
i2 <- names(split(i1, gsub('\\d+', '', i1)))
final_res <- lapply(split(i2, sub('\\..*', '', i2)), function(i)sub('.*\\.', '', i))
Sotos
la source
3

Essaye ça

d = data.frame(a = 1:3, b = 1:3, c = 1:3)

l = list(a = d, list(b = d, c = d))

foo <- function(x, f){
    if (is.data.frame(x)) return(f(x))
    lapply(x, foo, f = f)
}

foo(l, names)

Le point crucial ici est qu'il s'agit en data.framesfait d'une liste spéciale, il est donc important de savoir quoi tester.

Petite explication: ce qui doit être fait ici est une récursivité, car avec chaque élément, vous pouvez regarder soit une trame de données, donc vous voulez décider si vous appliquez le namesou approfondissez la récursivité et appelez à foonouveau.

Georgery
la source
Le problème est que foo (l, names) renvoie également une liste imbriquée
user680111
Je ne. Je ne sais pas, ce que vous avez fait différemment.
Georgery
Vous pouvez ajouter unlist()à la fin, mais je ne sais pas si c'est ce que vous voulez.
Georgery
2

Créez d'abord l1, une liste imbriquée avec uniquement les noms de colonnes

l1 <- lapply(l, function(x) if(is.data.frame(x)){
  list(colnames(x)) #necessary to list it for the unlist() step afterwards
}else{
  lapply(x, colnames)
})

Puis annulez l1

unlist(l1, recursive=F)
Solarion
la source
2

Voici une façon d'utiliser les purrrfonctions map_depthetvec_depth

library(purrr)

return_names <- function(x) {
   if(inherits(x, "list"))
     return(map_depth(x, vec_depth(x) - 2, names))
    else return(names(x))
}

map(l, return_names)

#$a
#[1] "a" "b" "c"

#[[2]]
#[[2]]$b
#[1] "a" "b" "c"

#[[2]]$c
#[1] "a" "b" "c"
Ronak Shah
la source