x <- 10
if (x < 11) {
print(paste(x, "< 11"))
}[1] "10 < 11"
We introduced loops in the previous sections. This section will introduce the other powerful concept in programming: conditionals. Conditionals, as the name hints at, allow to execute code only if a condition is true. For example, if we would like to execute code only when the random \(\text{Uniform}(0, 1)\) variable is less than 0.5, then the code within the conditional will only execute with a probability of 50%. Conditionals therefore allow us to split our code into sections each of which will only be executed if a pre-specified condition is true.
A condition in R connects a left-hand-side with a right-hand-side by using one of the following operators:
==: True if and only if the value on the left-hand-side equals the value on the right-hand-side.>: True if and only if the value on the left-hand-side is greater than the value on the right-hand-side.>=: True if and only if the value on the left-hand-side is greater or equal to the value on the right-hand-side.<: True if and only if the value on the left-hand-side is less than the value on the right-hand-side.<=: True if and only if the value on the left-hand-side is less or equal to the value on the right-hand-side.!=: True if and only if the value on the left-hand-side is not equal to the value on the right-hand-side.!x: True if and only if x is FALSE.||: True if and only if either the left-hand-side is true, or the right-hand-side is true, or both are true. Not vectorised.|: True if and only if either the left-hand-side is true, or the right-hand-side is true, or both are true. Operates element-wise on arrays.&&: True if and only if both the left-hand-side and the right-hand-side are true. Not vectorised.&: True if and only if both the value on the left-hand-side and the value on the right-hand-side are true. Operates element-wise on arrays.When stating a conditional in natural language, we often use ‘if’ to denote that what we are talking about is a conditional. Similarly, we can use if in R to denote that the code in the following curly brackets should only be executed if the conditional statement within parentheses is true. Take the example below. The conditional in parentheses is testing whether x is less than eleven. Only if this is the case, will we see x < 11 printed out. Test this out by changing x to some other value that violates the condition in parentheses.
x <- 10
if (x < 11) {
print(paste(x, "< 11"))
}[1] "10 < 11"
While if statements are nice, what would we do if we want an ‘else’ or ‘otherwise’? In natural language we often connect ‘if’ with an ‘else’ or ‘otherwise’ to communicate that if the condition is not true, then something else will happen. For example, this years INKOM had options for both rainy and non-rainy days which would be translated as: “If it is raining, we meet in the MAC, else we meet in the park.”. ‘Else’ statements are powerful and thus also a part of programming. Just as an ‘if’ statement can be implemented using if, an ‘else’ can be implemented using else. Extending the example above, we can now also provide some text if x is not less than eleven. Try out what happens when you change x to some other number.
x <- 11
if (x < 11) {
print(paste(x, "< 11"))
} else {
print(paste(x, ">= 11"))
}[1] "11 >= 11"
Although we strongly suggest to stick to if and else, sometimes there is no way around futher complicating the code by using else if. else if is like an additional check. If the first if or a previous else if did not execute, then the condition within the parentheses in the current else if will be checked and the code in curly brackets will be executed if the condition is true.
x <- 13
if (x < 11) {
print(paste(x, "< 11"))
} else if (x == 11) {
print(paste(x, "= 11"))
} else {
print(paste(x, "> 11"))
}[1] "13 > 11"
We do not recommend the use of else if statements, because they quickly become complex and often better methods exist. Consider the example below. We could have continued in this way for ever, but usually there are better methods to do such comparisons. Throughout these notes, you are likely to find some of these better methods and we will try to point them out when they show up.
x <- 13
if (x == 1) {
print(paste(x, "= 1"))
} else if (x == 2) {
print(paste(x, "= 2"))
} else if (x == 3) {
print(paste(x, "= 3"))
} else if (x == 4) {
print(paste(x, "= 4"))
} else if (x == 5) {
print(paste(x, "= 5"))
} else if (x == 6) {
print(paste(x, "= 6"))
} else if (x == 7) {
print(paste(x, "= 7"))
} else if (x == 8) {
print(paste(x, "= 8"))
}We have already shown in a previous section that Booleans can be used to index into an array. Since conditionals, except && and ||, all operate element-wise, we can also apply a logical operator to a vector which will return a Boolean vector of the same length. So, if we want to filter a vector for values that are less than -0.9, then all we need to do is to write x[x < -0.9]. The condition inside the square brackets will return a Boolean vector that has TRUE at a position if and only if the corresponding element in x is less than -0.9, and thus the indexing will only return such values.
# logical operators work element-wise
x <- 1:10
x > 5 [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
N <- 1000
x <- runif(N, min = -1, max = 1)
x[x < -0.9] [1] -0.9851275 -0.9307787 -0.9534590 -0.9872281 -0.9151864 -0.9987364
[7] -0.9602205 -0.9105376 -0.9086754 -0.9834861 -0.9107445 -0.9690683
[13] -0.9489840 -0.9678495 -0.9028808 -0.9105382 -0.9697371 -0.9815941
[19] -0.9900724 -0.9015134 -0.9951028 -0.9939444 -0.9221433 -0.9725292
[25] -0.9399621 -0.9212674 -0.9122635 -0.9437736 -0.9783719 -0.9234693
[31] -0.9782699 -0.9386596 -0.9857937 -0.9211194 -0.9325814 -0.9499385
[37] -0.9312718 -0.9300480 -0.9365129 -0.9437293 -0.9575915 -0.9630824
[43] -0.9900142 -0.9149328
The element-wise operation of logical operators can also be used to calculate proportions, probabilities, and counts. For example, we could approximate the probability that x < -0.9 if \(x \sim \text{Uniform}(-1, 1)\). You will learn throughout the course what this statement means, but the theoretical answer would be \(0.05\), so 5%. The simulated answer, using N draws comes close to this, and the larger you choose N, the closer will the simulated answer be to the theoretical answer. The sum in the code below sums up all elements in the vector given to it. Since we are giving it a Boolean vector (because we have a logical operator applied to a vector), we are summing over Booleans. A TRUE is represented by a 1, while a FALSE is represented by a 0. Thus, the sum will return the number of elements in x that are less than -0.9. Dividing by N will then give the proportion which is approximately equal to the theoretical probability.
N <- 1000
x <- runif(N, min = -1, max = 1) # drawing N Uniform(-1, 1) numbers
probability <- sum(x < -0.9) / N
probability[1] 0.052
Care must be taken when applying logical operators to a vector and using the result in an if statement. Since the logical operator works elementwise, the conditional will return a vector. It is then not clear what this should mean for the if statement. Should the if statement be evaluated once per element in the vector? Only if all are true? Only if at least one element is true? Due to this ambiguity, R requires a single Boolean within the parentheses of an if statement. For example, the code below would throw an error. Do you see why?
# Throws the following error:
# Error in if (x > 5) { : the condition has length > 1
x <- 1:10
if (x > 5) {
print(paste(x, "> 5"))
}Since R requires a single Boolean within the parentheses of an if statement, we need some way to summarise the information in a vector of Booleans. For example, if we would like to return TRUE if and only if all elements in the Boolean vector are true, then we can use the all function, which takes a Boolean vector and returns TRUE if and only if all elements are TRUE.
x <- 1:10
all(x > 5)[1] FALSE
Sometimes we wish to execute an if statement as long as at least one element in the Boolean vector is TRUE. We can look ahead a bit and use a custom function to return TRUE if and only if at least one element in the Boolean vector is TRUE. We will discuss functions in more detail in the next section. Here, just note, that within the curly brackets after the function statement, it says return(sum(condition) >= 1) so the function returns sum(condition) >= 1 which is TRUE if and only if the sum over all Booleans in the Boolean vector is at least one. Since TRUE==1 and FALSE==0, this is only the case if at least one TRUE is in the Boolean vector.
at_least_one <- function(condition) {
return(sum(condition) >= 1)
}
x <- 1:10
at_least_one(x > 5)[1] TRUE
Conditionals become very powerful when they are connected together or negated. In natural language we often use ‘and’, ‘or’, and ‘not’. Similarly, in R we can use && and & for an ‘and’, || and | for an ‘or’, and ! for a ‘not’. The code below showcases each of them. Can you spot why the || will always be true?
x <- 10
if ((x < 15) && (x > 5)) {
print(paste("5 <", x, "< 15"))
}[1] "5 < 10 < 15"
x <- 10
if ((x > 5) || (x < 15)) {
print("I am always true, why?")
}[1] "I am always true, why?"
x <- 10
if (!(x > 5)) {
print(paste(x, "<= 5"))
}