Case_when в мутантной трубе

Кажется, что dplyr::case_when не ведет себя как другие команды в вызове dplyr::mutate. Например:

library(dplyr)

case_when(mtcars$carb <= 2 ~ "low",
          mtcars$carb > 2 ~ "high") %>% 
  table

работы:

.
high  low 
  15   17 

Но поместите case_when в цепочку mutate:

mtcars %>% 
  mutate(cg = case_when(carb <= 2 ~ "low",
                        carb > 2 ~ "high"))

и вы получите:

 Error: object 'carb' not found

пока это прекрасно работает

mtcars %>% 
  mutate(cg = carb %>% 
           cut(c(0, 2, 8)))

Ответы

Ответ 1

Начиная с версии 0.7.0 от dplyr, case_when работает в mutate следующим образом:

library(dplyr) # >= 0.7.0
mtcars %>% 
  mutate(cg = case_when(carb <= 2 ~ "low",
                        carb > 2  ~ "high"))

Для получения дополнительной информации: http://dplyr.tidyverse.org/reference/case_when.html

Ответ 2

Мы можем использовать .$

mtcars %>%  
     mutate(cg = case_when(.$carb <= 2 ~ "low",  .$carb > 2 ~ "high")) %>%
    .$cg %>%
    table()
# high  low 
#  15   17 

Ответ 3

Благодаря @sumedh: @hadley объяснил, что это известный недостаток case_when:

case_when() все еще несколько экспериментирует и в настоящее время не работает внутри mutate(). Это будет исправлено в будущей версии.

Ответ 4

В моем случае квазиквотация очень помогла. Вы можете заранее создать набор формул в кавычках, которые определяют правила мутации (и либо использовать известные имена столбцов, как в первой формуле, либо использовать !! и динамически создавать правила, как во второй формуле), который затем используется в mutate - case_when комбинация как здесь

    library(dplyr)
    library(rlang)
    pattern <- quos(gear == 3L ~ "three", !!sym("gear") == 4L ~ "four", gear == 5L ~ "five")
    # Or
    # pattern <- list(
    #     quo(gear == 3L ~ "three"), 
    #     quo(!!sym("gear") == 4L ~ "four"),
    #     quo(gear == 5L ~ "five"))
    #
    mtcars %>% mutate(test = case_when(!!!pattern)) %>% head(10L)
#>     mpg cyl  disp  hp drat    wt  qsec vs am gear carb  test
#> 1  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4  four
#> 2  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  four
#> 3  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1  four
#> 4  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1 three
#> 5  18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2 three
#> 6  18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1 three
#> 7  14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4 three
#> 8  24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2  four
#> 9  22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2  four
#> 10 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4  four

Я предпочитаю такое решение, потому что оно позволяет создавать сложные правила, например, используя map2 с условиями LHS и значениями RHS для генерации цитируемых формул

    library(rlang)
    library(purrr)
    map2(c(3, 4, 5), c("three", "four", "five"), ~quo(gear == !!.x ~ !!.y))
#> [[1]]
#> <quosure>
#> expr: ^gear == 3 ~ "three"
#> env:  0000000014286520
#> 
#> [[2]]
#> <quosure>
#> expr: ^gear == 4 ~ "four"
#> env:  000000001273D0E0
#> 
#> [[3]]
#> <quosure>
#> expr: ^gear == 5 ~ "five"
#> env:  00000000125870E0

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

Создано в 2019-01-16 пакетом представлением (v0.2.1.9000)

Ответ 5

В дополнение к ответу @akrun выше, имейте в виду, что закрывающая скобка для case_when() не может помещаться в ее собственную строку.

Например, это работает нормально:

mtcars %>%  
   mutate(cg = case_when(
      .$carb <= 2 ~ "low",  .$carb > 2 ~ "high")) 

но это не так:

mtcars %>%  
   mutate(cg = case_when(
      .$carb <= 2 ~ "low",  .$carb > 2 ~ "high")
      ) 

Ответ 6

библиотека (dplyr) # загрузка пакета dplyr

    content150_fortified <- content150 %>% #creating a new variable
     mutate(number_yn = case_when( #creating a new column using mutate
        number >= 18 & number <=25 ~ "no", # if number is "none", make number_yn "no"
        number!="none" ~ "yes"  # if number is not "none", make number_yn "yes"
        )
      )