This function allows you to vectorise multiple if_else()
statements. Each
case is evaluated sequentially and the first match for each element
determines the corresponding value in the output vector. If no cases match,
the .default
is used as a final "else" statment.
case_when()
is an R equivalent of the SQL "searched" CASE WHEN
statement.
Arguments
- ...
<
dynamic-dots
> A sequence of two-sided formulas. The left hand side (LHS) determines which values match this case. The right hand side (RHS) provides the replacement value.The LHS inputs must evaluate to logical vectors.
The RHS inputs will be coerced to their common type.
All inputs will be recycled to their common size. That said, we encourage all LHS inputs to be the same size. Recycling is mainly useful for RHS inputs, where you might supply a size 1 input that will be recycled to the size of the LHS inputs.
NULL
inputs are ignored.- .default
The value used when all of the LHS inputs return either
FALSE
orNA
..default
must be size 1 or the same size as the common size computed from...
..default
participates in the computation of the common type with the RHS inputs.NA
values in the LHS conditions are treated likeFALSE
, meaning that the result at those locations will be assigned the.default
value. To handle missing values in the conditions differently, you must explicitly catch them with another condition before they fall through to the.default
. This typically involves some variation ofis.na(x) ~ value
tailored to your usage ofcase_when()
.If
NULL
, the default, a missing value will be used.- .ptype
An optional prototype declaring the desired output type. If supplied, this overrides the common type of the RHS inputs.
- .size
An optional size declaring the desired output size. If supplied, this overrides the common size computed from
...
.
Value
A vector with the same size as the common size computed from the
inputs in ...
and the same type as the common type of the RHS inputs
in ...
.
Examples
x <- 1:70
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
.default = as.character(x)
)
#> [1] "1" "2" "3" "4" "fizz"
#> [6] "6" "buzz" "8" "9" "fizz"
#> [11] "11" "12" "13" "buzz" "fizz"
#> [16] "16" "17" "18" "19" "fizz"
#> [21] "buzz" "22" "23" "24" "fizz"
#> [26] "26" "27" "buzz" "29" "fizz"
#> [31] "31" "32" "33" "34" "fizz buzz"
#> [36] "36" "37" "38" "39" "fizz"
#> [41] "41" "buzz" "43" "44" "fizz"
#> [46] "46" "47" "48" "buzz" "fizz"
#> [51] "51" "52" "53" "54" "fizz"
#> [56] "buzz" "57" "58" "59" "fizz"
#> [61] "61" "62" "buzz" "64" "fizz"
#> [66] "66" "67" "68" "69" "fizz buzz"
# Like an if statement, the arguments are evaluated in order, so you must
# proceed from the most specific to the most general. This won't work:
case_when(
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
x %% 35 == 0 ~ "fizz buzz",
.default = as.character(x)
)
#> [1] "1" "2" "3" "4" "fizz" "6" "buzz" "8" "9" "fizz"
#> [11] "11" "12" "13" "buzz" "fizz" "16" "17" "18" "19" "fizz"
#> [21] "buzz" "22" "23" "24" "fizz" "26" "27" "buzz" "29" "fizz"
#> [31] "31" "32" "33" "34" "fizz" "36" "37" "38" "39" "fizz"
#> [41] "41" "buzz" "43" "44" "fizz" "46" "47" "48" "buzz" "fizz"
#> [51] "51" "52" "53" "54" "fizz" "buzz" "57" "58" "59" "fizz"
#> [61] "61" "62" "buzz" "64" "fizz" "66" "67" "68" "69" "fizz"
# If none of the cases match and no `.default` is supplied, NA is used:
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
)
#> [1] NA NA NA NA "fizz"
#> [6] NA "buzz" NA NA "fizz"
#> [11] NA NA NA "buzz" "fizz"
#> [16] NA NA NA NA "fizz"
#> [21] "buzz" NA NA NA "fizz"
#> [26] NA NA "buzz" NA "fizz"
#> [31] NA NA NA NA "fizz buzz"
#> [36] NA NA NA NA "fizz"
#> [41] NA "buzz" NA NA "fizz"
#> [46] NA NA NA "buzz" "fizz"
#> [51] NA NA NA NA "fizz"
#> [56] "buzz" NA NA NA "fizz"
#> [61] NA NA "buzz" NA "fizz"
#> [66] NA NA NA NA "fizz buzz"
# Note that `NA` values on the LHS are treated like `FALSE` and will be
# assigned the `.default` value. You must handle them explicitly if you
# want to use a different value. The exact way to handle missing values is
# dependent on the set of LHS conditions you use.
x[2:4] <- NA_real_
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
is.na(x) ~ "nope",
.default = as.character(x)
)
#> [1] "1" "nope" "nope" "nope" "fizz"
#> [6] "6" "buzz" "8" "9" "fizz"
#> [11] "11" "12" "13" "buzz" "fizz"
#> [16] "16" "17" "18" "19" "fizz"
#> [21] "buzz" "22" "23" "24" "fizz"
#> [26] "26" "27" "buzz" "29" "fizz"
#> [31] "31" "32" "33" "34" "fizz buzz"
#> [36] "36" "37" "38" "39" "fizz"
#> [41] "41" "buzz" "43" "44" "fizz"
#> [46] "46" "47" "48" "buzz" "fizz"
#> [51] "51" "52" "53" "54" "fizz"
#> [56] "buzz" "57" "58" "59" "fizz"
#> [61] "61" "62" "buzz" "64" "fizz"
#> [66] "66" "67" "68" "69" "fizz buzz"
# `case_when()` evaluates all RHS expressions, and then constructs its
# result by extracting the selected (via the LHS expressions) parts.
# In particular `NaN`s are produced in this case:
y <- seq(-2, 2, by = .5)
case_when(
y >= 0 ~ sqrt(y),
.default = y
)
#> Warning: NaNs produced
#> [1] -2.0000000 -1.5000000 -1.0000000 -0.5000000 0.0000000 0.7071068
#> [7] 1.0000000 1.2247449 1.4142136
# `case_when()` is particularly useful inside `mutate()` when you want to
# create a new variable that relies on a complex combination of existing
# variables
starwars %>%
select(name:mass, gender, species) %>%
mutate(
type = case_when(
height > 200 | mass > 200 ~ "large",
species == "Droid" ~ "robot",
.default = "other"
)
)
#> # A tibble: 87 × 6
#> name height mass gender species type
#> <chr> <int> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywalker 172 77 masculine Human other
#> 2 C-3PO 167 75 masculine Droid robot
#> 3 R2-D2 96 32 masculine Droid robot
#> 4 Darth Vader 202 136 masculine Human large
#> 5 Leia Organa 150 49 feminine Human other
#> 6 Owen Lars 178 120 masculine Human other
#> 7 Beru Whitesun Lars 165 75 feminine Human other
#> 8 R5-D4 97 32 masculine Droid robot
#> 9 Biggs Darklighter 183 84 masculine Human other
#> 10 Obi-Wan Kenobi 182 77 masculine Human other
#> # ℹ 77 more rows
# `case_when()` is not a tidy eval function. If you'd like to reuse
# the same patterns, extract the `case_when()` call in a normal
# function:
case_character_type <- function(height, mass, species) {
case_when(
height > 200 | mass > 200 ~ "large",
species == "Droid" ~ "robot",
.default = "other"
)
}
case_character_type(150, 250, "Droid")
#> [1] "large"
case_character_type(150, 150, "Droid")
#> [1] "robot"
# Such functions can be used inside `mutate()` as well:
starwars %>%
mutate(type = case_character_type(height, mass, species)) %>%
pull(type)
#> [1] "other" "robot" "robot" "large" "other" "other" "other" "robot"
#> [9] "other" "other" "other" "other" "large" "other" "other" "large"
#> [17] "other" "other" "other" "other" "other" "robot" "other" "other"
#> [25] "other" "other" "other" "other" "other" "other" "other" "other"
#> [33] "other" "other" "other" "large" "large" "other" "other" "other"
#> [41] "other" "other" "other" "other" "other" "other" "other" "other"
#> [49] "other" "other" "other" "other" "other" "other" "other" "large"
#> [57] "other" "other" "other" "other" "other" "other" "other" "other"
#> [65] "other" "other" "other" "other" "other" "other" "large" "large"
#> [73] "other" "robot" "other" "other" "other" "large" "large" "other"
#> [81] "other" "large" "other" "other" "other" "robot" "other"
# `case_when()` ignores `NULL` inputs. This is useful when you'd
# like to use a pattern only under certain conditions. Here we'll
# take advantage of the fact that `if` returns `NULL` when there is
# no `else` clause:
case_character_type <- function(height, mass, species, robots = TRUE) {
case_when(
height > 200 | mass > 200 ~ "large",
if (robots) species == "Droid" ~ "robot",
.default = "other"
)
}
starwars %>%
mutate(type = case_character_type(height, mass, species, robots = FALSE)) %>%
pull(type)
#> [1] "other" "other" "other" "large" "other" "other" "other" "other"
#> [9] "other" "other" "other" "other" "large" "other" "other" "large"
#> [17] "other" "other" "other" "other" "other" "other" "other" "other"
#> [25] "other" "other" "other" "other" "other" "other" "other" "other"
#> [33] "other" "other" "other" "large" "large" "other" "other" "other"
#> [41] "other" "other" "other" "other" "other" "other" "other" "other"
#> [49] "other" "other" "other" "other" "other" "other" "other" "large"
#> [57] "other" "other" "other" "other" "other" "other" "other" "other"
#> [65] "other" "other" "other" "other" "other" "other" "large" "large"
#> [73] "other" "other" "other" "other" "other" "large" "large" "other"
#> [81] "other" "large" "other" "other" "other" "other" "other"