In this post, I will talk about the ifelse function, which behaviour can be easily misunderstood, as pointed out in my latest question on SO. I will try to show how it can be used, and misued. We will also check if it is as fast as we could expect from a vectorized base function of R.
How can it be used?
The first example comes directly from the R documentation:
x &amp;lt;- c(6:-4)
sqrt(x) #- gives warning
## Warning in sqrt(x): NaNs produced
## [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000 NaN NaN
## [10] NaN NaN
sqrt(ifelse(x &amp;gt;= 0, x, NA)) # no warning
## [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000 NA NA
## [10] NA NA
So, it can be used, for instance, to handle special cases, in a vectorized, succinct way.
The second example comes from the vignette of Rcpp Sugar:
foo &amp;lt;- function(x, y) {
ifelse(x &amp;lt; y, x*x, -(y*y))
}
foo(1:5, 5:1)
## [1] 1 4 -9 -4 -1
So, it can be used to construct a vector, by doing an element-wise comparison of two vectors, and specifying a custom output for each comparison.
A last example, just for the pleasure:
(a &amp;lt;- matrix(1:9, 3, 3))
## [,1] [,2] [,3]
## [1,] 1 4 7
## [2,] 2 5 8
## [3,] 3 6 9
ifelse(a %% 2 == 0, a, 0)
## [,1] [,2] [,3]
## [1,] 0 4 0
## [2,] 2 0 8
## [3,] 0 6 0
How can it be misused?
I think many people think they can use ifelse as a shorter way of writing an if-then-else statement (this is a mistake I made). For example, I use:
legend.pos &amp;lt;- ifelse(is.top, ifelse(is.right, &amp;quot;topright&amp;quot;, &amp;quot;topleft&amp;quot;),
ifelse(is.right, &amp;quot;bottomright&amp;quot;, &amp;quot;bottomleft&amp;quot;))
instead of:
if (is.top) {
if (is.right) {
legend.pos &amp;lt;- &amp;quot;topright&amp;quot;
} else {
legend.pos &amp;lt;- &amp;quot;topleft&amp;quot;
}
} else {
if (is.right) {
legend.pos &amp;lt;- &amp;quot;bottomright&amp;quot;
} else {
legend.pos &amp;lt;- &amp;quot;bottomleft&amp;quot;
}
}
That works, but this doesn’t:
ifelse(FALSE, 0, 1:5)
## [1] 1
Indeed, if you read carefully the R documentation, you see that ifelse is returning a vector of the same length and attributes as the condition (here, of length 1).
If you really want to use a more succinct notation, you could use
`if`(FALSE, 0, 1:5)
## [1] 1 2 3 4 5
If you’re not familiar with this notation, I suggest you read the chapter about functions in book Advanced R.
Benchmarks
Reimplementing ‘abs’
abs2 &amp;lt;- function(x) {
ifelse(x &amp;lt; 0, -x, x)
}
abs2(-5:5)
## [1] 5 4 3 2 1 0 1 2 3 4 5
library(microbenchmark)
x &amp;lt;- rnorm(1e4)