I am trying to implement advice I am finding in the web but I am halfway where I want to go.
Here is a reproducible example:
library(tidyverse)
library(dplyr)
library(rlang)
data(mtcars)
filter_expr = "am == 1"
mutate_expr = "gear_carb = gear*carb"
select_expr = "mpg , cyl"
mtcars %>% filter_(filter_expr) %>% mutate_(mutate_expr) %>% select_(select_expr)
The filter expression works fine.
The mutate expression works as well but the new variable has the name gear_carb = gear*carb instead of the intended gear_carb.
Finally, the select expression returns an exception.
The filter () function in dplyr (and other similar functions from the package) use something called non-standard evaluation (NSE). In NSE, names are treated as string literals.
Convert a String to an Expression in R Programming – parse () Function parse () function in R Language is used to convert an object of character class to an object of expression class. Syntax: parse (text = character)
Last Updated : 24 Jun, 2020. parse () function in R Language is used to convert an object of character class to an object of expression class. Syntax: parse (text = character) Parameters: character: Object of character class. Example 1: x <- "sin (pi / 2)" class(x)
If we don’t want to pass a string but a name instead, the tidyverse has recently introduced a { {}} (#curly-curly’) operator for tidy evaluation. See now we can pass the variable name climbing to the group_by function using the { {}} operator.
As mentioned in the comments, the underscore versions of dplyr verbs are now deprecated. The correct approach is to use quasiquotation.
To address your issue with select, you simply need to modify select_expr to contain multiple expressions:
## I renamed your variables to *_str because they are, well, strings.
filter_str <- "am == 1"
mutate_str <- "gear_carb = gear*carb"
select_str <- "mpg; cyl" # Note the ;
Use rlang::parse_expr to convert these strings to unevaluated expressions:
## Notice the plural parse_exprs, which parses a list of expressions
filter_expr <- rlang::parse_expr( filter_str )
mutate_expr <- rlang::parse_expr( mutate_str )
select_expr <- rlang::parse_exprs( select_str )
Given the unevaluated expressions, we can now pass them to the dplyr verbs. Writing filter( filter_expr ) won't work because filter will look for a column named filter_expr in your data frame. Instead, we want to access the expression stored inside filter_expr. To do this we use the !! operator to let dplyr verbs know that the argument should be expanded to its contents (which is the unevaluated expressions we are interested in):
mtcars %>% filter( !!filter_expr )
# mpg cyl disp hp drat wt qsec vs am gear carb
# 1 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
# 2 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
# 3 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
# 4 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
mtcars %>% mutate( !!mutate_expr )
# mpg cyl disp hp drat wt qsec vs am gear carb gear_carb = gear * carb
# 1 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 16
# 2 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 16
# 3 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 4
# 4 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 3
In case of select, we have multiple expressions, which is handled by !!! instead:
mtcars %>% select( !!!select_expr )
# mpg cyl
# Mazda RX4 21.0 6
# Mazda RX4 Wag 21.0 6
# Datsun 710 22.8 4
P.S. It's also worth mentioning that select works directly with string vectors, without having to rlang::parse_expr() them first:
mtcars %>% select( c("mpg", "cyl") )
# mpg cyl
# Mazda RX4 21.0 6
# Mazda RX4 Wag 21.0 6
# Datsun 710 22.8 4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With