尝试让do.call()
在整洁的评估环境中发挥作用:
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
之间有什么不同:
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
和
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))
都会出现同样的错误:
# Error in mutate_impl(.data, dots) :
# Evaluation error: object 'item_name' not found.
我使用browser()
进入函数,检查参数,在那里运行expr(mutate(df, category = case_when(!!! cat1)))
(如http://rpubs.com/lionel-/programming-draft中的通用调试策略所示),在两种情况下输出相同: mutate(df, category = case_when(~(item_name == "apple" ~ "fruit"), ~(item_name == "bmw" ~ "car")))
。
我也尝试调整envir
或.env
参数无济于事。
我的理解是它可能与不同的quosure环境有关,但environment(cat1[[1]])
也是相同的(<environment: R_GlobalEnv>
)。
注意:
这是我试图回答的Tidy evaluation programming with dplyr::case_when的后续行动。
> 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)
[...]
答案 0 :(得分:3)
我们可以将'cat'创建为quosure
,然后使用!!!
进行评估
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
答案 1 :(得分:2)
我认为这与其他帖子类似;引用列表本身与单独引用列表的元素不同。
我已经修改了cat定义以单独引用元素,并稍微修改了函数以删除quosure语句并明确命名参数。在do.call语句中,第二个参数,即要提供给函数的参数列表,我已将cat元素作为列表的一部分包含在内。
通过这些修改,两个 do.call 语句和 invoke 会返回与帖子中直接执行相同的结果:
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
quote参数的值在两个do.call示例中没有区别。
我觉得Quosures在概念上很困难,而且目前在Cran上的programming with dplyr小插图并没有那么容易。
答案 2 :(得分:2)
我对Tidy evaluation programming with dplyr::case_when的回复(1a)部分的答案也适用于此。
如果cat
,data
和category_fn
与当前问题相同,则可行。第一行将cat
转换为cat_
,其形式可在此处使用。
cat_ <- lapply(cat, function(x) do.call("substitute", list(x)))
do.call("category_fn", c(list(df = data), cat_))
,并提供:
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
关于最后的问题,似乎在我对上述链接的原始问题的答案中要求替代quosures是使用wrapr包和基础R的问题的解决方案。作者的seplyr包包裹,也可能是另一种选择。