Типичное программирование оценки с помощью 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"))
Каков способ сделать это?
Ответы
Ответ 1
Сначала укажите каждый элемент вашего списка:
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
Ответ 2
1) Список переходов Используя let
из пакета wrapr и 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