Home > OS >  How to use ifelse () correctly in R?
How to use ifelse () correctly in R?

Time:02-03

Given the following code:

a <- 3
colors <-  ifelse(a == 3,
c("#004B93", "#389DC3", "#6DBE99"),
ifelse(a == 2, c("#004B93", "#389DC3"), c("#000000", "#000000", "#000000")))

My expectation ist to get something like

> colors 
[1] "#004B93" "#389DC3" "#6DBE99"

But what I get is

> colors
[1] "#004B93"

What am I doing wrong?

CodePudding user response:

You can also use if else statements for check the conditions inside R.

For that you can do the same logic as I checked for you.

a <- 3
colors <- if(a == 3) {
c("#004B93", "#389DC3", "#6DBE99")
} else if (a == 2) {
c("#004B93", "#389DC3")
} else {
c("#000000", "#000000", "#000000")
}

print(colors)

Output Result :

[1] "#004B93" "#389DC3" "#6DBE99"

CodePudding user response:

Here's how I would write this:

possible_colors = list(
    c("#000000", "#000000", "#000000"),
    c("#004B93", "#389DC3"),
    c("#004B93", "#389DC3", "#6DBE99")
)

colors = if (a < 1L || a > 3L) list[[1L]] else list[[a]]

This assumes that a only has integer values. Adjust the condition of the if accordingly if that is not the case.

If you know that a will never be any value except 1, 2 or 3, you can omit the if completely and directly write colors = possible_colors[[a]].

CodePudding user response:

ifelse is a command that is usually better to avoid.

Why? Because

ifelse(TRUE, 1:3, 4:6)

returns 1 and not 1:3.

In fact, ifelse wants the output to be the same length as the test. Above TRUE is a vector of length 1, while the candidate output1:3 is of length 3. However, ifelse does not raise any warning, it just cuts the vector 1:3.

There is rationale behind this, and this example should explain it:

ifelse(c(TRUE, FALSE, TRUE), yes = 1:3, no = 4:6)
[1] 1 5 3

True values are taken from the yes argument and false values are taken from the no argument. If you really want this sophisticate behaviour, ifelse can be extremely powerful, but this is rarely what you want.

The good news is that R is a functional language, thus everything is a function, including the if/else construct. Hence, to get the intended result from

ret <- ifelse(TRUE, 1:3, 4:6)

rewrite it like so:

ret <- if(TRUE) 1:3 else 4:6

and ret is 1:3

Do you need nested conditions? No problem.

a <- 1 
ret <- if(a == 1) 1:3 else (if(a == 2) 4:6 else 7:9)

The line above emphasises the functional approach, where I replace the expression 4:6 with the bracketed one, but it can be shortened to:

ret <- if(a == 1) 1:3 else if(a == 2) 4:6 else 7:9

However, let me note that for this type of tasks, there is a dedicated R function: switch.

ret <- switch(a, 1:3, 4:6, 7:9)

Counting after a, the first, second, etc. argument is chosen depending on the value of a, thus:

a <- 2
switch(a, 1:3, 4:6, 7:9)
# 4:6

As for your case:

a <- 3
switch(a, 
       c("#000000", "#000000", "#000000"),
       c("#004B93", "#389DC3"), 
       c("#004B93", "#389DC3", "#6DBE99"))

# [1] "#004B93" "#389DC3" "#6DBE99"
  • Related