dplyr coalesce() in R: First Non-NA Value Across Vectors
The coalesce() function in dplyr returns the first non-NA value across multiple vectors element-wise. It is the SQL COALESCE ported to R, and the cleanest way to fill missing values with a fallback.
coalesce(x, 0) # NA in x becomes 0 coalesce(x, y) # NA in x replaced from y elementwise coalesce(x, y, z) # tries x, then y, then z coalesce(c(1, NA, 3), c(99, 99, 99)) # c(1, 99, 3) df |> mutate(name = coalesce(name, "anonymous")) tidyr::replace_na(x, 0) # equivalent for scalar fallback
Need explanation? Read on for examples and pitfalls.
What coalesce() does in one sentence
coalesce(...) returns a vector where each element is the first non-NA value taken from the corresponding element of each input vector. Inputs must be the same length (or recyclable) and the same type.
This is the canonical "fill NA with a fallback" function. Use it whenever you have a primary source and one or more backup sources.
Syntax
coalesce(...). Variadic; pass any number of vectors. Returns the first non-NA per element.
coalesce(x, 0) and tidyr::replace_na(x, 0) are equivalent. Pick the one your pipeline already uses. coalesce shines when the fallback is itself a vector.Five common patterns
1. Scalar fallback
2. Vector fallback (column-wise)
3. Chain of fallbacks
The first non-NA per position wins.
4. Use inside mutate
5. Coalesce columns of mixed sources
A staple in data-merging pipelines: prefer column A's value, fall back to column B if NA.
coalesce(x, y, z, default) is the idiomatic way to express "pick the first available source". Common scenarios: primary key from main table, fallback to a join key, default to a constant if both are NA. SQL programmers know this pattern; dplyr ports it cleanly to R.coalesce() vs replace_na() vs ifelse() vs if_else()
Four ways to handle NAs in R, with different shapes.
| Function | Use case | Shape |
|---|---|---|
coalesce(x, y) |
First non-NA across vectors | x, y same length |
tidyr::replace_na(x, v) |
NA in x -> v | x is vector, v scalar |
dplyr::if_else(is.na(x), y, x) |
Equivalent to coalesce(x, y) | More verbose |
base::ifelse(is.na(x), y, x) |
Same; less type-strict | Less safe |
dplyr::na_if(x, val) |
Convert val to NA | Inverse direction |
When to use which:
coalescefor fallback-chain semantics.replace_nafor one scalar default.if_elseif you need explicit logic.na_iffor converting sentinels TO NA.
A practical workflow
The most common coalesce pattern is "merge two columns": prefer A, fall back to B.
Three-source merge: try the new price column, then the legacy column, then default to 0. One line replaces a nested if_else chain.
For text fields, coalesce with a placeholder is the classic "show something or else 'unknown'" idiom:
Common pitfalls
Pitfall 1: type mismatch. coalesce(c(1, 2), c("a", "b")) errors because numeric and character don't share a type. Convert one side first.
Pitfall 2: scalar recycling surprises. coalesce(c(1, NA, 3), 0) works because the scalar 0 recycles. But coalesce(c(1, NA, 3), c(0, 0)) errors because length 2 doesn't recycle to length 3. Use a length-1 fallback or a length-matching vector.
coalesce() returns NA only if EVERY input is NA at that position. If any input has a non-NA value, that wins. To test "ALL are NA", use all(is.na(c(x[i], y[i], z[i]))) per row, or rowwise() + mutate.Try it yourself
Try it: Merge price_new and price_old, falling back to 0 when both are NA. Save to ex_price.
Click to reveal solution
Explanation: coalesce tries price_new first; if NA, falls to price_old; if still NA, the scalar 0.
Related dplyr functions
After mastering coalesce, look at:
tidyr::replace_na(): scalar fallback onlydplyr::na_if(): inverse (value -> NA)dplyr::if_else(): 2-way conditionaldplyr::case_when(): multi-condition mappingdplyr::across(): apply coalesce across multiple columnsmean(x, na.rm = TRUE): ignoring NAs in aggregates is a different operation
For multi-column NA filling, mutate(across(everything(), ~ coalesce(.x, 0))) cleans every column at once.
FAQ
What does coalesce do in dplyr?
coalesce(x, y, ...) returns the first non-NA value across the inputs, element-wise. Used to fill NAs with a fallback vector or scalar.
What is the difference between coalesce and replace_na?
replace_na(x, v) replaces NA with a scalar v. coalesce(x, v) does the same. coalesce additionally allows multiple fallback vectors and a chain. Use replace_na for simple scalar; coalesce for vector chains.
Can coalesce handle multiple fallback columns?
Yes. coalesce(x, y, z, default) tries each in order. The first non-NA per position wins.
What happens if all inputs are NA?
coalesce returns NA. To convert to a default, append a non-NA scalar at the end: coalesce(x, y, 0).
Does coalesce work on character columns?
Yes. All inputs must share a type. coalesce(c("a", NA, "c"), "unknown") works; coalesce(c("a", NA), 1) errors.