Search code examples
rtidyversetidyrtibble

`tidyr::unnest`: "cant't recycle input" error **ONLY** when using `cols = dplyr::everything()`


Consider the following:

library(magrittr)
sapply(c("dplyr", "tibble", "tidyr"), requireNamespace)

mytbl <- structure(
  list(
    A = list(c("A.1", "A.2")),
    B = list(c("B.1", "B.2")),
    C = list("C.1"),
    D = list(c("D.1", "D.2")),
    E = list(c("E.1", "E.2", "E.3"))),
  class = c("tbl_df", "tbl", "data.frame"),
  row.names = c(NA, -1L))

While tidyr::unnest works for every individual column like so:

lapply(names(mytbl), function(x) tidyr::unnest(mytbl, cols = x))

unnesting fails when trying to operate on all columns using tidyselect:

mytbl %>% tidyr::unnest(cols = dplyr::everything())
Error in `tidyr::unnest()`:
! In row 1, can't recycle input of size 2 to size 3.
Run `rlang::last_trace()` to see where the error occurred.

Any pointers? What is going on here and ho do I prevent this?


Solution

  • The problem arises from the fact that your columns have different lengths (e.g., A is 2, but C is 1, and E is 3). A data.frame object, which is what returns unnest, cannot have columns of different lengths.

    I'm not aware of an easy solution, but one possibility is to pad NAs to every vector to match the maximum length. This requires pivoting first:

    library(dplyr)
    library(purrr)
    library(tidyr)
    
    mytbl %>% 
      pivot_longer(everything()) %>% 
      mutate(value = map(value, `length<-`, max(lengths(value)))) %>% 
      pivot_wider(names_from = name, values_from = value) %>% 
      unnest(everything())
    

    output

    # A tibble: 3 × 5
      A     B     C     D     E    
      <chr> <chr> <chr> <chr> <chr>
    1 A.1   B.1   C.1   D.1   E.1  
    2 A.2   B.2   NA    D.2   E.2  
    3 NA    NA    NA    NA    E.3 
    

    In base R, you can do:

    l <- unlist(mytbl, recursive = FALSE)
    as.data.frame(lapply(l, `length<-`, max(lengths(l))))