I am writing a fuction that I will use regularly to filter for dates and names in our database (and then to perform some monthly counting/calculations on them).
I would like to know what is the correct way to insert and evaluate strings within rlang
functions in this case?
Am I doing right by using quo
to put strings into the fuction?
Example:
business_flights = tibble(passanger_name=rep(c(rep("John RED",3),rep("Mary ORANGE",3)),4),
dep_date=seq(from = lubridate::ymd('2005-04-07'),
to = lubridate::ymd('2025-03-22'), length.out = 24),
flight_num = sample(seq(from = 99, to = 1999, by = 30), size = 24, replace = TRUE))
filter_flights = function(mytibble, name, date0, date1) {
require(tidyverse); require(lubridate)
flights_filtered = mytibble %>%
filter(dep_date >= !!date0, dep_date < !!date1,
grepl(!!name, passanger_name))
View(flights_filtered)
}
filter_flights(mytibble = business_flights,
name = quo("RED"),
date0 = quo("2005-10-13"),
date1 = quo(today()))
Non-standard evaluation allows you to capture and manipulate expressions. In base R, this is primarily accomplished through the use of quote
:
quote(x)
# x
quote( a*log10(b+5) )
# a * log10(b + 5)
However, any captured expression that consists of a single literal is itself that literal:
identical( quote(5), 5 ) # TRUE
identical( quote("a"), "a" ) # TRUE
identical( quote(FALSE), FALSE ) # TRUE
identical( quote(5+5), 10 ) # FALSE, expression is not a single literal
rlang::quo
from tidyverse builds on this functionality by capturing an expression AND the environment that gave rise to that expression. Together, these define a quosure:
quo(x)
# <quosure>
# expr: ^x
# env: global
f <- function() {quo(x)}
f()
# <quosure>
# expr: ^x
# env: 0x55b7e61d5c80
Keeping expressions next to their environments allows you to ensure that they are always evaluated in a consistent fashion, as they make their way through your code:
x <- 5
g <- function( myexpr ) {
x <- 10
eval_tidy( myexpr )
}
x # Evaluate expression directly in its environment
# 5
g( quote(x) ) # Evaluate expression inside g()
# 10
g( quo(x) ) # Evaluate expression in its env, while inside g()
# 5
However, when capturing a literal inside a quosure, quo
assigns an empty environment to it:
quo("x")
# <quosure>
# expr: ^"x"
# env: empty
That is because the string "x"
will always evaluate to "x"
, regardless of what environment it's evaluated in. Because of this, there is almost never a good reason to quo
a string object (or any literal for that matter). It doesn't do anything and, as pointed out in the comments, your code will work fine without it.