Search code examples
rdplyrnse

Weird dplyr+NSE: using NSE in custom function inside mutate


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.


Solution

  • 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)
      )