17.7 Customising evaluation with data
Rebinding functions is an extremely powerful technique, but it tends to require a lot of investment. A more immediately practical application is modifying evaluation to look for variables in a data frame instead of an environment. This idea powers the base subset()
and transform()
functions, as well as many tidyverse functions like ggplot2::aes()
and dplyr::mutate()
. It’s possible to use eval()
for this, but there are a few potential pitfalls (Section 20.6), so we’ll switch to rlang::eval_tidy()
instead.
As well as expression and environment, eval_tidy()
also takes a data mask, which is typically a data frame:
data.frame(x = 1:5, y = sample(5))
df <-eval_tidy(expr(x + y), df)
#> [1] 6 5 7 5 7
Evaluating with a data mask is a useful technique for interactive analysis because it allows you to write x + y
rather than df$x + df$y
. However, that convenience comes at a cost: ambiguity. In Section 20.4 you’ll learn how to deal with ambiguity using special .data
and .env
pronouns.
We can wrap this pattern up into a function by using enexpr()
. This gives us a function very similar to base::with()
:
function(df, expr) {
with2 <-eval_tidy(enexpr(expr), df)
}
with2(df, x + y)
#> [1] 6 5 7 5 7
Unfortunately, this function has a subtle bug and we need a new data structure to help deal with it.