% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/case-when.R
\name{case-and-replace-when}
\alias{case-and-replace-when}
\alias{case_when}
\alias{replace_when}
\title{A general vectorised if-else}
\usage{
case_when(
  ...,
  .default = NULL,
  .unmatched = "default",
  .ptype = NULL,
  .size = NULL
)

replace_when(x, ...)
}
\arguments{
\item{...}{<\code{\link[rlang:dyn-dots]{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.

For \code{case_when()}:
\itemize{
\item The LHS inputs must be logical vectors. For backwards compatibility,
scalars are \link[vctrs:theory-faq-recycling]{recycled}, but we no longer
recommend supplying scalars.
\item The RHS inputs will be \link[vctrs:theory-faq-coercion]{cast} to their common
type, and will be \link[vctrs:theory-faq-recycling]{recycled} to the common
size of the LHS inputs.
}

For \code{replace_when()}:
\itemize{
\item The LHS inputs must be logical vectors the same size as \code{x}.
\item The RHS inputs will be \link[vctrs:theory-faq-coercion]{cast} to the type of
\code{x} and \link[vctrs:theory-faq-recycling]{recycled} to the size of \code{x}.
}

\code{NULL} inputs are ignored.}

\item{.default}{The value used when all of the LHS inputs return either
\code{FALSE} or \code{NA}.
\itemize{
\item If \code{NULL}, the default, a missing value will be used.
\item If provided, \code{.default} will follow the same type and size rules as the
RHS inputs.
}

\code{NA} values in the LHS conditions are treated like \code{FALSE}, meaning that
the result at those locations will be assigned the \code{.default} value. To
handle missing values in the conditions differently, you must explicitly
catch them with another condition before they fall through to the
\code{.default}. This typically involves some variation of \code{is.na(x) ~ value}
tailored to your usage of \code{case_when()}.}

\item{.unmatched}{Handling of unmatched locations.

One of:
\itemize{
\item \code{"default"} to use \code{.default} in unmatched locations.
\item \code{"error"} to error when there are unmatched locations.
}}

\item{.ptype}{An optional prototype declaring the desired output type. If
supplied, this overrides the common type of the RHS inputs.}

\item{.size}{An optional size declaring the desired output size. If supplied,
this overrides the common size computed from the LHS inputs.}

\item{x}{A vector.}
}
\value{
For \code{case_when()}, a new vector where the size is the common size of the LHS
inputs, the type is the common type of the RHS inputs, and the names
correspond to the names of the RHS elements used in the result.

For \code{replace_when()}, an updated version of \code{x}, with the same size, type,
and names as \code{x}.
}
\description{
\code{case_when()} and \code{replace_when()} are two forms of vectorized \code{\link[=if_else]{if_else()}}.
They work by evaluating each case sequentially and using the first match for
each element to determine the corresponding value in the output vector.
\itemize{
\item Use \code{case_when()} when creating an entirely new vector.
\item Use \code{replace_when()} when partially updating an existing vector.
}

If you are just replacing a few values within an existing vector, then
\code{replace_when()} is always a better choice because it is type stable, size
stable, pipes better, and better expresses intent.

A major difference between the two functions is what happens when no cases
match:
\itemize{
\item \code{case_when()} falls through to a \code{.default} as a final "else" statement.
\item \code{replace_when()} retains the original values from \code{x}.
}

See \code{vignette("recoding-replacing")} for more examples.
}
\examples{
x <- 1:70
case_when(
  x \%\% 35 == 0 ~ "fizz buzz",
  x \%\% 5 == 0 ~ "fizz",
  x \%\% 7 == 0 ~ "buzz",
  .default = as.character(x)
)

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

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

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

# `case_when()` is not a replacement for basic if/else control flow. When
# you have a single scalar condition, using if/else is faster, simpler to
# reason about, and is lazy on the branch that isn't run. For example, this
# seems to work:
x <- "value"
case_when(is.character(x) ~ x, .default = "not-a-character")

# Until `x` is a non-character type
x <- 1
try(case_when(is.character(x) ~ x, .default = "not-a-character"))

# Instead, you should use if/else
if (is.character(x)) {
  y <- x
} else {
  y <- "not-a-character"
}
y

# If you believe that you've covered every possible case, then set
# `.unmatched = "error"` rather than supplying a `.default`. This adds an
# extra layer of safety to `case_when()` and is particularly useful when you
# have a series of complex expressions!
set.seed(123)
x <- sample(50)

# Oops, we forgot to handle `50`
try(case_when(
  x < 10 ~ "ten",
  x < 20 ~ "twenty",
  x < 30 ~ "thirty",
  x < 40 ~ "forty",
  x < 50 ~ "fifty",
  .unmatched = "error"
))

case_when(
  x < 10 ~ "ten",
  x < 20 ~ "twenty",
  x < 30 ~ "thirty",
  x < 40 ~ "forty",
  x <= 50 ~ "fifty",
  .unmatched = "error"
)

# Note that `NA` is considered unmatched and must be handled with its own
# explicit case, even if that case just propagates the missing value!
x[c(2, 5)] <- NA

case_when(
  x < 10 ~ "ten",
  x < 20 ~ "twenty",
  x < 30 ~ "thirty",
  x < 40 ~ "forty",
  x <= 50 ~ "fifty",
  is.na(x) ~ NA,
  .unmatched = "error"
)

# `replace_when()` is useful when you're updating an existing vector,
# rather than creating an entirely new one. Note the so-far unused "puppy"
# factor level:
pets <- tibble(
  name = c("Max", "Bella", "Chuck", "Luna", "Cooper"),
  type = factor(
    c("dog", "dog", "cat", "dog", "cat"),
    levels = c("dog", "cat", "puppy")
  ),
  age = c(1, 3, 5, 2, 4)
)

# We can replace some values with `"puppy"` based on arbitrary conditions.
# Even though we are using a character `"puppy"` value, `replace_when()` will
# automatically cast it to the factor type of `type` for us.
pets |>
  mutate(
    type = replace_when(type, type == "dog" & age <= 2 ~ "puppy")
  )

# Compare that with this `case_when()` call, which loses the factor class.
# It's always better to use `replace_when()` when updating a few values in
# an existing vector!
pets |>
  mutate(
    type = case_when(type == "dog" & age <= 2 ~ "puppy", .default = type)
  )

# `case_when()` and `replace_when()` evaluate all RHS expressions, and then
# construct their result by extracting the selected (via the LHS expressions)
# parts. For example, `NaN`s are produced here because `sqrt(y)` is evaluated
# on all of `y`, not just where `y >= 0`.
y <- seq(-2, 2, by = .5)
replace_when(y, y >= 0 ~ sqrt(y))

# These functions are 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"
    )
  )

# `case_when()` is not a tidy eval function. If you'd like to reuse
# the same patterns, extract the `case_when()` call into 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")
case_character_type(150, 150, "Droid")

# Such functions can be used inside `mutate()` as well:
starwars |>
  mutate(type = case_character_type(height, mass, species)) |>
  pull(type)

# `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)

# `replace_when()` can also be used in combination with `pick()` to
# conditionally mutate rows within multiple columns using a single condition.
# Here `replace_when()` returns a data frame with new `species` and `name`
# columns, which `mutate()` then automatically unpacks.
starwars |>
  select(homeworld, species, name) |>
  mutate(replace_when(
    pick(species, name),
    homeworld == "Tatooine" ~ tibble(
      species = "Tatooinese",
      name = paste(name, "(Tatooine)")
    )
  ))
}
\seealso{
\code{\link[=recode_values]{recode_values()}}, \code{\link[vctrs:vec-case-and-replace]{vctrs::vec_case_when()}}
}
