Passing an expression for a function in R with quotations

I want to pass in an expression as a character vector for a function in R

For example,

library(dplyr)
filter_function <- function(data, filter_exp) {
  data %>% 
    filter( {{ filter_exp }})
}

filter_function(mtcars, filter_exp = cyl == 6)

However, I would like it to work where the filter_exp argument requires quotation marks. For example,

filter_function(mtcars, filter_exp = cyl == 6) # works

filter_function(mtcars, filter_exp = "cyl == 6") # does not work

This is a way that seems to work, but this doesn't seem to be the current suggested way of programming in dplyr from the "Programming with dplyr" vignette.

filter_function_2 <- function(data, filter_exp) {
  filter(data, !! rlang::parse_expr(filter_exp))
  
}
filter_function_2(mtcars, filter_exp = "cyl == 6") 

Is this still the most sensible way of doing this or is there a better way to read in an expression with quotations using the current suggested way of programming in dplyr?

2 Likes

In general we avoid this sort of parsing because it is unstructured. If you're taking inputs from e.g. a Shiny app, then it might be better to split the components of the comparison into three parts: LHS, RHS, and operation.

You can provide different semantics for each part. For instance you might take variable names as strings for the LHS and a value from the environment for the RHS with the .data and .env pronouns:

filter_function <- function(data, var, value, op = `==`) {
  data %>% filter(op(.data[[var]], .env$value))
}

mtcars %>% filter_function("cyl", 6)

# Pass a different comparison function
mtcars %>% filter_function("cyl", 6, op = `<`)

You could also allow arbitrary expressions for the variable names with {{:

filter_function <- function(data, var, value, op = `==`) {
  data %>% filter(op({{ var }}, .env$value))
}

In this case your function takes expressions that can refer to the columns:

mtcars %>% filter_function(cyl, 6)

Your users can still pass strings by using the .data pronoun:

var <- "cyl"
mtcars %>% filter_function(.data[[var]], 6)

And it is fully flexible, e.g. you can pass an expression that combines multiple columns:

mtcars %>% filter_function(cyl + am, 6)
4 Likes

Thanks for this answer @lionel. Is this type of flexible tidyeval parsing and manipulation of expressions and function arguments (e.g., for the purpose of writing more advanced custom functions that use dplyr and tidyeval under the hood) covered systematically in a cookbook-style tutorial anywhere?

@Joels I don't think so.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.