我可以在给定的 case_when true 子句中进行多项分配吗?

Can I make multiple assignments within a given case_when true clause?

更新:显然这个功能现在计划用于 dplyr,如这里所讨论的:https://github.com/tidyverse/dplyr/pull/6145

在 SQL 中,可以分配多个变量 - 比如 var1 和 var2 - 当给定的情况得到验证时,使用类似

的构造

case when condition then var1 = x, var2 = y

dplyr::case_when(或 tidyverse 中的任何其他内容)是否支持此有用的功能),如果支持,如何支持?!

请注意,在下面的示例中,var1、var2 和 var3 具有完全相同的测试条件,即 Species == "setosa",并且退化情况为 TRUE。我想通过不重复条件来减少这种冗余: one 调用 case_when (或类似的),尽管对 var1、var2 和 var3 使用不同的 eval_on_true 表达式, 分别。显然,在此示例中,冗余不是问题,但我的 case_when 在现实生活中的示例变得非常庞大和复杂。

library(tidyverse)

# create example data
set.seed(1337)
data <- iris %>%
  sample_n(5) %>%
  select(Petal.Length, Petal.Width, Species) %>%
  as_tibble()
data
#> # A tibble: 5 × 3
#>   Petal.Length Petal.Width Species   
#>          <dbl>       <dbl> <fct>     
#> 1          5.5         1.8 virginica 
#> 2          5           1.9 virginica 
#> 3          1.5         0.2 setosa    
#> 4          5.9         2.3 virginica 
#> 5          4.1         1.3 versicolor
data %>%
  mutate(var1 = case_when(Species == "setosa" ~ "green", TRUE ~ "blue"),
         var2 = case_when(Species == "setosa" ~ Petal.Length * 99, TRUE ~ Petal.Length),
         var3 = case_when(Species == "setosa" ~ as.Date("2002-12-01"), TRUE ~ as.Date("2003-12-02")))
#> # A tibble: 5 × 6
#>   Petal.Length Petal.Width Species    var1   var2 var3      
#>          <dbl>       <dbl> <fct>      <chr> <dbl> <date>    
#> 1          5.5         1.8 virginica  blue    5.5 2003-12-02
#> 2          5           1.9 virginica  blue    5   2003-12-02
#> 3          1.5         0.2 setosa     green 148.  2002-12-01
#> 4          5.9         2.3 virginica  blue    5.9 2003-12-02
#> 5          4.1         1.3 versicolor blue    4.1 2003-12-02

reprex package (v2.0.1)

于 2022-02-09 创建

也许这有帮助:

library(tidyverse)

# create example data
set.seed(1337)
data <- iris %>%
  sample_n(5) %>%
  as_tibble()
data
#> # A tibble: 5 x 5
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
#>          <dbl>       <dbl>        <dbl>       <dbl> <fct>     
#> 1          6.4         3.1          5.5         1.8 virginica 
#> 2          6.3         2.5          5           1.9 virginica 
#> 3          5.3         3.7          1.5         0.2 setosa    
#> 4          6.8         3.2          5.9         2.3 virginica 
#> 5          5.7         2.8          4.1         1.3 versicolor

data %>%
  mutate(
    sepal_size = case_when(
      Sepal.Length > 6 & Sepal.Width > 3 ~ "big",
      Sepal.Width > 3 ~ "medium",
      TRUE ~ "small"
    ),
    petal_size = case_when(
      Petal.Length > 5 ~ "big",
      TRUE ~ "small"
    ),
    is_fancy = TRUE
  )
#> # A tibble: 5 x 8
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species    sepal_size
#>          <dbl>       <dbl>        <dbl>       <dbl> <fct>      <chr>     
#> 1          6.4         3.1          5.5         1.8 virginica  big       
#> 2          6.3         2.5          5           1.9 virginica  small     
#> 3          5.3         3.7          1.5         0.2 setosa     medium    
#> 4          6.8         3.2          5.9         2.3 virginica  big       
#> 5          5.7         2.8          4.1         1.3 versicolor small     
#> # … with 2 more variables: petal_size <chr>, is_fancy <lgl>

reprex package (v2.0.1) 于 2022-02-08 创建 要反过来,你可以这样做:

iris %>%
  mutate(
    selected = Species == "setosa",
    var1 = ifelse(selected, Petal.Length * 99, Petal.Length),
    var2 = ifelse(selected, as.Date("2002-12-01"), as.Date("2003-12-02"))
  )

如果我们想通过将单个列与不同的值相乘来创建新列,一种 tidyverse 方法是使用 imap(默认情况下,默认 .y 是序列,如果我们传递未命名的元素)以遍历乘数值,return 具有 transmute 的单个列,然后将这些列与原始数据绑定

library(purrr)
library(dplyr)
library(stringr)
imap_dfc(c(10, 99), ~ data %>% 
   transmute(!! str_c('var', .y) := 
   case_when(Species == "setosa"~ Petal.Length * .x, 
              TRUE ~ Petal.Length))) %>% 
   bind_cols(data, .)

-输出

# A tibble: 5 × 7
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species     var1  var2
         <dbl>       <dbl>        <dbl>       <dbl> <fct>      <dbl> <dbl>
1          6.4         3.1          5.5         1.8 virginica    5.5   5.5
2          6.3         2.5          5           1.9 virginica    5     5  
3          5.3         3.7          1.5         0.2 setosa      15   148. 
4          6.8         3.2          5.9         2.3 virginica    5.9   5.9
5          5.7         2.8          4.1         1.3 versicolor   4.1   4.1

case_when 的问题是接受一个 lengthnrow 或 1 的值。因此,您必须通过生成列表来欺骗 case_when nrow 1 行数据帧。 访问器函数 lod(定义如下)构造一个单行数据帧列表:

lod <- function(...) {
  args <- list(...)
  do.call(function(...) mapply(data.frame, ..., SIMPLIFY = FALSE), args)
}

lod(x = 1:4, y = letters[1:4])

[[1]]
  x y
1 1 a

[[2]]
  x y
1 2 b

[[3]]
  x y
1 3 c

[[4]]
  x y
1 4 d

然后编写你的case_when语句,使用lod对变量进行分组,如下:

data |>
  mutate(case_when(Species == "setosa" ~
                     lod(var1 = "green",
                         var2 = Petal.Length * 99,
                         var3 = as.Date("2002-12-01")),
                   TRUE ~
                     lod(var1 = "blue",
                         var2 = Petal.Length,
                         var3 = as.Date("2003-12-02"))) |>
         bind_rows())

+ # A tibble: 5 x 6
  Petal.Length Petal.Width Species    var1   var2 var3      
         <dbl>       <dbl> <fct>      <chr> <dbl> <date>    
1          5.5         1.8 virginica  blue    5.5 2003-12-02
2          5           1.9 virginica  blue    5   2003-12-02
3          1.5         0.2 setosa     green 148.  2002-12-01
4          5.9         2.3 virginica  blue    5.9 2003-12-02
5          4.1         1.3 versicolor blue    4.1 2003-12-02

mutate 需要最后的 bind_rows 才能接受新变量。