2

I am trying to use pivot_wider inside a function. The values are not uniquely defined, so I need to specify the values_fn argument of pivot_wider, which should return a list. The column for values_fn is the argument of the function.

Here is a minimal example:

library(rlang)
library(tidyverse)

t <- tibble::tibble(a = c(rep("A", 2), rep("B", 3), "C"), 
                    b = seq(1:6))

# This works without a function
t %>% tidyr::pivot_wider(names_from = a, 
                         values_from = b, 
                         values_fn =  list(b = list))

# Now I want to do the same with a function. But the := is not valid in the values_fn.

pivot_func <- function(dat, name_col, value_col){
  dat %>% 
    tidyr::pivot_wider(names_from = {{name_col}}, 
                       values_from = {{value_col}}, 
                       values_fn = list({{value_col}} := list))
}

pivot_func(t, a, b)

Error: ":=" can only be used within a quasiquoted argument Run "rlang::last_error()" to see where the error occurred.

IrisOren
  • 69
  • 1
  • 1
  • 6

1 Answers1

2

The error tells you that you are using := inside a non-quasiquoted argument. This is because you are using it inside the function list. Also you are using {{values_from}}, but your function does not have a parameter called values_from.

Instead, you can create the function list you need before the pipe and pass it to values_fn:

pivot_func <- function(dat, name_col, value_col){
  fns <- setNames(list(list), deparse(substitute(value_col)))
  dat %>% 
    tidyr::pivot_wider(names_from = {{name_col}}, 
                       values_from = {{value_col}}, 
                       values_fn = fns)
}

pivot_func(t, a, b)
#> # A tibble: 1 x 3
#>   A         B         C        
#>   <list>    <list>    <list>   
#> 1 <int [2]> <int [3]> <int [1]>

Or, if you want to stick to rlang syntax, you could do something like:

pivot_func <- function(dat, name_col, value_col){
  
  dat %>% 
    tidyr::pivot_wider(names_from = {{name_col}}, 
                       values_from = {{value_col}}, 
                       values_fn = setNames(list(list), 
                                            expr_deparse(enexpr(value_col))))
}

Which does the same thing.

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87