Использование оператора switch в dplyr mutate
Я хотел бы использовать оператор switch внутри dplyr mutate. У меня есть простая функция, которая выполняет некоторые операции и назначает альтернативные значения через переключатель, например:
convert_am <- function(x) {
x <- as.character(x)
switch(x,
"0" = FALSE,
"1" = TRUE,
NA)
}
Это работает как нужно, когда применяется к скалярам:
>> convert_am(1)
[1] TRUE
>> convert_am(2)
[1] NA
>> convert_am(0)
[1] FALSE
Я хотел бы получить эквивалентные результаты через mutate
вызов:
mtcars %>% mutate(am = convert_am(am))
Это не удается:
Ошибка в
mutate_impl(.data, dots)
: Ошибка оценки: EXPR должен быть вектором длины 1.
Я понимаю, что это потому, что значения, передаваемые для переключения, не единичны, как в примере:
convert_am(c(1,2,2))
Ошибка вswitch(x, 0 = FALSE, 1 = TRUE, NA)
: EXPR должен быть длиной 1 вектор
Векторизация
Попытка векторизации также дает желаемые результаты:
convert_am <- function(x) {
x <- as.character(x)
fun_switch <- function(x) {
switch(x,
"0" = FALSE,
"1" = TRUE,
NA)
}
vf <- Vectorize(fun_switch, "x")
}
>> mtcars %>% mutate(am = convert_am(am))
Error in mutate_impl(.data, dots) :
Column `am` is of unsupported type function
Заметки
- Я в курсе
case_when
в dplyr, и я не заинтересован в его использовании, я заинтересован только в созданииswitch
работать внутри мутировать - Идеальное решение позволит для дальнейшего расширения использовать
mutate_at
с переменными, переданными как.
2 ответа
switch
не векторизовано, поэтому для эффективности вам нужно использовать ifelse
или же case_when
- но так как ваш вопрос конкретно о switch
, вы можете достичь того, что вы хотите, векторизация, например,
convert_am <- Vectorize(function(x) {
x <- as.character(x)
switch(x,
"0" = FALSE,
"1" = TRUE,
NA)
})
или же
convert_am <- function(x) {
x <- as.character(x)
sapply(x, function(xx) switch(xx,
"0" = FALSE,
"1" = TRUE,
NA))
}
Они оба неэффективны, так как включают в себя петлю под капотом.
This is simple enough to handle with ifelse directly:
Test <- tibble::tibble(
am = c(-1:5,NA,1, 0)
)
Test %>%
mutate(
newam = ifelse(am == 1, TRUE,
ifelse(am == 0, FALSE, NA))
)
With more categories, use a named vector:
Test %>%
mutate(
newam = ifelse(is.na(am) | !am %in% c(1,3,5), NA,
c("1" = "in1", "3" = "in3", "5" = "in5")[as.character(am)])
)
In fact if the value is not in the named list it will default to an NA
I think this will be pretty efficient
Test %>%
mutate(
newam = c("1" = "in1", "3" = "in3", "5" = "in5")[as.character(am)]
)