I am trying to write a function that operates similarly to with() where the function has a data argument, and two other arguments that I would like evaluated in the context of the data frame.
I initially tried something like below where we just wrap with(), but couldn't get it working.
# this works
with(mtcars, mean(am) - mean(vs))
#> [1] -0.03125
# how to extend this to a function
mean_diff <- function(data, x, y) {
with(data, mean(x = x) - mean(x = y))
}
mean_diff(mtcars, x = am, y = vs)
#> Error: object 'am' not found
Created on 2025-05-08 with reprex v2.1.1
Other things I tried that did not work include
# these variants don't work either
mean_diff <- function(data, x, y) {
rlang::inject(
with(data, mean(x = {{ x }}) - mean(x = {{ y }}))
)
}
mean_diff(mtcars, x = am, y = vs)
mean_diff <- function(data, x, y) {
rlang::eval_tidy(
mean(x = x) - mean(x = y),
data = data
)
}
mean_diff(mtcars, x = am, y = vs)
mean_diff <- function(data, x, y) {
rlang::eval_tidy(
rlang::expr(mean(x = x) - mean(x = y)),
data = data
)
}
mean_diff(mtcars, x = am, y = vs)
Any ideas for getting a function like this with this user experience working? Thank you!
You are on the right track with rlang::inject(). But note that the documentation states that
You can use
{{ arg }}with functions documented to support quosures. Otherwise, use!!enexpr(arg).
So let’s try that:
mean_diff <- function(data, x, y) {
rlang::inject(
with(data, mean(x = !! rlang::enexpr(x)) - mean(x = !! rlang::enexpr(y)))
)
}
mean_diff(mtcars, x = am, y = vs)
[1] -0.03125