Search code examples
rfunctiondplyrtidyeval

dplyr arrange() works with single variable inside c(), but not multiple variables inside of c() when evaluated inside of a function


Everything I can find online about tidyeval is either older and not up to date with latest version of tidyverse\dplyr or else doesn't quite apply.

An example tibble is:

df <- tribble(
       ~var1, ~var2, ~var3,
         1,     2,     3,
         4,     5,     6,
         7,     8,     9
        )

I have a small function that I've written:

fun <- function(data, select_var, arrange_var) {
   select_var <- enquo(select_var)
   arrange_var <- enquo(arrange_var)

   data %>%
     select(!!select_var) %>%
     arrange(!!arrange_var)
   }

The function simply selects column(s) and then arranges by row(s).

When I pass the arguments to the function it works fine with a single variable inside of c():

fun(df, 
    c(var1,
      var2)),
    c(var2))

However, when I try to pass it two variables like this:

    fun(df, 
    c(var1,
      var2)),
    c(var1,
      var2))

I get the following error:

Error: incorrect size (282) at position 1, expecting : 141

The closest stack responses I've been able to find are: arrange() doesn't recognize column name parameter and Pass a vector of variable names to arrange() in dplyr

but both of these seem to give answers that included deprecated solutions (e.g., arrange_())

Some great information here: tidyeval resource roundup by Mara Averick

and Separating and Trimming Messy Data the Tidy Way by Paul Oldham

and of course I've dug into: tidyeval

However none of them seem to address this quirk. I've exhausted my resources after spending an afternoon. The code works find in a standard R file, just can't get it to work inside of a function, but about ready to give up, so thought I would see if you wonderful folks could help. Thanks in advance.


Solution

  • Update 2022/03/17

    The tidyverse has evolved and so should this answer.

    There is no need for enquo anymore! Instead we enclose tidy-select expressions in double braces {{ }}.

    library("tidyverse")
    
    df <- tribble(
      ~var1, ~var2, ~var3,
      1, 2, 3,
      4, 5, 6,
      7, 8, 9
    )
    
    fun <- function(data, select_vars, ...) {
      data %>%
        select(
          {{ select_vars }}
        ) %>%
        arrange(
          ...
        )
    }
    
    
    fun(df, c(var1, var2), desc(var2))
    #> # A tibble: 3 × 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     7     8
    #> 2     4     5
    #> 3     1     2
    fun(df, c(var1, var2), var1, var2)
    #> # A tibble: 3 × 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     1     2
    #> 2     4     5
    #> 3     7     8
    

    We still can't use c() with the arrange and filter verbs because that's not allowed with data-masking.

    df %>%
      arrange(
        c(var1, var2)
      )
    #> Error in `arrange()`:
    #> ! Problem with the implicit `transmute()` step.
    #> x Problem while computing `..1 = c(var1, var2)`.
    #> x `..1` must be size 3 or 1, not 6.
    

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

    Old answer

    Replacing arrange_var with ... and specifying the variables without enclosing them in c() makes it work.

    library("dplyr")
    
    df <- tribble(
      ~var1, ~var2, ~var3,
      1, 2, 3,
      4, 5, 6,
      7, 8, 9
    )
    
    fun <- function(data, select_var, ...) {
      select_var <- enquo(select_var)
      data %>%
        select(!!select_var) %>%
        # You can pass the dots to `arrange` directly
        arrange(...)
    }
    
    fun(df, c(var1, var2), var2)
    #> # A tibble: 3 x 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     1     2
    #> 2     4     5
    #> 3     7     8
    fun(df, c(var1, var2), var1, var2)
    #> # A tibble: 3 x 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     1     2
    #> 2     4     5
    #> 3     7     8
    

    Created on 2019-03-08 by the reprex package (v0.2.1)

    It turns out that only select supports strings and character vectors. As the documentation says, "This is unlike other verbs where strings would be ambiguous." See the last example for dplyr::select.

    # Two alternatives; both work with `select`.
    df %>%
      select(var1, var2)
    #> # A tibble: 3 x 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     1     2
    #> 2     4     5
    #> 3     7     8
    df %>%
      select(c(var1, var2))
    #> # A tibble: 3 x 2
    #>    var1  var2
    #>   <dbl> <dbl>
    #> 1     1     2
    #> 2     4     5
    #> 3     7     8
    
    # `arrange` only works with lists on comma separated unquoted variable names.
    df %>%
      arrange(var1, var2)
    #> # A tibble: 3 x 3
    #>    var1  var2  var3
    #>   <dbl> <dbl> <dbl>
    #> 1     1     2     3
    #> 2     4     5     6
    #> 3     7     8     9
    df %>%
      arrange(c(var, var2))
    #> Error: incorrect size (4) at position 1, expecting : 3
    

    Created on 2019-03-08 by the reprex package (v0.2.1)