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

Time:05-20

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 

CodePudding user response:

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  

CodePudding user response:

Another possible solution, based on purrr::map2:

library(tidyverse)

map2(list1, list2, bind_rows)

#> $X
#> # A tibble: 8 x 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 x 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 x 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
  • Related