x <- if (TRUE) 3
x
#> [1] 3Control Flow
IF
Syntax:
if (condition) true_action
if (condition) true_action else false_actionReturn value of if
y <- if (FALSE) 3
y
#> NULLif(NA) 3
#> Error in if (NA) 3: missing value where TRUE/FALSE neededNote that, however:
is.logical(NA)
#> [1] TRUEUse case of NULL from if (FALSE)
c() and paste() drop NULL inputs, this allows for a compact expression of certain idioms:
greet <- function(name, birthday = FALSE) {
paste0(
"Hi ", name,
if (birthday) " and HAPPY BIRTHDAY"
)
}
greet("A")
#> [1] "Hi A"
greet("A", birthday = TRUE)
#> [1] "Hi A and HAPPY BIRTHDAY"Vectorized IF
x <- 1:10
ifelse(x %% 5 == 0, "XXX", as.character(x))
#> [1] "1" "2" "3" "4" "XXX" "6" "7" "8" "9" "XXX"Test Vectorized IF
library(emo)x <- 1:10If x can be divided by:
3 return π§
5 return π
otherwise, return
x
if with Loop
f_loopif <- function(x) {
out <- character(length(x))
for (i in seq_along(x)) {
out[i] <- if (x[i] %% 3 == 0) {
ji("cheese")
} else if (x[i] %% 5 == 0) {
ji("car")
} else {
x[i]
}
}
out
}
f_loopif(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"ifelse approach
f_ifelse <- function(x) {
ifelse(x %% 3 == 0, ji("cheese"),
ifelse(x %% 5 == 0, ji("car"), x)
)
}
f_ifelse(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"Logical Subsetting
f_subset <- function(x) {
out <- character(length(x))
# Assign value to location where `x` is:
out[x %% 3 == 0] <- ji("cheese") # divided by 3
out[x %% 5 == 0] <- ji("car") # divided by 5
out[out == ""] <- x[out == ""] # and the rest
out
}
f_subset(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"case_when
f_case_when <- function(x) {
dplyr::case_when(
x %% 3 == 0 ~ as.character(ji("cheese")),
x %% 5 == 0 ~ as.character(ji("car")),
TRUE ~ as.character(x)
)
}
f_case_when(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"Benchmark
Letβs measure a performance of 3 approachs of control flow over vector.
mark_ctrflow <- function(x) {
bench::mark(
loopif = f_loopif(x), # Loop with if
ifelse = f_ifelse(x), # ifelse
subset = f_subset(x), # subset
case_when = f_case_when(x) # dplyr::case_when()
)
}set.seed(123)
results <- mark_ctrflow(1:10)
results
#> # A tibble: 4 Γ 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 loopif 184.1Β΅s 241.9Β΅s 3949. 165.8KB 13.9
#> 2 ifelse 89.8Β΅s 101.9Β΅s 7569. 88.1KB 10.4
#> 3 subset 68.9Β΅s 83.9Β΅s 10979. 110.4KB 13.9
#> 4 case_when 198.5Β΅s 247.6Β΅s 3149. 76.2KB 17.2ggplot2::autoplot(results)
#> Loading required namespace: tidyr
Conclusion
It seems like using vector subsetting is the fastest, followed by ifelse, and then dplyr::case_when() or looping over if approach is about the same.