Программно использовать dplyr :: case_when с аргументами

Я хотел бы иметь возможность использовать dplyr case_when программным способом для замены базовой функции R cut().

В настоящее время case_when можно использовать с внешним аргументом через NSE, например:

library(dplyr)
library(rlang)

patterns <- list(
  x <= 2 ~ "<=2",
  x <= 4 ~ "2<->4",
  x > 4 ~ ">4"
 )
 x <- 1:10
 case_when(!!!patterns)

Что я хочу сделать, так это использовать его с другой переменной внутри мутации.

Идея была бы примерно такой, хотя я не могу понять, как заставить ее работать:

library(dplyr)
patterns_lazy <- list(
  !!quo(x) <= 2 ~ "<=2",
  !!quo(x) <= 4 ~ "2<->4",
  !!quo(x) > 4 ~ ">4"
)
x <- "cyl"
mtcars %>% mutate(ABC = case_when(!!!patterns_lazy))

Я хотел бы иметь возможность определить столбец (внутри строки), который я хочу фильтровать, и получить что-то вроде этого (этот пример не работает, поскольку это желаемый синтаксис):

x <- "cyl"
mtcars %>%
  select(cyl) %>%
  mutate(ABC = case_when(!!!patterns_lazy)) %>%
  head()

  cyl ABC
1   6 >4
2   6 >4
3   4 2<->4
4   6 >4
5   8 >4
6   6 >4

Спасибо за любую помощь :)


person RobinCura    schedule 29.06.2017    source источник


Ответы (2)


Вы не можете использовать там !!:

patterns <- list(
  !!quo(x) <= 2 ~ "<=2",
  !!quo(x) <= 4 ~ "2<->4",
  !!quo(x) > 4 ~ ">4"
)
  1. Ни list(), ни ~ квазиквотирование не поддерживают.
  2. Если бы он поддерживал квазиквотацию, вам нужно было бы быть осторожным с приоритетом операторов и заключать !!quo() в круглые скобки.
  3. И, наконец, эта цитата для x будет оценивать строку, и вы будете сравнивать числа со строками (в вашем примере это будет "cyl), что R с радостью сделает благодаря неявному принуждению: /

Поэтому вам нужно использовать exprs() вместо list() и использовать x с .data местоимением вместо цитаты x.

exprs() создаст список неоцененных выражений. Без оценки - это хорошо: если ваша формула была оценена, она будет содержать среду (здесь глобальный env), и эта среда не содержит никаких данных, предоставленных dplyr, и, в частности, не имеет местоимения .data. С другой стороны, если формулы не зависят от контекста, они оцениваются в контексте данных, чего мы и хотим.

patterns_lazy <- exprs(
  .data[[x]] <= 2 ~ "<=2",
  .data[[x]] <= 4 ~ "2<->4",
  .data[[x]] > 4 ~ ">4"
)

x <- "cyl"
pull(mutate(mtcars, case_when(!!!patterns_lazy)))
#>  [1] ">4"    ">4"    "2<->4" ">4"    ">4"    ">4"    ">4"    "2<->4" "2<->4"
#> [10] ">4"    ">4"    ">4"    ">4"    ">4"    ">4"    ">4"    ">4"    "2<->4"
#> [19] "2<->4" "2<->4" "2<->4" ">4"    ">4"    ">4"    ">4"    "2<->4" "2<->4"
#> [28] "2<->4" ">4"    ">4"    ">4"    "2<->4"
person Lionel Henry    schedule 29.06.2017

Вот один вариант с ifelse

f1 <- function(data, x){
       x <- enquo(x)       
       f2 <- function(y) ifelse(y <= 2, "<=2", ifelse(y <=4, "2<->4", ">4"))

  data %>%
          mutate( ABC = f2(UQ(x)))
 }

f1(mtcars, cyl) %>%
              head()
#   mpg cyl disp  hp drat    wt  qsec vs am gear carb   ABC
#1 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4    >4
#2 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4    >4
#3 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1 2<->4
#4 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1    >4
#5 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2    >4
#6 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1    >4
person akrun    schedule 29.06.2017
comment
Спасибо за этот ответ, но я выбрал синтаксис case_when, так как считаю его более читаемым, чем вложенный ifelse :) - person RobinCura; 29.06.2017