Search code examples
rdplyrtidyverse

bind_rows of multiple tibbles in 2 named lists using imap: how to subset properly


This post seems relevant/similar but not quite the same issue: `dplyr::bind_rows` not working while combining listed tibbles

I'm trying to bind the rows of multiple tibbles in 2 named lists. I've been using imap to iterate across list elements because it retains the names (map doesn't it seems). However, bind_rows doesn't seem to work here for some reason. It creates weird column names. It only seems to work with an unnamed list, but then I lose the names.

library(tidyverse)

# named lists
list1 <- list(
  tibble(a = 1:4, b = LETTERS[1:4]),
  tibble(a = 5:8, b = LETTERS[5:8]),
  tibble(a = 9:12, b = LETTERS[9:12])
) %>% set_names(c("X","Y","Z"))

list2 <- list(
  tibble(a = 3:6, b = LETTERS[3:6], c = 3:6 / 2),
  tibble(a = 7:10, b = LETTERS[7:10], c = 7:10 / 2),
  tibble(a = 11:14, b = LETTERS[11:14], c = 11:14 / 2)
) %>% set_names(c("X","Y","Z"))

# subsetting a named lists with bind_rows creates weird column names
list1 %>% imap(~ {
  .x %>% bind_rows(list2[.y])
})

#> $X
#> # A tibble: 8 × 3
#>       a b       X$a $b       $c
#>   <int> <chr> <int> <chr> <dbl>
#> 1     1 A        NA NA     NA  
#> 2     2 B        NA NA     NA  
#> 3     3 C        NA NA     NA  
#> 4     4 D        NA NA     NA  
#> 5    NA NA        3 C       1.5
#> 6    NA NA        4 D       2  
#> 7    NA NA        5 E       2.5
#> 8    NA NA        6 F       3  
#> 
#> $Y
#> # A tibble: 8 × 3
#>       a b       Y$a $b       $c
#>   <int> <chr> <int> <chr> <dbl>
#> 1     5 E        NA NA     NA  
#> 2     6 F        NA NA     NA  
#> 3     7 G        NA NA     NA  
#> 4     8 H        NA NA     NA  
#> 5    NA NA        7 G       3.5
#> 6    NA NA        8 H       4  
#> 7    NA NA        9 I       4.5
#> 8    NA NA       10 J       5  
#> 
#> $Z
#> # A tibble: 8 × 3
#>       a b       Z$a $b       $c
#>   <int> <chr> <int> <chr> <dbl>
#> 1     9 I        NA NA     NA  
#> 2    10 J        NA NA     NA  
#> 3    11 K        NA NA     NA  
#> 4    12 L        NA NA     NA  
#> 5    NA NA       11 K       5.5
#> 6    NA NA       12 L       6  
#> 7    NA NA       13 M       6.5
#> 8    NA NA       14 N       7  

# subsetting unnamed lists works but loses names
list1 -> list1 %>% unname()
list2 -> list2 %>% unname()

list1 %>% imap(~ {
  .x %>% bind_rows(list2[.y])
})

#> [[1]]
#> # A tibble: 8 × 3
#>       a b         c
#>   <int> <chr> <dbl>
#> 1     1 A      NA  
#> 2     2 B      NA  
#> 3     3 C      NA  
#> 4     4 D      NA  
#> 5     3 C       1.5
#> 6     4 D       2  
#> 7     5 E       2.5
#> 8     6 F       3  
#> 
#> [[2]]
#> # A tibble: 8 × 3
#>       a b         c
#>   <int> <chr> <dbl>
#> 1     5 E      NA  
#> 2     6 F      NA  
#> 3     7 G      NA  
#> 4     8 H      NA  
#> 5     7 G       3.5
#> 6     8 H       4  
#> 7     9 I       4.5
#> 8    10 J       5  
#> 
#> [[3]]
#> # A tibble: 8 × 3
#>       a b         c
#>   <int> <chr> <dbl>
#> 1     9 I      NA  
#> 2    10 J      NA  
#> 3    11 K      NA  
#> 4    12 L      NA  
#> 5    11 K       5.5
#> 6    12 L       6  
#> 7    13 M       6.5
#> 8    14 N       7 

Solution

  • You should use double brackets [[ to extract the element of a list. What you subset by a single bracket [ from a list is still a list. You could check the difference between list2["X"] and list2[["X"]].

    list1 %>% imap(~ {
      .x %>% bind_rows(list2[[.y]])
    })
    
    Output
    $X
    # A tibble: 8 × 3
          a b         c
      <int> <chr> <dbl>
    1     1 A      NA  
    2     2 B      NA  
    3     3 C      NA  
    4     4 D      NA  
    5     3 C       1.5
    6     4 D       2  
    7     5 E       2.5
    8     6 F       3  
    
    $Y
    # A tibble: 8 × 3
          a b         c
      <int> <chr> <dbl>
    1     5 E      NA  
    2     6 F      NA  
    3     7 G      NA  
    4     8 H      NA  
    5     7 G       3.5
    6     8 H       4  
    7     9 I       4.5
    8    10 J       5  
    
    $Z
    # A tibble: 8 × 3
          a b         c
      <int> <chr> <dbl>
    1     9 I      NA  
    2    10 J      NA  
    3    11 K      NA  
    4    12 L      NA  
    5    11 K       5.5
    6    12 L       6  
    7    13 M       6.5
    8    14 N       7