Программирование вычисления Tidy с помощью dplyr::case_when
Я пытаюсь написать простую функцию, заключающуюся в функцию dplyr::case_when(). Я читаю программирование с помощью документации dplyr по https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html но не могу понять, как это работает с функцией 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"))
Как это сделать?
2 ответа
Сначала процитируйте каждый элемент вашего списка:
cat <- list(
quo(item_name == "apple" ~ "fruit"),
quo(item_name == "bmw" ~ "car")
)
Ваша функция не должна заключать в кавычки сам объект cat. Я также изменил использование аргумента "все остальное" ... для явной ссылки на аргумент категории в вызове:
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) Использование списка пропусков 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)))