0

I stumbled over an issue when using NSE in a custom function inside dplyr::mutate. Consider the following code:

require(tidyverse)

f <- function(var) {
  varname <- deparse(substitute(var))
  v1      <- as.name(sprintf("%s.Width", varname))
  v2      <- as.name(sprintf("%s.Length", varname))
  return(substitute(v1 + v2))
}

iris %>% 
  mutate(
    test = f(Sepal) %>% eval()
  )

ff <- function(var) {
  varname <- deparse(substitute(var))
  v1      <- as.name(sprintf("%s.Width", varname))
  v2      <- as.name(sprintf("%s.Length", varname))
  substitute(v1 + v2) %>% eval.parent(n = 1)
}

iris %>% 
  mutate(
    test = ff(Sepal)
  )

Here f works fine but requires an external call to eval() to execute the code inside the mutate() environment. This is of course a bit ugly and leads to a lot of boilerplate code. My best guess at making this work was ff which tries to evaluate the constructed expression in its calling environment - which I expected to be the mutate() environment. However, this throws a variable-not-found error. Any thoughts on how to make this work and what the underlying issue is? Essentially I want allow custom 'macros' in dplyr verbs.

JJJ
  • 1,009
  • 6
  • 19
  • 31
Jack
  • 195
  • 2
  • 10

1 Answers1

1

Using the !! tidy eval "unquote" operator:

ff <- function(var) {
  varname <- deparse(substitute(var))
  v1      <- as.name(sprintf("%s.Width", varname))
  v2      <- as.name(sprintf("%s.Length", varname))
  substitute(v1 + v2)
}

iris %>% 
  head() %>% 
  mutate(
    test = !! ff(Sepal)
  )
Aurèle
  • 12,545
  • 1
  • 31
  • 49
  • thanks, but that's just a nicer variant of eval, right? Why can't I access the correct envronment directly? – Jack Aug 03 '18 at 10:38
  • @Jack Not quite. The `!!` operator does "unquoting", not evaluation. I used it because it feels more natural to me in the context of the new tidyeval. But you can achieve what you were trying by just dropping the pipe `%>%` (that was messing up with the hierarchy of calling environments): `eval.parent(substitute(v1 + v2), n = 1)` – Aurèle Aug 03 '18 at 12:03