converting lists to tibbles (curly brackets + map())

Hi everyone (and thank you in advance for you time :)). I am working with purrr
and have a question about converting a list to a tibble. Below is an example list:

library(purrr)
library(tibble)

my_list <- list(
    list(1, 
         x = 1.5, 
         y = c(2), 
         z = "A"),
    list(2, 
         x = 4.2, 
         y = c(5, 6), 
         z = "B"),
    list(3, 
         x = 8.8, 
         y = c(9, 10, 11))
)

When I pipe my_list to tibble() directly with purrr::map(), I get the following output:

my_list %>% 
    tibble(x = map(., "x"),
           y = map(., "y"),
           z = map(., "z"))
#> # A tibble: 3 × 4
#>   .                x         y         z        
#>   <list>           <list>    <list>    <list>   
#> 1 <named list [4]> <dbl [1]> <dbl [1]> <chr [1]>
#> 2 <named list [4]> <dbl [1]> <dbl [2]> <chr [1]>
#> 3 <named list [3]> <dbl [1]> <dbl [3]> <NULL>

This . is a column with 'named list' contents, but I am not sure why the . is included in the construction of the tibble? I know I can remove this behavior by adding some curly brackets:

my_list %>% { # <- magic? 
    tibble(x = map(., "x"),
           y = map(., "y"),
           z = map(., "z"))}
#> # A tibble: 3 × 3
#>   x         y         z        
#>   <list>    <list>    <list>   
#> 1 <dbl [1]> <dbl [1]> <chr [1]>
#> 2 <dbl [1]> <dbl [2]> <chr [1]>
#> 3 <dbl [1]> <dbl [3]> <NULL>

So, I know how to get the behavior I want, but I am not sure why I get this behavior. Any help or explanation would be greatly appreciated!

Thank you!

Created on 2022-01-03 by the reprex package (v2.0.1)

Hi there!

The difference is subtle. It helps understanding what %>% actually does. It takes whats left of it as a first argument to whats right of it. This argument is named .. In your first example, you pass my_list as . to the function tibble() as its first argument. so what's actually happening is that you call

tibble(my_list, 
       x = map(my_list, "x"),
       y = map(my_list, "y"),
       z = map(my_list, "z"))

If you look closely at your result, you see, that you have a so-called 'nested' tibble with the first column being your original list of list and the second list of the other list elements.

In the second case, what you pass my_list to is actually not a function but a closure, again . is the passed on my_list. What happens is:

 tibble(x = map(my_list, "x"),
        y = map(my_list, "y"),
        z = map(my_list, "z"))

In both cases, however, the classes of the columns are, still lists! See in the output after the column names. So you'll still need to fix that by running tidyr::unnest() afterwards, or by fixing the calls to map() to also extract the values properly.

Hope this helps!
Best,
Valentin

2 Likes

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.