我可以在给定的 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
的问题是接受一个 length
是 nrow
或 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
才能接受新变量。
更新:显然这个功能现在计划用于 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
的问题是接受一个 length
是 nrow
或 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
才能接受新变量。