Assume this was your dataset:
df1 <- tribble(~ Country, ~ Value,
'Brazil', 1,
'Brazil', 2,
'Canada', 3,
'Canada', 4)
> df1
# A tibble: 4 × 2
Country Value
<chr> <dbl>
1 Brazil 1
2 Brazil 2
3 Canada 3
4 Canada 4
You could write your custom filter function simply as:
fun1 <- function(df, Country_name){
df %>%
filter(Country == Country_name)
}
> fun1(df1, 'Brazil')
# A tibble: 2 × 2
Country Value
<chr> <dbl>
1 Brazil 1
2 Brazil 2
But imagine you want to be able to omit the quotes around 'Brazil'
and still get the same output. If you made no modification you would get an error:
> fun1(df1, Brazil)
# ...
#! object 'Brazil' not found
# ...
R is understanding Brazil
as a variable, and is looking for it in your global environment. It is failing to find it, and then, it returns an error. If Brazil
were a variable, you could get weird results:
Brazil <- 'Cadada'
> fun2(df1, Brazil)
# A tibble: 2 × 2
Country Value
<chr> <dbl>
1 Canada 3
2 Canada 4
R is seeing that Brazil
has the value of 'Canada'
, binding that value to Country_name
, and using that value on the filter.
That's not what you wanted. You wanted to get the actual word Brazil
, and not the value it represents. That is what the line you were referring to does. I'll explain how it works below.
The first step is saying to R "I don't want you to evaluate the argument you received, I just want you to save it's text". That is, we want to delay the evaluation of the expression that was passed onto Country_name
. That can be done in several ways:
substitute(Country_name)
in base R, as Nir Graham noted;
substitute returns the parse tree for the (unevaluated) expression ... -substitute's help page.
enquo(Country_name)
with rlang, as your function did.
enquo() and enquos() defuse function arguments. A defused expression can be examined, modified, and injected into other expressions. -enquo's help page.
enexpr(Country_name)
with rlang, also as Nir Graham noted;
enexpr() and enexprs() are like enquo() and enquos() but return naked expressions instead of quosures. -enexpr's help page
So they all have very similar effects. The biggest difference is that enquo
"return quosures instead of naked expressions". In simple terms, quosures are expressions that also point to the environment where the value for it's relevant variables should be found*. We don't need that (but it's also not a problem), as the expression in question wont be evaluated, we just want it's text.
After that we just want to get the text of that defused expression, which can be made with:
as.character()
;
deparse1()
;
rlang::quo_name()
;
rlang::expr_name()
.
And others. Thus, the options are similar to what Nir Graham did:
fun2_base <- function(df, Country_name){
Country_name <- deparse1(substitute(Country_name))
df %>%
filter(Country == Country_name)
}
fun2_rlang <- function(df, Country_name){
Country_name <- as.character(enexpr(Country_name))
df %>%
filter(Country == Country_name)
}
fun2_base(df1, Brazil)
fun2_rlang(df1, Brazil)
All yield:
# A tibble: 2 × 2
Country Value
<chr> <dbl>
1 Brazil 1
2 Brazil 2
Note that we didn't needed to remove that Brazil
variable, because it's not being evaluated.
*: To know more, read about tidy evaluation and the metaprogramming chapters of "Advanced R"
parse_expr(quo_name(enquo('string')))
is wrong. The correct way of usingenquo()
should be likefoo(df, Country_name = `United States`)
, and there should be aUnited states
column in thatdf
object. – MedoraCanada
instead of"Canada"
. However, this is in my opinion not great practice asCountry_name
is a character column, so it would make more sense for the function to accept that arg as a character than a symbol. Also `parse_expr()‘ isn’t necessary. – Hitandmiss