rtidyverserlangquosure

Mimicking a secondary tidy dots argument in an R function


I'm looking to create a function that accepts a list of (data frame) variables as one of its parameters. I've managed to get it working partially, but when I get to the group_by/count, things fall apart. How can I do this??

## Works
f1 <- function(dfr, ..., split = NULL) {
  dots <- rlang::enquos(...)
  split <- rlang::enquos(split)
  dfr %>%
    select(!!!dots, !!!split) %>%
    gather('type', 'score', -c(!!!split))
}

## does not work
f2 <- function(dfr, ..., split = NULL) {
  dots <- rlang::enquos(...)
  split <- rlang::enquos(split)
  dfr %>%
    select(!!!dots, !!!split) %>%
    gather('type', 'score', -c(!!!split))
    count(!!!split, type, score)
  }

I would want to do things like

mtcars %>% f2(drat:qsec)
mtcars %>% f2(drat:qsec, split = gear)
mtcars %>% f2(drat:qsec, split = c(gear, carb)) ## ??

These calls with f1() all work, but for f2 none of the commands work. They all end up with a Error in !split : invalid argument type. That f2(drat:qsec) doesn't (immediately) work without the split argument, I'm not too surprised about, but how to get the second and third comment working?


Solution

  • The issue with the second function (the missing pipe notwithstanding) is that count() (or rather group_by() which is called by count()) doesn't support tidyselect syntax so you can't pass it a list to be spliced like you can with select(), gather() etc. Instead, one option is to use group_by_at() and add_tally(). Here's a slightly modified version of the function:

    library(dplyr)
    
    f2 <- function(dfr, ..., split = NULL) {
      dfr %>%
        select(..., {{split}}) %>%
        gather('type', 'score', -{{split}}) %>%
        group_by_at(vars({{split}}, type, score)) %>% # could use `group_by_all()`
        add_tally()
    }
    
    mtcars %>% f2(drat:qsec)
    
    # A tibble: 96 x 3
    # Groups:   type, score [81]
       type  score     n
       <chr> <dbl> <int>
     1 drat   3.9      2
     2 drat   3.9      2
     3 drat   3.85     1
     4 drat   3.08     2
     5 drat   3.15     2
     6 drat   2.76     2
     7 drat   3.21     1
     8 drat   3.69     1
     9 drat   3.92     3
    10 drat   3.92     3
    # ... with 86 more rows
    
    mtcars %>% f2(drat:qsec, split = c(gear, carb))
    
    # A tibble: 96 x 5
    # Groups:   gear, carb, type, score [89]
        gear  carb type  score     n
       <dbl> <dbl> <chr> <dbl> <int>
     1     4     4 drat   3.9      2
     2     4     4 drat   3.9      2
     3     4     1 drat   3.85     1
     4     3     1 drat   3.08     1
     5     3     2 drat   3.15     2
     6     3     1 drat   2.76     1
     7     3     4 drat   3.21     1
     8     4     2 drat   3.69     1
     9     4     2 drat   3.92     1
    10     4     4 drat   3.92     2
    # ... with 86 more rows