Chapter 8 If Statements

If statements are a type of control flow structure. Control structures help us control how many times code is executed, and when it will be executed. This is helpful because sometime we only want our program to run if certain conditions are met.

8.1 If

In R we can also execute commands only if certain conditions are met using the if operator. This feature in R checks a logical value (<value>) and if it is TRUE then a sequence of commands within {} will be ran. If <value> is FALSE, then the commands inside of {} will not be ran.

if(<value>){
  # Commands 
}

Below we have an example. The code will only be executed if x is a positive number.

x = 3

if (x > 0) {
    type = "positive"
}
type
## [1] "positive"

In the example above we have a relational operator which returns a logical value. This logical value was equal to TRUE so the code was executed.

8.2 If Else

We can pair an if statement with an else value. After the else object we can define another sequence of commands inside of {}. The else value is paired with the immediate previous if statement. If this if statement is FALSE then the else code will run. If the if statement is TRUE then the else code will not be executed.

x = -3

if (class(x) == "numeric") {
    type = "number"
} else {
    type = "not a number"
}
type
## [1] "number"

8.3 Else If

Sometimes we will want to do a sequence of checks that are all related, and we will only want code to run if the previous if statements were FALSE and another criteria is TRUE. We can use else if to implement these rules.

x = -3


if (x > 0) {
    type = "positive"
} else if (x < 0) {
    type = "negative"
} else if (x == 0) {
    type = "zero"
} else {
    type = "Error"
}

type
## [1] "negative"

The command for the if statement will only run if x>0, and the rest of the code will not be implemented.

The first else if commands will only execute if the first if statement was FALSE and x<0.

The second else if commands will only execute if the previous else if and if statements were FALSE and x==0.

If the previous if statement and all previous else if statements are FALSE then the else code will be executed.

Here is another example with an if else chain.

Toyfun <- function(X, Y, Do) {
    if (Do == "Add") {
        Z = X + Y

    } else if (Do == "Subtract") {
        Z = X - Y

    } else if (Do == "Multiply") {
        Z = X * Y

    } else if (Do == "Penguin") {
        Z = c("<('' )")

    } else {
        Z = c(X, Y)
    }

    return(Z)
}
Toyfun(2, 4, "Add")
## [1] 6
Toyfun(2, 4, "Subtract")
## [1] -2
Toyfun(2, 4, "Penguin")
## [1] "<('' )"
Toyfun(2, 4, "Typo")
## [1] 2 4

8.4 Nested If Chains

We can make if-else chains nested within each other.

x <- 105
if (x > 0) {
    if (x > 100) {
        type = "large positive number"
    } else {
        type = "positive number"
    }
} else if (x < 0) {
    type = "negative number"
} else if (x == 0) {
    type = "zero"
} else {
    type = "Error"
}

type
## [1] "large positive number"

8.5 Ifelse

One of the critical things about if-statements is that they require that we use only ONE TRUE\FALSE value inside the condition that is checked. For example, consider the following:

x <- -3:3

if (x > 0) {
    type <- "positive"
} else {
    type <- "non-negative"
}
## Warning in if (x > 0) {: the condition has length > 1 and only the first
## element will be used
type
## [1] "non-negative"

When we run the above line of code we obtain a “Warning” message. Recall that warning messages are given when R compiled an expression, but the program suspects that the result was not the user wanted. A “Error” message is when the expression could not be compiled. In this case the expression x>0 produces a vector of logical values that has length greater than 1, when the condition for an if-statement is expecting a logical vector of length 1. If-statements are one of the few things in base R that are not automatically vectorized.

In R we are used to vectorized functions and operations. We say that a function or operation is vectorized when we can use the function or operation to every element in a vector in an efficient way. We saw a few examples of vectorized operations already in 2.2.2. Below we have an example of using a vectorized approach with a function, and non-vectorized approach.

# Non-vectorized
c(log(1), log(2), log(3))
## [1] 0.0000000 0.6931472 1.0986123
# Vectorized
log(1:3)
## [1] 0.0000000 0.6931472 1.0986123

In the the second call the function log() is efficiently applied to each element of the vector that is used as the input. The vectorized approach is efficiently computationally, and it is also efficient in typing. For much larger vectors it would be (human) time consuming to apply the log function to each element.

When we want to do a vectorized approach for if-statements we generally have two options. The first, and simplest, option is the ifelse() function. The first argument of this function is a logical value, the second and third arguments are what to do if the value is TRUE or FALSE, respectively. Be sure to check out the help file for this function!

x <- -3:3
type <- ifelse(x > 0, "positive", "non-positive")
type
## [1] "non-positive" "non-positive" "non-positive" "non-positive" "positive"    
## [6] "positive"     "positive"

Here is another example that we can use to find all cars in the mtcars data set that have a high horsepower and are fuel efficient, i.e. cars have mpg > 25 and hp > 60.

fast_efficient <- ifelse(mtcars$mpg > 25 & mtcars$hp > 60, TRUE,
    FALSE)
sum(fast_efficient)/length(fast_efficient)
## [1] 0.15625