使用 dplyr::case_when 进行整洁的评估编程
Tidy evaluation programming with dplyr::case_when
我尝试编写一个围绕 dplyr::case_when() 函数的简单函数。我在 https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html 上阅读了 programming with dplyr 文档,但无法弄清楚它如何与 case_when() 函数一起使用。
我有以下数据:
data <- tibble(
item_name = c("apple", "bmw", "bmw")
)
以及以下列表:
cat <- list(
item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car"
)
然后我想写一个像这样的函数:
category_fn <- function(df, ...){
cat1 <- quos(...)
df %>%
mutate(category = case_when((!!!cat1)))
}
不幸的是category_fn(data,cat)
在这种情况下给出了评估错误。我想获得与通过以下方式获得的输出相同的输出:
data %>%
mutate(category = case_when(item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car"))
有什么方法可以做到这一点?
首先引用列表中的每个元素:
cat <- list(
quo(item_name == "apple" ~ "fruit"),
quo(item_name == "bmw" ~ "car")
)
您的函数不必引用 cat 对象本身。我还更改了 "everything else" ... 参数的使用,以在调用中明确引用类别参数:
category_fn <- function(df, categories){
df %>%
mutate(category = case_when(!!!categories))
}
函数的输出如预期的那样:
category_fn(data, cat)
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
为了完整起见,我注意到类别列表在使用基本 R quote() 函数定义时也适用于您的函数:
cat <- list(
quote(item_name == "apple" ~ "fruit"),
quote(item_name == "bmw" ~ "car")
)
> cat
[[1]]
item_name == "apple" ~ "fruit"
[[2]]
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) pass list 使用 wrapr 包中的 let
和问题中的 data
和 cat
这无需修改输入即可无论如何。
library(dplyr)
library(wrapr)
category_fn <- function(data, List) {
let(c(CATEGORY = toString(sapply(List, format))),
data %>% mutate(category = case_when(CATEGORY)),
subsMethod = "stringsubs",
strict = FALSE)
}
category_fn(data, cat) # test
给予:
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
1a) 使用 tidyeval/rlang 和 data
以及 cat
来自问题:
category_fn <- function(data, List) {
cat_ <- lapply(List, function(x) do.call("substitute", list(x)))
data %>% mutate(category = case_when(!!!cat_))
}
category_fn(data, cat)
给出与上面相同的结果。
2) 分别传递列表组件 如果您的意图是分别传递 cat
的每个组件而不是 cat
本身,那么这是可行的:
category_fn <- function(data, ...) eval.parent(substitute({
data %>% mutate(category = case_when(...))
}))
category_fn(data, item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car") # test
给予:
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
2a) 如果您更喜欢 tidyeval/rlang 那么这种情况很简单:
library(dplyr)
library(rlang)
category_fn <- function(data, ...) {
cat_ <- quos(...)
data %>% mutate(category = case_when(!!!cat_))
}
category_fn(data, item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car") # test
这是另一种以 tidyverse 为中心的方法
cat <- tribble(
~name, ~category,
"apple", "fruit",
"bmw", "car"
) %>%
str_glue_data("item_name == '{name}' ~ '{category}'")
data %>%
mutate(category = case_when(!!! map(cat, rlang::parse_expr)))
我尝试编写一个围绕 dplyr::case_when() 函数的简单函数。我在 https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html 上阅读了 programming with dplyr 文档,但无法弄清楚它如何与 case_when() 函数一起使用。
我有以下数据:
data <- tibble(
item_name = c("apple", "bmw", "bmw")
)
以及以下列表:
cat <- list(
item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car"
)
然后我想写一个像这样的函数:
category_fn <- function(df, ...){
cat1 <- quos(...)
df %>%
mutate(category = case_when((!!!cat1)))
}
不幸的是category_fn(data,cat)
在这种情况下给出了评估错误。我想获得与通过以下方式获得的输出相同的输出:
data %>%
mutate(category = case_when(item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car"))
有什么方法可以做到这一点?
首先引用列表中的每个元素:
cat <- list(
quo(item_name == "apple" ~ "fruit"),
quo(item_name == "bmw" ~ "car")
)
您的函数不必引用 cat 对象本身。我还更改了 "everything else" ... 参数的使用,以在调用中明确引用类别参数:
category_fn <- function(df, categories){
df %>%
mutate(category = case_when(!!!categories))
}
函数的输出如预期的那样:
category_fn(data, cat)
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
为了完整起见,我注意到类别列表在使用基本 R quote() 函数定义时也适用于您的函数:
cat <- list(
quote(item_name == "apple" ~ "fruit"),
quote(item_name == "bmw" ~ "car")
)
> cat
[[1]]
item_name == "apple" ~ "fruit"
[[2]]
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) pass list 使用 wrapr 包中的 let
和问题中的 data
和 cat
这无需修改输入即可无论如何。
library(dplyr)
library(wrapr)
category_fn <- function(data, List) {
let(c(CATEGORY = toString(sapply(List, format))),
data %>% mutate(category = case_when(CATEGORY)),
subsMethod = "stringsubs",
strict = FALSE)
}
category_fn(data, cat) # test
给予:
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
1a) 使用 tidyeval/rlang 和 data
以及 cat
来自问题:
category_fn <- function(data, List) {
cat_ <- lapply(List, function(x) do.call("substitute", list(x)))
data %>% mutate(category = case_when(!!!cat_))
}
category_fn(data, cat)
给出与上面相同的结果。
2) 分别传递列表组件 如果您的意图是分别传递 cat
的每个组件而不是 cat
本身,那么这是可行的:
category_fn <- function(data, ...) eval.parent(substitute({
data %>% mutate(category = case_when(...))
}))
category_fn(data, item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car") # test
给予:
# A tibble: 3 x 2
item_name category
<chr> <chr>
1 apple fruit
2 bmw car
3 bmw car
2a) 如果您更喜欢 tidyeval/rlang 那么这种情况很简单:
library(dplyr)
library(rlang)
category_fn <- function(data, ...) {
cat_ <- quos(...)
data %>% mutate(category = case_when(!!!cat_))
}
category_fn(data, item_name == "apple" ~ "fruit",
item_name == "bmw" ~ "car") # test
这是另一种以 tidyverse 为中心的方法
cat <- tribble(
~name, ~category,
"apple", "fruit",
"bmw", "car"
) %>%
str_glue_data("item_name == '{name}' ~ '{category}'")
data %>%
mutate(category = case_when(!!! map(cat, rlang::parse_expr)))