dplyr::if_else

if_else(condition, true, false, missing = NULL, ..., ptype = NULL, size = deprecated())
Returns: vector · Updated April 6, 2026 · Tidyverse
dplyr r tidyverse data-wrangling conditionals

if_else() is dplyr’s type-strict vectorized if-else. It evaluates a logical condition element-wise and returns corresponding values from a true branch and a false branch. The key difference from base R’s ifelse() is that if_else() enforces type consistency, handles NA values in the condition explicitly, and preserves types like factors that ifelse() would destroy.

Basic Syntax

library(dplyr)

x <- c(-5:5, NA)

if_else(x < 0, "negative", "positive")
#>  [1] "negative" "negative" "negative" "negative" "negative" "positive"
#>  [7] "positive" "positive" "positive" "positive" "positive" NA

The condition must be a logical vector. The true and false arguments are recycled to match the size of condition.

Handling NAs with the missing Argument

Base ifelse() passes NA values through from the condition. if_else() gives you explicit control with the missing argument:

x <- c(-5:5, NA)

# Without missing — NA stays NA
if_else(x < 0, "negative", "positive")
#>  [1] "negative" "negative" "negative" "negative" "negative" "positive"
#>  [7] "positive" "positive" "positive" "positive" "positive" NA

# With missing — explicit label for NA condition values
if_else(x < 0, "negative", "positive", missing = "unknown")
#>  [1] "negative" "negative" "negative" "negative" "negative" "positive"
#>  [7] "positive" "positive" "positive" "positive" "positive" "unknown"

Type Strictness

if_else() requires true and false to be coercible to a common type. If they can’t be reconciled, it throws an error:

# This works — both sides are numeric
if_else(c(TRUE, FALSE), 1, 0)
#> [1] 1 0

# This throws an error — can't reconcile integer and character
if_else(c(TRUE, FALSE), 1, "zero")
#> Error: `true` and `false` must be compatible types.

This is a feature, not a bug. Type coercion in ifelse() often produces surprising results silently.

Factor Preservation

This is where if_else() genuinely improves on ifelse(). Base ifelse() with factors strips the levels and returns integers:

x <- factor(c("a", "b", "c", "a"))

# Base ifelse — factors become integers
ifelse(x == "a", x, NA)
#> [1]  1 NA NA  1   # integers, not "a"

# dplyr if_else — factors are preserved
if_else(x == "a", x, NA)
#> [1] a    <NA> <NA> a
#> Levels: a b c

Inside mutate()

if_else() is most commonly used inside mutate() to conditionally create or transform columns:

starwars |>
  mutate(
    height_category = if_else(height < 100, "short", "tall"),
    .keep = "used"
  )
#> # A tibble: 87 × 2
#>   height height_category
#>    <int> <chr>
#> 1     172 tall
#> 2     167 tall
#> 3      96 short
#> 4     202 tall
#> ...

Specifying Output Type with ptype

If you need a specific output type, you can force it with the ptype argument, which overrides the automatic common-type detection:

# Force character output even when inputs are coercible
if_else(c(TRUE, FALSE), "yes", "no", ptype = character())
#> [1] "yes" "no"

if_else vs ifelse

ifelse()if_else()
Type coercionSilent, often surprisingStrict — throws error
NA in conditionPasses throughmissing parameter
Factor preservationDrops to integersPreserves levels
SpeedSlowerFaster (in dplyr context)
LocationBase Rdplyr

For new dplyr-based code, if_else() is almost always the better choice. Reserve ifelse() for quick scripts where type strictness doesn’t matter.

For multiple conditions, case_when() handles more branches cleanly, but if_else() is the right tool for a simple binary choice.

See Also

  • base R ifelse — the base R alternative
  • dplyr::case_when — multi-branch conditional, useful when you have more than two cases
  • dplyr::mutate — creating and transforming columns, where if_else() is most often used