Trying to make do.call()
work in the context of tidy evaluation:
library(rlang)
library(dplyr)
data <- tibble(item_name = c("apple", "bmw", "bmw"))
mutate(data, category = case_when(item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car"))
# # A tibble: 3 x 2
# item_name category
# <chr> <chr>
# 1 apple fruit
# 2 bmw car
# 3 bmw car
What differs between:
category_fn <- function(df, ...){
# browser()
cat1 <- quos(...)
mutate(df, category = case_when(!!! cat1))
}
category_fn(df = data, item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car")
# # A tibble: 3 x 2
# item_name category
# <chr> <chr>
# 1 apple fruit
# 2 bmw car
# 3 bmw car
and:
cat <- list(item_name == "apple" ~ "fruit", item_name == "bmw" ~ "car")
do.call(category_fn, c(list(df = data), cat), quote = FALSE)
# Or:
do.call(category_fn, c(list(df = data), cat), quote = TRUE)
# Or:
rlang::invoke(category_fn, c(list(df = data), cat))
which all give the same error:
# Error in mutate_impl(.data, dots) :
# Evaluation error: object 'item_name' not found.
I stepped into the function with browser()
, examined the arguments, ran expr(mutate(df, category = case_when(!!! cat1)))
there (as suggested as a universal debugging strategy in http://rpubs.com/lionel-/programming-draft), with the same output in both cases: mutate(df, category = case_when(~(item_name == "apple" ~ "fruit"), ~(item_name == "bmw" ~ "car")))
.
I've also tried to tweak the envir
or .env
arguments to no avail.
My understanding is that it has likely something to do with different quosure environments, but environment(cat1[[1]])
is also identical (<environment: R_GlobalEnv>
).
Note:
This is somehow a follow-up of Tidy evaluation programming with dplyr::case_when which I was trying to answer.
> sessioninfo::session_info()
─ Session info ────────────────────────────────────────────────────────
setting value
version R version 3.4.3 (2017-11-30)
os Linux Mint 18
system x86_64, linux-gnu
[...]
─ Packages ────────────────────────────────────────────────────────────
package * version date source
[...]
dplyr * 0.7.4 2017-09-28 CRAN (R 3.4.3)
[...]
rlang * 0.1.6 2017-12-21 CRAN (R 3.4.3)
[...]
We could create 'cat' as a quosure
and then do the evaluation with !!!
cat <- quos(item_name == "apple" ~ "fruit", item_name == "bmw" ~ "car")
category_fn(data, !!!(cat))
# A tibble: 3 x 2
# item_name category
# <chr> <chr>
#1 apple fruit
#2 bmw car
#3 bmw car
I think it's a similar issue to the other post; quoting the list itself is not the same as quoting the elements of the list individually.
I have modified the cat definition to quote the elements individually, and the function slightly to remove the quosure statement and explicitly name the argument. In the do.call statements the second argument, the list of arguments to be supplied to the function, I have included the cat element as part of the list.
With these modifications the two do.call statements and the invoke then return the same result as the direct execution in your post:
data <- tibble(item_name = c("apple", "bmw", "bmw"))
cat <- list(quo(item_name == "apple" ~ "fruit"),
quo(item_name == "bmw" ~ "car"))
category_fn <- function(df, category){
mutate(df, category = case_when(!!! category))
}
> do.call(category_fn, list(data, cat), quote = FALSE)
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
> # Or:
> do.call(category_fn, list(data, cat), quote = TRUE)
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
> # Or:
> rlang::invoke(category_fn, list(df = data, cat))
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
The value of the quote argument makes no difference in the two do.call examples.
I find quosures conceptually difficult, and not made a great deal easier by the current programming with dplyr vignette on Cran.
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