4 Control Structures
Control structures in R allow you to control the flow of execution of a series of R expressions. Basically, control structures allow you to put some ‘logic’ into your R code, rather than just always executing the same R code every time.
4.1 Loops
Sometimes it is necessary to repeat a calculation multiple times, e.g. calculate the sum of each row of a matrix. You can use for loops to do this.
Lets assume a simple matrix with 5 rows and 3 columns. How would we generate the sum of each row?
We could do so, by calculating the sum step-by-step:
#build a dataframe
<- matrix(1:15, 5)
m m
[,1] [,2] [,3]
[1,] 1 6 11
[2,] 2 7 12
[3,] 3 8 13
[4,] 4 9 14
[5,] 5 10 15
#sum rows without a loop --> sum row 1
sum(m[1, ])
[1] 18
#sum rows without a loop --> sum row 2, ...
sum(m[2, ])
[1] 21
However, you will quickly see that this becomes quite tedious. So lets do this via a loop, in which we:
- define that we want to loop through rows 1 to 5 (1:5)
for (i in 1:5)
–> we say that we want to run a for loop, in which we name our variable i{}
–> defines the operation we want to loop through- in our case we want to sum a specific row (rows 1 through 5)
- so in i we store the numbers 1,2,3,4 and 5 and then run through the sum functions 5x
#run a loop to get the sum for all five rows
for (i in 1:5) {
print(sum(m[i, ]))
}
[1] 18
[1] 21
[1] 24
[1] 27
[1] 30
How would we store our results in a new dataframe and do not just print them to the screen? To do this, we:
- define a variable (named
results
) in which we store an empty vector withc()
. We need this empty vector to have something to store our results in while running the loop. - start the for loop and define i as before
- run the sum as before but now store the results for each iteration in
results
#store the results, Note that here, the variable results had to be created before as an empty vector.
<- c()
results for (i in 1:5) {
<- sum(m[i, ])
results[i]
}
results
[1] 18 21 24 27 30
Very often, there are some R packages or functions that are faster than loops.
The apply()
function is such an example. apply()
applies a function, i.e. sum, across a matrix.
However, since loops are also a very useful feature in bash or python it is useful to understand their general concept.
#way to do the same using the apply function (faster than loops)
<- apply(m, 1, sum)
results2 results2
[1] 18 21 24 27 30
Apply takes the following arguments:
- Our input, the matrix,
- The dimension of the matrix on which the function should be applied ((1 means rows and 2 means columns))
- apply(data, 1, mean) = apply mean on each row
- apply(data, 2, mean) = apply mean on each column
- The function we want to use. Here, the function sum is applied to each row of m.
Actually, there is a family of apply functions, depending on the object you pass as an input, and/or the object you want as output. You can read a brief tutorial under this link.
Two other examples are sapply and lapply, which work on lists.
4.2 if-else
One can also create a decision making structure in R. A decision making structure has at least one condition to be evaluated together with a statement or statements to be evaluated if the condition is TRUE, and optionally, other statements to be executed if the condition is FALSE, as we can see in the figure.
The test_expression has to be logical, i.e., it has to be a expression that, when evaluated, is either TRUE or FALSE. The logical operators listed above can be used to construct them. For example, we can use an if-else statement to check if a number is positive or negative,
#store a random number
<- -5
x
#write a loop and ask if our number is positive or negative
if (x > 0) {
print("Positive number")
else if (x == 0) {
} print("Number is zero")
else {
} print("Negative number")
}
[1] "Negative number"
Note that in the example there was an else if. In that way we can check more than 2 conditions.
This of course is a very simplistic example. But if-else statements can be useful if we want to deal with larger dataframes, i.e. our growth data.
Specifically, we want to:
- Create a new column, with the name
category
- If a value in our Root length column is equal to or smaller than 10, we want to say this category is small.
- If that is not the case, we want to say it is large
#apply ifelse
$category <- ifelse(growth_data$Rootlength<=10.0, "small", "large")
growth_data
#check the structure of our data
kable(growth_data) %>%
kable_styling() %>%
scroll_box(width = "700px", height = "400px")
SampleID | Nutrient | Condition | FW_shoot_mg | Rootlength | category |
---|---|---|---|---|---|
noP | noP | MgCl | 10.26 | 5.931015 | small |
noP | noP | MgCl | 6.52 | 5.743447 | small |
noP | noP | MgCl | 12.17 | 6.834720 | small |
noP | noP | MgCl | 11.37 | 6.742735 | small |
noP | noP | MgCl | 9.80 | 6.736886 | small |
noP | noP | MgCl | 3.75 | 4.236349 | small |
noP | noP | MgCl | 5.38 | 4.753485 | small |
noP | noP | MgCl | 4.53 | 5.532333 | small |
noP | noP | MgCl | 7.75 | 5.484364 | small |
noP | noP | MgCl | 8.64 | 5.963254 | small |
noP | noP | MgCl | 10.36 | 5.359044 | small |
noP | noP | MgCl | 7.69 | 5.725348 | small |
noP | noP | MgCl | 8.57 | 6.424601 | small |
noP_101 | noP | Strain101 | 10.49 | 7.143667 | small |
noP_101 | noP | Strain101 | 8.91 | 7.669841 | small |
noP_101 | noP | Strain101 | 9.32 | 7.807710 | small |
noP_101 | noP | Strain101 | 6.76 | 7.508370 | small |
noP_101 | noP | Strain101 | 5.99 | 6.607630 | small |
noP_101 | noP | Strain101 | 8.26 | 7.269267 | small |
noP_101 | noP | Strain101 | 7.61 | 7.973207 | small |
noP_101 | noP | Strain101 | 7.56 | 7.399504 | small |
noP_101 | noP | Strain101 | 7.90 | 6.717792 | small |
noP_101 | noP | Strain101 | 6.69 | 6.721007 | small |
noP_101 | noP | Strain101 | 8.14 | 7.070333 | small |
noP_101 | noP | Strain101 | 6.07 | 5.947965 | small |
noP_101 | noP | Strain101 | 8.19 | 6.393722 | small |
noP_230 | noP | Strain230 | 4.96 | 7.166174 | small |
noP_230 | noP | Strain230 | 6.20 | 7.515659 | small |
noP_230 | noP | Strain230 | 5.97 | 7.250036 | small |
noP_230 | noP | Strain230 | 5.32 | 7.134681 | small |
noP_230 | noP | Strain230 | 5.45 | 6.917319 | small |
noP_230 | noP | Strain230 | 6.03 | 6.120089 | small |
noP_230 | noP | Strain230 | 5.70 | 7.665526 | small |
noP_230 | noP | Strain230 | 6.04 | 7.809111 | small |
noP_230 | noP | Strain230 | 5.22 | 6.601296 | small |
noP_230 | noP | Strain230 | 3.99 | 6.524710 | small |
noP_230 | noP | Strain230 | 4.85 | 8.197116 | small |
noP_28 | noP | Strain28 | 8.63 | 6.160052 | small |
noP_28 | noP | Strain28 | 7.27 | 4.720711 | small |
noP_28 | noP | Strain28 | 9.51 | 6.917185 | small |
noP_28 | noP | Strain28 | 7.46 | 4.384756 | small |
noP_28 | noP | Strain28 | 7.91 | 6.069185 | small |
noP_28 | noP | Strain28 | 7.40 | 7.113879 | small |
noP_28 | noP | Strain28 | 9.36 | 5.428766 | small |
noP_28 | noP | Strain28 | 9.74 | 6.694142 | small |
noP_28 | noP | Strain28 | 4.15 | 5.270298 | small |
noP_28 | noP | Strain28 | 8.60 | 6.811773 | small |
noP_28 | noP | Strain28 | 5.41 | 5.106644 | small |
noP_28 | noP | Strain28 | 8.30 | 7.413519 | small |
noP_28 | noP | Strain28 | 9.93 | 7.112385 | small |
noP_28 | noP | Strain28 | 9.44 | 7.428674 | small |
noP_28 | noP | Strain28 | 7.85 | 6.372659 | small |
P | P | MgCl | 32.42 | 11.286793 | large |
P | P | MgCl | 21.03 | 10.456630 | large |
P | P | MgCl | 18.35 | 11.106341 | large |
P | P | MgCl | 21.04 | 10.816896 | large |
P | P | MgCl | 16.61 | 10.608252 | large |
P | P | MgCl | 25.79 | 7.121587 | small |
P | P | MgCl | 31.00 | 9.165891 | small |
P | P | MgCl | 36.13 | 11.053978 | large |
P | P | MgCl | 17.50 | 8.271196 | small |
P | P | MgCl | 21.42 | 8.649225 | small |
P | P | MgCl | 19.60 | 10.313985 | large |
P | P | MgCl | 11.62 | 8.098859 | small |
P | P | MgCl | 18.76 | 10.061778 | large |
P | P | MgCl | 21.55 | 11.048822 | large |
P | P | MgCl | 19.05 | 9.741622 | small |
P_101 | P | Strain101 | 20.91 | 12.119304 | large |
P_101 | P | Strain101 | 23.48 | 12.057252 | large |
P_101 | P | Strain101 | 17.47 | 12.064659 | large |
P_101 | P | Strain101 | 24.49 | 13.576118 | large |
P_101 | P | Strain101 | 25.70 | 10.983965 | large |
P_101 | P | Strain101 | 28.24 | 12.935397 | large |
P_101 | P | Strain101 | 17.70 | 11.921333 | large |
P_101 | P | Strain101 | 32.90 | 13.030630 | large |
P_101 | P | Strain101 | 22.80 | 12.644756 | large |
P_101 | P | Strain101 | 32.47 | 14.715830 | large |
P_101 | P | Strain101 | 22.05 | 13.186593 | large |
P_101 | P | Strain101 | 23.47 | 13.513763 | large |
P_230 | P | Strain230 | 23.46 | 10.981978 | large |
P_230 | P | Strain230 | 22.93 | 12.563616 | large |
P_230 | P | Strain230 | 17.08 | 12.174456 | large |
P_230 | P | Strain230 | 15.95 | 13.151797 | large |
P_230 | P | Strain230 | 15.03 | 12.072456 | large |
P_230 | P | Strain230 | 13.00 | 10.692603 | large |
P_230 | P | Strain230 | 18.30 | 12.236482 | large |
P_230 | P | Strain230 | 17.80 | 11.792879 | large |
P_230 | P | Strain230 | 17.43 | 11.914695 | large |
P_230 | P | Strain230 | 14.93 | 11.281731 | large |
P_230 | P | Strain230 | 22.75 | 11.502507 | large |
P_230 | P | Strain230 | 37.90 | 13.170210 | large |
P_230 | P | Strain230 | 13.27 | 10.055399 | large |
P_28 | P | Strain28 | 37.14 | 11.908378 | large |
P_28 | P | Strain28 | 39.39 | 12.612785 | large |
P_28 | P | Strain28 | 28.27 | 12.434356 | large |
P_28 | P | Strain28 | 29.50 | 12.559904 | large |
P_28 | P | Strain28 | 27.29 | 11.585637 | large |
P_28 | P | Strain28 | 22.82 | 11.925326 | large |
P_28 | P | Strain28 | 25.05 | 12.831222 | large |
P_28 | P | Strain28 | 17.48 | 11.644037 | large |
P_28 | P | Strain28 | 15.85 | 11.900193 | large |
P_28 | P | Strain28 | 9.54 | 10.698752 | large |
P_28 | P | Strain28 | 27.91 | 11.668794 | large |
P_28 | P | Strain28 | 26.63 | 12.555404 | large |
P_28 | P | Strain28 | 29.96 | 11.771277 | large |
So the function works like this:
ifelse(our_test, value_to_return_if_test_is_true, value_to_return_if_test_is_false)
We can also combine different statements with the & (AND) or | (OR) symbol. I.e. we only send things in the big category if the roots are longer than 10cm AND the shoot weight is larger than 15mg
#apply ifelse
$category <- ifelse(growth_data$Rootlength>10 & growth_data$FW_shoot_mg>15, "large", "small")
growth_data
#check the structure of our data
kable(growth_data) %>%
kable_styling() %>%
scroll_box(width = "700px", height = "400px")
SampleID | Nutrient | Condition | FW_shoot_mg | Rootlength | category |
---|---|---|---|---|---|
noP | noP | MgCl | 10.26 | 5.931015 | small |
noP | noP | MgCl | 6.52 | 5.743447 | small |
noP | noP | MgCl | 12.17 | 6.834720 | small |
noP | noP | MgCl | 11.37 | 6.742735 | small |
noP | noP | MgCl | 9.80 | 6.736886 | small |
noP | noP | MgCl | 3.75 | 4.236349 | small |
noP | noP | MgCl | 5.38 | 4.753485 | small |
noP | noP | MgCl | 4.53 | 5.532333 | small |
noP | noP | MgCl | 7.75 | 5.484364 | small |
noP | noP | MgCl | 8.64 | 5.963254 | small |
noP | noP | MgCl | 10.36 | 5.359044 | small |
noP | noP | MgCl | 7.69 | 5.725348 | small |
noP | noP | MgCl | 8.57 | 6.424601 | small |
noP_101 | noP | Strain101 | 10.49 | 7.143667 | small |
noP_101 | noP | Strain101 | 8.91 | 7.669841 | small |
noP_101 | noP | Strain101 | 9.32 | 7.807710 | small |
noP_101 | noP | Strain101 | 6.76 | 7.508370 | small |
noP_101 | noP | Strain101 | 5.99 | 6.607630 | small |
noP_101 | noP | Strain101 | 8.26 | 7.269267 | small |
noP_101 | noP | Strain101 | 7.61 | 7.973207 | small |
noP_101 | noP | Strain101 | 7.56 | 7.399504 | small |
noP_101 | noP | Strain101 | 7.90 | 6.717792 | small |
noP_101 | noP | Strain101 | 6.69 | 6.721007 | small |
noP_101 | noP | Strain101 | 8.14 | 7.070333 | small |
noP_101 | noP | Strain101 | 6.07 | 5.947965 | small |
noP_101 | noP | Strain101 | 8.19 | 6.393722 | small |
noP_230 | noP | Strain230 | 4.96 | 7.166174 | small |
noP_230 | noP | Strain230 | 6.20 | 7.515659 | small |
noP_230 | noP | Strain230 | 5.97 | 7.250036 | small |
noP_230 | noP | Strain230 | 5.32 | 7.134681 | small |
noP_230 | noP | Strain230 | 5.45 | 6.917319 | small |
noP_230 | noP | Strain230 | 6.03 | 6.120089 | small |
noP_230 | noP | Strain230 | 5.70 | 7.665526 | small |
noP_230 | noP | Strain230 | 6.04 | 7.809111 | small |
noP_230 | noP | Strain230 | 5.22 | 6.601296 | small |
noP_230 | noP | Strain230 | 3.99 | 6.524710 | small |
noP_230 | noP | Strain230 | 4.85 | 8.197116 | small |
noP_28 | noP | Strain28 | 8.63 | 6.160052 | small |
noP_28 | noP | Strain28 | 7.27 | 4.720711 | small |
noP_28 | noP | Strain28 | 9.51 | 6.917185 | small |
noP_28 | noP | Strain28 | 7.46 | 4.384756 | small |
noP_28 | noP | Strain28 | 7.91 | 6.069185 | small |
noP_28 | noP | Strain28 | 7.40 | 7.113879 | small |
noP_28 | noP | Strain28 | 9.36 | 5.428766 | small |
noP_28 | noP | Strain28 | 9.74 | 6.694142 | small |
noP_28 | noP | Strain28 | 4.15 | 5.270298 | small |
noP_28 | noP | Strain28 | 8.60 | 6.811773 | small |
noP_28 | noP | Strain28 | 5.41 | 5.106644 | small |
noP_28 | noP | Strain28 | 8.30 | 7.413519 | small |
noP_28 | noP | Strain28 | 9.93 | 7.112385 | small |
noP_28 | noP | Strain28 | 9.44 | 7.428674 | small |
noP_28 | noP | Strain28 | 7.85 | 6.372659 | small |
P | P | MgCl | 32.42 | 11.286793 | large |
P | P | MgCl | 21.03 | 10.456630 | large |
P | P | MgCl | 18.35 | 11.106341 | large |
P | P | MgCl | 21.04 | 10.816896 | large |
P | P | MgCl | 16.61 | 10.608252 | large |
P | P | MgCl | 25.79 | 7.121587 | small |
P | P | MgCl | 31.00 | 9.165891 | small |
P | P | MgCl | 36.13 | 11.053978 | large |
P | P | MgCl | 17.50 | 8.271196 | small |
P | P | MgCl | 21.42 | 8.649225 | small |
P | P | MgCl | 19.60 | 10.313985 | large |
P | P | MgCl | 11.62 | 8.098859 | small |
P | P | MgCl | 18.76 | 10.061778 | large |
P | P | MgCl | 21.55 | 11.048822 | large |
P | P | MgCl | 19.05 | 9.741622 | small |
P_101 | P | Strain101 | 20.91 | 12.119304 | large |
P_101 | P | Strain101 | 23.48 | 12.057252 | large |
P_101 | P | Strain101 | 17.47 | 12.064659 | large |
P_101 | P | Strain101 | 24.49 | 13.576118 | large |
P_101 | P | Strain101 | 25.70 | 10.983965 | large |
P_101 | P | Strain101 | 28.24 | 12.935397 | large |
P_101 | P | Strain101 | 17.70 | 11.921333 | large |
P_101 | P | Strain101 | 32.90 | 13.030630 | large |
P_101 | P | Strain101 | 22.80 | 12.644756 | large |
P_101 | P | Strain101 | 32.47 | 14.715830 | large |
P_101 | P | Strain101 | 22.05 | 13.186593 | large |
P_101 | P | Strain101 | 23.47 | 13.513763 | large |
P_230 | P | Strain230 | 23.46 | 10.981978 | large |
P_230 | P | Strain230 | 22.93 | 12.563616 | large |
P_230 | P | Strain230 | 17.08 | 12.174456 | large |
P_230 | P | Strain230 | 15.95 | 13.151797 | large |
P_230 | P | Strain230 | 15.03 | 12.072456 | large |
P_230 | P | Strain230 | 13.00 | 10.692603 | small |
P_230 | P | Strain230 | 18.30 | 12.236482 | large |
P_230 | P | Strain230 | 17.80 | 11.792879 | large |
P_230 | P | Strain230 | 17.43 | 11.914695 | large |
P_230 | P | Strain230 | 14.93 | 11.281731 | small |
P_230 | P | Strain230 | 22.75 | 11.502507 | large |
P_230 | P | Strain230 | 37.90 | 13.170210 | large |
P_230 | P | Strain230 | 13.27 | 10.055399 | small |
P_28 | P | Strain28 | 37.14 | 11.908378 | large |
P_28 | P | Strain28 | 39.39 | 12.612785 | large |
P_28 | P | Strain28 | 28.27 | 12.434356 | large |
P_28 | P | Strain28 | 29.50 | 12.559904 | large |
P_28 | P | Strain28 | 27.29 | 11.585637 | large |
P_28 | P | Strain28 | 22.82 | 11.925326 | large |
P_28 | P | Strain28 | 25.05 | 12.831222 | large |
P_28 | P | Strain28 | 17.48 | 11.644037 | large |
P_28 | P | Strain28 | 15.85 | 11.900193 | large |
P_28 | P | Strain28 | 9.54 | 10.698752 | small |
P_28 | P | Strain28 | 27.91 | 11.668794 | large |
P_28 | P | Strain28 | 26.63 | 12.555404 | large |
P_28 | P | Strain28 | 29.96 | 11.771277 | large |