在mutate管道中使用case_when

64

看起来dplyr::case_whendplyr::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
是的,它不会。在此处检查case_when的部分这里,以及这里 - Sumedh
如下所述,使用dplyr > 0.7.0版本,此问题已经解决。 - SprengMeister
也许可以接受其中一个答案,看起来现在这个问题已经解决了。 - zx8754
如果您想将一个范围分成几个间隔,考虑使用 cut 而不是 case_whencut 有一个 labels 参数,可以重命名结果类别。 - Paul Rougieux
7个回答

98

3
dplyr < 0.7.0 会受益于一个错误信息,指出这个问题已经在 0.7.0 中得到解决,而不是 Error: object 'carb' not found - Paul
为了完整起见,您可以在结尾处添加:, .default = "other" 作为“else”语句。 - arielhasidim

22

We can use .$

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

当然,我熟悉“.”运算符,但对于“cut”、“recode”等操作来说并不是必需的。 - tomw
3
我会尽力为您翻译:@tomw,根据您发布的问题和Sumedh(已删除帖子)的说法,我只是回答了问题,并且根据目前情况,case_when函数仍处于实验阶段,因此在其他函数中有效的一些常规操作在这里可能无法使用。 - akrun

8
感谢@sumedh和@hadley,@hadley已经解释了这是case_when的已知缺陷:

case_when()仍然是一种实验性质的函数,目前无法在mutate()内使用。这个问题将在未来的版本中得到修复。


6

在我的情况下,准引用(quasiquotation)非常有帮助。您可以预先创建一组引用的公式,定义变异规则(并使用已知列名作为第一个公式中的列名,或者利用!!动态创建规则,如第二个公式所示),然后在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

您可以在不需要手动输入所有规则的情况下,在不同地方使用它,并应用于不同的数据集。

作为解决问题的最终答案,7个附加符号和两个括号解决了它。

library(rlang)
library(dplyr)
mtcars %>% 
    mutate(test = case_when(!!!quos(gear == 3L ~ "three", gear != 3L ~ "not three"))) %>% 
    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 not three
#> 2  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4 not three
#> 3  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1 not three
#> 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 not three
#> 9  22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2 not three
#> 10 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4 not three

本文创建于2019年1月16日,使用reprex软件包 (v0.2.1.9000)


0
data_2 = mutate(data, name = case_when(condition_new_var  ~ value, condition_new_var ~ value, condition_new_var ~ value))

1
欢迎来到Stack Overflow!请阅读[答案]并[编辑]您的答案,以包含有关此代码实际解决问题的说明。请记住,您不仅要解决问题,还要教育OP和任何未来的读者。 - Adriaan

-2
library(dplyr) #loading the dplyr package

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"
        )
      )

好的,我会尝试不同的东西。 - varun

-4
除了@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")
      ) 

3
嗯?对我来说两者都可以。我无法想象“解析器”会对这样的事情有问题(与底层评估器相比)。 - Hong Ooi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接