Andy
Andy

Reputation: 4659

Making new expressions within a function using existing quosures (dplyr programming)

I am trying to make "new expressions" based off quosures within a function from its arguments, but am unsure of how exactly to make this new expression.

Here is an example where I pass a numerator and denominator and would ideally do mutations on both but also would like to do a mutation where I divide them:

df <- tibble(
  g1 = c(1, 1, 2, 2, 2),
  g2 = c(1, 2, 1, 2, 1),
  a = sample(5), 
  b = sample(5)
)

my_divide <- function(df, numerator, denominator) {
  numerator <- enquo(numerator)
  denominator <- enquo(denominator)

  df %>%
    mutate(p = !!numerator / !!denominator)
}

my_divide(df, g1 , g2)

This fails with the following error:

Error in !denominator : invalid argument type 

I could easily pass the expression as its own argument and enquo() it, but this doesn't scale for more expressions. I could also create temporary columns in the dataframe from the base quosures and then calculate the expression directly, but this seems too verbose. I imagine there's a simpler way to do this

Upvotes: 0

Views: 113

Answers (3)

G. Grothendieck
G. Grothendieck

Reputation: 269694

1) / has higher precedence than ! but we want ! to bind more tightly since we want !! to be applied prior to the division, not afterwards. Put parentheses around !!numerator and then it will work. See ?Syntax for documentation of the precedence rules used by the R language.

2) Another possibility but without rlang/tidyeval is:

my_divide <- function(df, numerator, denominator = 1) {
  eval.parent(substitute(
   df %>% mutate(p = numerator / denominator)
  ))
}

my_divide(df, g1 / g2)
my_divide(df, g1, g2)

Upvotes: 2

seasmith
seasmith

Reputation: 919

Use backticks to call the divide function.

my_divide <- function(df, numerator, denominator) {
  n <- enquo(numerator)
  d <- enquo(denominator)

  df %>%
    mutate(p = `/`(!!n, !!d))
}

my_divide(df, g1 , g2)

Upvotes: 1

akrun
akrun

Reputation: 887213

An option would be to pass it as an quoted expression for later evaluation

my_divide <- function(df, exprs) {  

 df %>%
    mutate(p = !! exprs) 
 }

my_divide(df, quote(g1/ g2))
# A tibble: 5 x 5
#    g1    g2     a     b     p
#  <dbl> <dbl> <int> <int> <dbl>
#1  1.00  1.00     5     2 1.00 
#2  1.00  2.00     2     4 0.500
#3  2.00  1.00     3     1 2.00 
#4  2.00  2.00     4     5 1.00 
#5  2.00  1.00     1     3 2.00 

Upvotes: 0

Related Questions