Extraiga colnames de una lista anidada de data.frames

10

Tengo una lista anidada de data.frames, ¿cuál es la forma más fácil de obtener los nombres de columna de todos data.frames?

Ejemplo:

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

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

Resultado:

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

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

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

Respuestas:

7

Ya hay un par de respuestas. Pero déjame dejar otro enfoque. Utilicé rapply2()en el paquete 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
fuente
5

Aquí hay una solución base R.

Puede definir una función personalizada para aplanar su lista anidada (que puede tratar una lista anidada de cualquier profundidad , por ejemplo, más de 2 niveles), es decir,

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)
}

y luego use el siguiente código para lograr los colnames

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

tal que

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

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

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

Ejemplo con una lista anidada más profunda

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

y obtendrás

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

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

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

Aquí hay un intento de hacer esto lo más vectorizado posible,

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"

Ahora podemos dividir i2todo antes del punto, lo que dará,

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"

Para que se limpien completamente, necesitamos recorrer y aplicar una expresión regular simple,

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

lo que da,

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

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

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

El código compactado

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
fuente
3

Prueba esto

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)

El quid aquí es que en data.framesrealidad hay una lista especial, por lo que es importante qué probar.

Pequeña explicación: lo que debe hacerse aquí es una recursión, ya que con cada elemento puede mirar un marco de datos, por lo que desea decidir si aplica nameso profundiza en la recursión y foovuelve a llamar .

Georgery
fuente
El problema es que foo (l, nombres) también devuelve una lista anidada
user680111
Yo no. No estoy seguro, lo que hiciste de manera diferente.
Georgery
Puede agregar unlist()al final, pero no estoy seguro de si esto es lo que desea.
Georgery
2

Primero cree l1, una lista anidada con solo los colnames

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)
})

Entonces desliste l1

unlist(l1, recursive=F)
Solarion
fuente
2

Aquí hay una manera de usar purrrfunciones map_depthyvec_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
fuente