Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get column names and dataframe name from a list of dataframes into a single dataframe

Tags:

r

dplyr

purrr

I am looking for a way to make column names and dataframe names from a list of dataframes into a single dataframe. They have unequal length of columns. What's the best way to do this?

dlist <- list(mtcars[1:2], mtcars[1:3], mtcars[1:4])
names(dlist) <- c("mtcars1", "mtcars2", "mtcars3")

Tried:
dlist |> map(~colnames(.x))

Expected output:

1 mtcars1 mpg   cyl   NA    NA   
2 mtcars2 mpg   cyl   disp  NA   
3 mtcars3 mpg   cyl   disp  hp  
like image 438
codedancer Avatar asked Jul 02 '21 11:07

codedancer


4 Answers

You can try:

library(tidyverse)

dlist %>%
  map_df(~names(.x) %>%
           enframe(), .id = "id") %>%
  pivot_wider(names_from = name, id_cols = id)

# A tibble: 3 x 5
  id      `1`   `2`   `3`   `4`  
  <chr>   <chr> <chr> <chr> <chr>
1 mtcars1 mpg   cyl   NA    NA   
2 mtcars2 mpg   cyl   disp  NA   
3 mtcars3 mpg   cyl   disp  hp  

Or same idea in base:

reshape(stack(lapply(dlist, names)), idvar = "ind", timevar = "values", direction = "wide", v.names = "values")

      ind values.mpg values.cyl values.disp values.hp
1 mtcars1        mpg        cyl        <NA>      <NA>
3 mtcars2        mpg        cyl        disp      <NA>
6 mtcars3        mpg        cyl        disp        hp
like image 60
Ritchie Sacramento Avatar answered Sep 27 '22 18:09

Ritchie Sacramento


Similar to the accepted solution but more compact thanks to unnest_wider:

library(tidyr)
library(tibble)
library(purrr)

dlist %>% 
  map(colnames) %>% 
  enframe %>% 
  unnest_wider(value, names_sep = "_")

#> # A tibble: 3 x 5
#>   name    value_1 value_2 value_3 value_4
#>   <chr>   <chr>   <chr>   <chr>   <chr>  
#> 1 mtcars1 mpg     cyl     NA      NA     
#> 2 mtcars2 mpg     cyl     disp    NA     
#> 3 mtcars3 mpg     cyl     disp    hp   
like image 28
Edo Avatar answered Sep 27 '22 19:09

Edo


Maybe there is a better way but this is how I would do it:

library(purrr)

dlist %>%
  map(~ .x %>% 
        names() %>% 
        append(rep(NA_character_, dlist %>%
                     map(~ .x %>% names()) %>%
                     reduce(~ max(length(..1), length(..2))) - length(.x)))) %>%
  exec(rbind, !!!.) %>%
  as.data.frame()

         V1  V2   V3   V4
mtcars1 mpg cyl <NA> <NA>
mtcars2 mpg cyl disp <NA>
mtcars3 mpg cyl disp   hp

Or a bit more concise with map2, similar to may dear friend's elegant solution:

dlist %>% 
  map2_dfr(names(dlist), ~ c(.y, names(.x)) %>% set_names(~ letters[seq_along(.x)])) %>%
  column_to_rownames("a")

          b   c    d    e
mtcars1 mpg cyl <NA> <NA>
mtcars2 mpg cyl disp <NA>
mtcars3 mpg cyl disp   hp
like image 22
Anoushiravan R Avatar answered Sep 27 '22 17:09

Anoushiravan R


A base R solution is:

dlist |> lapply(names) |>
  (\(x){
    res <- t(sapply(x, `length<-`, max(lengths(x))))
    cbind(id = names(x), setNames(data.frame(res), 1:NCOL(res)))
  })()
#R>              id   1   2    3    4
#R> mtcars1 mtcars1 mpg cyl <NA> <NA>
#R> mtcars2 mtcars2 mpg cyl disp <NA>
#R> mtcars3 mtcars3 mpg cyl disp   hp

Since the row names and id are the same, then this might do:

dlist |> lapply(names) |>
  (\(x) sapply(x, `length<-`, max(lengths(x))))() |> 
  t() |> as.data.frame()
#R>          V1  V2   V3   V4
#R> mtcars1 mpg cyl <NA> <NA>
#R> mtcars2 mpg cyl disp <NA>
#R> mtcars3 mpg cyl disp   hp
like image 44
Benjamin Christoffersen Avatar answered Sep 27 '22 17:09

Benjamin Christoffersen