Reverse of tidyr::unnest_wider() or tidyr::unnest_longer()?

Is there a way to reverse tidyr::unnest_wider() or tidyr::unnest_longer() (e.g. some type of nest_narrower() type function) when the unnesting column is NOT a data frame?

Here is a small example:

library(tidyr)
library(tibble)
library(dplyr, warn.conflicts = FALSE)

df1 <- tibble::tibble(x = 1:3,
                      y = list(list(a = 10, b = 20, c = 30),
                               list(a = 40, b = 50, c = 60),
                               list(a = 70, b = 70, c = 90)))
df1
#> # A tibble: 3 x 2
#>       x y               
#>   <int> <list>          
#> 1     1 <named list [3]>
#> 2     2 <named list [3]>
#> 3     3 <named list [3]>

Note that column "y" is a list of lists, not a list of data frames


df2 <- df1 %>% tidyr::unnest_wider(y)
df2
#> # A tibble: 3 x 4
#>       x     a     b     c
#>   <int> <dbl> <dbl> <dbl>
#> 1     1    10    20    30
#> 2     2    40    50    60
#> 3     3    70    70    90

When I unnest it, I can see the column names clearly.



df2 %>% tidyr::nest(y = c(a, b, c))
#> # A tibble: 3 x 2
#>       x              y
#>   <int> <list<df[,3]>>
#> 1     1        [1 × 3]
#> 2     2        [1 × 3]
#> 3     3        [1 × 3]

However, when I nest it again, the recreated y is a list of data frames, not list of lists.

Is there a way to change this behavior? I can do it through a mix of purrr::pmap() and dplyr::mutate(), but I'm wondering if there is a more direct way to do it.


df2 %>% dplyr::mutate(l0 = purrr::pmap(list(a = a, b = b, c = c), list))
#> # A tibble: 3 x 5
#>       x     a     b     c l0              
#>   <int> <dbl> <dbl> <dbl> <list>          
#> 1     1    10    20    30 <named list [3]>
#> 2     2    40    50    60 <named list [3]>
#> 3     3    70    70    90 <named list [3]>

Thank you,

Created on 2019-10-29 by the reprex package (v0.3.0)

Can you explain your end goal? A data frame is just a list (with the additional condition that each element is a vector with the same length), so it generally shouldn't matter.

My original use case was that I was doing some hyperparameter tuning for xgboost.

I used tidyr::crossing() to create a data frame of hyperparameters, and then I wanted to use

df1 %>%
  dplyr::mutate(xgb_model = purrr::map(.x = parameters, 
                                       .f = ~xgboost::xgboost(params = .x, 
                                                              data = data, 
                                                              ...)))

to create a model for each combination of hyper parameters...but xgboost assumes that the input to params is a list.

I had assumed (perhaps naively) that xgboost wouldn't accept it as a data frame, and needed a pure "list" -- but maybe that was an incorrect assumption :blush: