<- if (TRUE) 3
x
x#> [1] 3
Control Flow
IF
Syntax:
if (condition) true_action
if (condition) true_action else false_action
Return value of if
<- if (FALSE) 3
y
y#> NULL
if(NA) 3
#> Error in if (NA) 3: missing value where TRUE/FALSE needed
Note that, however:
is.logical(NA)
#> [1] TRUE
Use case of NULL
from if (FALSE)
c()
and paste()
drop NULL
inputs, this allows for a compact expression of certain idioms:
<- function(name, birthday = FALSE) {
greet 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
<- 1:10
x
ifelse(x %% 5 == 0, "XXX", as.character(x))
#> [1] "1" "2" "3" "4" "XXX" "6" "7" "8" "9" "XXX"
Test Vectorized IF
library(emo)
<- 1:10 x
If x
can be divided by:
3 return π§
5 return π
otherwise, return
x
if
with Loop
<- function(x) {
f_loopif
<- character(length(x))
out
for (i in seq_along(x)) {
<- if (x[i] %% 3 == 0) {
out[i]
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
<- function(x) {
f_ifelse
ifelse(x %% 3 == 0, ji("cheese"),
ifelse(x %% 5 == 0, ji("car"), x)
)
}
f_ifelse(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"
Logical Subsetting
<- function(x) {
f_subset
<- character(length(x))
out
# Assign value to location where `x` is:
%% 3 == 0] <- ji("cheese") # divided by 3
out[x %% 5 == 0] <- ji("car") # divided by 5
out[x == ""] <- x[out == ""] # and the rest
out[out
out
}
f_subset(x)
#> [1] "1" "2" "π§" "4" "π" "π§" "7" "8" "π§" "π"
case_when
<- function(x) {
f_case_when ::case_when(
dplyr%% 3 == 0 ~ as.character(ji("cheese")),
x %% 5 == 0 ~ as.character(ji("car")),
x 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.
<- function(x) {
mark_ctrflow ::mark(
benchloopif = 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)
<- mark_ctrflow(1:10)
results
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.2
::autoplot(results)
ggplot2#> 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.