Search code examples
rtidyversetidyeval

How do you specify of possible arguments states in functions using non-standard evaluation?


I am learning about programming with tidy evaluation and non-standard evaluation and have been trying to work out how to constrain the possible states of an argument in a function.

For instance given a data set:

set.seed(123)
data <- data_frame(GROUP_ONE = sample(LETTERS[1:3], 10, replace = TRUE), 
                   GROUP_TWO = sample(letters[4:6], 10, replace = TRUE), 
                   result = rnorm(10))

I can create a function which has an argument I use to group the data using a quosure like so:

my_function <- function(data, group = GROUP_ONE){

  require(dplyr)
  require(magrittr)

  group <- enquo(group)

  result <- data %>% 
    group_by(!!group) %>% 
    summarise(mean=mean(result))

  return(result)
}

and this does what I want

my_function(data)

# A tibble: 3 x 2
  GROUP_ONE       mean
      <chr>      <dbl>
1         A  1.5054975
2         B  0.2817966
3         C -0.5129904

and I can supply a different group:

my_function(data, group = GROUP_TWO)

# A tibble: 3 x 2
  GROUP_TWO       mean
      <chr>      <dbl>
1         d -0.3308130
2         e  0.2352483
3         f  0.7347437

However, I cannot group by a column for which is not present in the data.

e.g.

 my_function(data, group = GROUP_THREE)

Error in grouped_df_impl(data, unname(vars), drop) : Column GROUP_THREE is unknown

I would like to add a step at the beginning of the function so that the function stops with a custom error message if the group argument is not GROUP_ONE or GROUP_TWO

something like:

if(!group %in% c(~GROUP_ONE, ~GROUP_TWO)) stop("CUSTOM ERROR MESSAGE")

except this does not work as you apparently you can't put quosures in a vector. It should be possible to convert the quosure to a string somehow and have a vector of strings but I can't figure out how.

How is this done?


Solution

  • I think you need quo_name (from dplyr or rlang), which transforms a quoted symbol to a string:

    my_function <- function(data, group = GROUP_ONE){
    
        require(dplyr)
        require(magrittr)
    
        group <- enquo(group)
    
        if(!quo_name(group) %in% names(data)) stop("CUSTOM ERROR MESSAGE")
    
        result <- data %>% 
            group_by(!!group) %>% 
            summarise(mean=mean(result))
    
        return(result)
    }
    
    # > my_function(data, GROUP_THREE)
    # Error in my_function(data, GROUP_THREE) : CUSTOM ERROR MESSAGE
    

    Edit

    As noted by lionel in comment: except for quo_name, there are many other alternatives including base R as.character and as_string from rlang.