Quinten's answer is the simplest. I offer an alternative that is more defensive and declarative. (Translation: when I look at code that I wrote 6+ months ago, I often scratch my head wondering why certain things happen. Because of this, I tend to be a bit more "declarative" in certain steps to remind future-me of some steps.)
Background: ifelse
is not class safe, it does not fail/warn when the classes of yes=
and no=
are not the same. (It also silently dumps some object classes, c.f., How to prevent ifelse() from turning Date objects into numeric objects)
Examples:
ifelse(c(T,T), 1, "A")
# [1] 1 1
ifelse(c(T,F), 1, "A")
# [1] "1" "A"
ifelse(c(T,F), Sys.Date(), 1+Sys.Date())
# [1] 19443 19444
ifelse(c(T,F), Sys.time(), 1+Sys.time())
# [1] 1679933419 1679933420
dplyr::if_else
will fail since the classes are not the same, and this is good: it protects the user from unknowingly changing the class of the object. It requires that the user enforce this internally,
df %>%
mutate(x = if_else(is.na(x) & genes == 'A', 'Yes', x))
# Error in `mutate()`:
# ! Problem while computing `x = if_else(is.na(x) & genes == "A", "Yes", x)`.
# Caused by error in `if_else()`:
# ! `false` must be a character vector, not a double vector.
# Run `rlang::last_error()` to see where the error occurred.
df %>%
mutate(
x = as.character(x),
x = if_else(is.na(x) & genes == 'A', 'Yes', x)
)
# # A tibble: 3 × 3
# genes x y
# <chr> <chr> <dbl>
# 1 A Yes NA
# 2 B <NA> 3
# 3 C 4 4
(We might have used as.character(x)
directly within the if_else
as well.)
FYI, coalesce
is also useful here, though because of the dependency on genes
it cannot fully replace:
df %>%
mutate(
x = as.character(x),
x = if_else(genes == 'A', coalesce(x, 'Yes'), x)
)
The advantage of coalesce
is when there is no genes
condition, where it might instead look as simple as x = coalesce(x, "Yes")
.