Skip to content

Instantly share code, notes, and snippets.

@steve-liang
Last active July 7, 2019 20:03
Show Gist options
  • Select an option

  • Save steve-liang/31597d8c909f923e2c4fc3f9dcb56b57 to your computer and use it in GitHub Desktop.

Select an option

Save steve-liang/31597d8c909f923e2c4fc3f9dcb56b57 to your computer and use it in GitHub Desktop.
Gist for Twitter's Troubleshooting substitute() vs. enquo()
my_summarise() is a custom function that automatically choose between sym() and enquo() for input type "character" and ^quosure
## This works!
my_summarise <- function(df, var){
if(inherits(substitute(var), "character")){
var = sym(var)
}
else if(inherits(substitute(var), "name")){
var = enquo(var)
}
df %>% summarise(avg = mean(!!var))
}
identical(my_summarise(iris, Sepal.Length),
my_summarise(iris, 'Sepal.Length'))
# TRUE
######################
## Thought substitute() was equivalent to enquo() but it's not. Replacing substitute() with enquo()
my_summarise2 <- function(df, var){
if(inherits(enquo(var), "character")){
var = sym(var)
}
else if(inherits(enquo(var), "name")){
var = enquo(var)
}
df %>% summarise(avg = mean(!!var))
}
identical(my_summarise2(iris, Sepal.Length),
my_summarise2(iris, 'Sepal.Length'))
# object 'Sepal.Length' not found
#####################
## Replacing enquo() with enexpr() WORKS !
my_summarise3 <- function(df, var){
if(inherits(enexpr(var), "character")){
var = sym(var)
}
else if(inherits(enexpr(var), "name")){
var = enquo(var)
}
df %>% summarise(avg = mean(!!var))
}
identical(my_summarise3(iris, Sepal.Length),
my_summarise3(iris, 'Sepal.Length'))
# TRUE
@mikmart
Copy link

mikmart commented Jul 7, 2019

Whenever you want to make a selection of columns, think {tidyselect}:

suppressPackageStartupMessages(library(dplyr))

my_summarise2 <- function(df, var){
  var <- tidyselect::vars_pull(names(df), {{ var }})
  df %>% summarise(avg = mean(!!sym(var)))
}

my_summarise2(iris, Sepal.Length)
#>        avg
#> 1 5.843333
my_summarise2(iris, 'Sepal.Length')
#>        avg
#> 1 5.843333

selection <- "Sepal.Length"
my_summarise2(iris, selection)
#>        avg
#> 1 5.843333

Created on 2019-07-07 by the reprex package (v0.3.0)

@mikmart
Copy link

mikmart commented Jul 7, 2019

As to enquo() vs substitute(): enquo() will always return a quosure. enexpr() is the more direct equivalent to substitute(), and would work with your example.

@steve-liang
Copy link
Author

@mikmart, that's awesome. I had no idea about tidyselect and enexpr(), thanks for sharing. I'll paste this in my original tweet. This should be written out in other RStudio's education materials.

@mikmart
Copy link

mikmart commented Jul 7, 2019

Yeah this is one of the few cases I can think of where the distinction between enquo() and enexpr() is important. Most of the time you can just use enquo() almost everywhere.

@steve-liang
Copy link
Author

For anyone else stumbled on this. My takeaway is,

enquo() returns tidyverse-specific type quosure, which has a base R type expression component + it tracks global environment for easy passing across tidy functions. It is advised to use it only within tidyverse functions.

enexpr() returns base R type expression. Since it returns a base R type, this makes it equivalent to substitute(). You can use it outside of tidyverse framework, in my case feed it into a non-tidy function inherits().

Bottomline, use enquo() within pure tidyverse functions. use enexpr() or substitute() when you need some base R interactions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment