Why can't R's ifelse statements return vectors?
RIf StatementVectorizationR FaqR Problem Overview
I've found R's ifelse statements to be pretty handy from time to time. For example:
ifelse(TRUE,1,2)
# [1] 1
ifelse(FALSE,1,2)
# [1] 2
But I'm somewhat confused by the following behavior.
ifelse(TRUE,c(1,2),c(3,4))
# [1] 1
ifelse(FALSE,c(1,2),c(3,4))
# [1] 3
Is this a design choice that's above my paygrade?
R Solutions
Solution 1 - R
The documentation for ifelse
states:
> ifelse
returns a value with the same
> shape as test
which is filled with
> elements selected from either yes
or
> no
depending on whether the element
> of test
is TRUE
or FALSE
.
Since you are passing test values of length 1, you are getting results of length 1. If you pass longer test vectors, you will get longer results:
> ifelse(c(TRUE, FALSE), c(1, 2), c(3, 4))
[1] 1 4
So ifelse
is intended for the specific purpose of testing a vector of booleans and returning a vector of the same length, filled with elements taken from the (vector) yes
and no
arguments.
It is a common confusion, because of the function's name, to use this when really you want just a normal if () {} else {}
construction instead.
Solution 2 - R
I bet you want a simple if
statement instead of ifelse
- in R, if
isn't just a control-flow structure, it can return a value:
> if(TRUE) c(1,2) else c(3,4)
[1] 1 2
> if(FALSE) c(1,2) else c(3,4)
[1] 3 4
Solution 3 - R
Note that you can circumvent the problem if you assign the result inside the ifelse
:
ifelse(TRUE, a <- c(1,2), a <- c(3,4))
a
# [1] 1 2
ifelse(FALSE, a <- c(1,2), a <- c(3,4))
a
# [1] 3 4
Solution 4 - R
use `if`, e.g.
> `if`(T,1:3,2:4)
[1] 1 2 3
Solution 5 - R
yeah, I think ifelse() is really designed for when you have a big long vector of tests and want to map each to one of two options. For example, I often do colors for plot() in this way:
plot(x,y, col = ifelse(x>2, 'red', 'blue'))
If you had a big long vector of tests but wanted pairs for outputs, you could use sapply()
or plyr
's llply()
or something, perhaps.
Solution 6 - R
Sometimes the user just needs a switch
statement instead of an ifelse
. In that case:
condition <- TRUE
switch(2-condition, c(1, 2), c(3, 4))
#### [1] 1 2
(which is another syntax option of Ken Williams's answer)
Solution 7 - R
Here is an approach similar to that suggested by Cath, but it can work with existing pre-assigned vectors
It is based around using the get()
like so:
a <- c(1,2)
b <- c(3,4)
get(ifelse(TRUE, "a", "b"))
# [1] 1 2
Solution 8 - R
In your case, using if_else
from dplyr
would have been helpful: if_else
is more strict than ifelse
, and throws an error for your case:
library(dplyr)
if_else(TRUE,c(1,2),c(3,4))
#> `true` must be length 1 (length of `condition`), not 2
Solution 9 - R
Found on everydropr:
ifelse(rep(TRUE, length(c(1,2))), c(1,2),c(3,4))
#>[1] 1 2
Can replicate the result of your condition to return the desired length