R offers different control flow structures, which allow us to control the flow of execution of a script:
if
, if else
and else
switch
for
while
repeat
break
next
return
if (condition) {
# do something
} else {
# do something else
}
A simple example cloud look like this:
my_age <- 14
if (my_age < 18) {
print("You are a teenager")
} else {
print("You are an adult")
}
## [1] "You are a teenager"
my_age <- 21
if (my_age < 18) {
print("You are a teenager")
} else {
print("You are an adult")
}
## [1] "You are an adult"
For such simple if
-else
condition there
exists a vectorized, and hence more efficient, version.
my_age <- 15
ifelse(my_age <= 18, "You are a teenager", "You are an adult")
## [1] "You are a teenager"
my_age <- 45
ifelse(my_age <= 18, "You are a teenager", "You are an adult")
## [1] "You are an adult"
Another related control flow element is the switch()
command, which selects one of a list of alternatives. Let us create a
function called centre
, which computes either the mean or
the median of a given vector.
centre <- function(x, type) {
switch(type,
mean = mean(x),
median = median(x)
)
}
Let us construct a vector \(\mathbf v\).
v <- c(1, 2, 3, 4, 5, 1000)
Now we call the centre()
function, first computing the
mean and thereafter the median of the vector \(\mathbf v\).
centre(v, "mean")
## [1] 169.1667
centre(v, "median")
## [1] 3.5
Loops are very powerful techniques for repeating some task.
For-loops
A for
loop works on an iterable variable of finite
length. For
loops are applied if we know in advance how
often a task shall be repeated.
Let us use a for
loop to print each number between 1 and
10.
for (i in 1:10) {
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
We may as well loop through any iterable object, such as a vector of strings. Let us create a vector of strings.
beatles <- c("John", "George", "Paul", "Ringo")
We may either access the items directly…
for (i in beatles) {
print(i)
}
## [1] "John"
## [1] "George"
## [1] "Paul"
## [1] "Ringo"
or we access the item via indexing
for (i in 1:length(beatles)) {
print(i)
print(beatles[i])
}
## [1] 1
## [1] "John"
## [1] 2
## [1] "George"
## [1] 3
## [1] "Paul"
## [1] 4
## [1] "Ringo"
Sometimes it makes sense to construct a nested loop to achieve a particular task.
M <- matrix(1:10, 5)
M
## [,1] [,2]
## [1,] 1 6
## [2,] 2 7
## [3,] 3 8
## [4,] 4 9
## [5,] 5 10
We now loop through all rows and columns of the matrix \(\mathbf M\) and print out the value at the index \(\mathbf M[i,j]\).
for (i in seq(nrow(M))) {
for (j in seq(ncol(M))) {
val <- M[i, j]
print(paste("row index:", i, "column index:", j, "value:", val))
}
}
## [1] "row index: 1 column index: 1 value: 1"
## [1] "row index: 1 column index: 2 value: 6"
## [1] "row index: 2 column index: 1 value: 2"
## [1] "row index: 2 column index: 2 value: 7"
## [1] "row index: 3 column index: 1 value: 3"
## [1] "row index: 3 column index: 2 value: 8"
## [1] "row index: 4 column index: 1 value: 4"
## [1] "row index: 4 column index: 2 value: 9"
## [1] "row index: 5 column index: 1 value: 5"
## [1] "row index: 5 column index: 2 value: 10"
Exercise: Write a for-loop, that calculates the Fibonacci numbers from the first one to the e.g. 10th!
### your code here
fib <- c() # empty vector for storing the numbers
k <- 10 # order of last calculated Fibonacci number
fib[1] <- 0 # fib[1] & fib [2] define the first two start numbers
fib[2] <- 1
for (i in 3:k) { # calculate Fibonacci numbers from 3rd position onward
fib[i] <- fib[i - 1] + fib[i - 2]
}
fib
## [1] 0 1 1 2 3 5 8 13 21 34
While-loops
A while
loop repeats a task until a condition is met.
This means that we do not need to know in advance how many times the
loop will be executed. Be sure there is a way to exit out of a
while
loop, otherwise your program will run forever.
i <- 1 # set i to 1
while (i < 10) { # continue as long as i < 10
print(i) # print i
i <- i + 1 # increase i by one for each step
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
Other useful commands to control the execution of your code are the
break
and the next
statement.
We redo the while
loop from above, this time however, we
build in an error, which would cause the loop to run infinitely.
However, by introducing the break
statement we make sure
the the loop stops no matter what after 50 iterations.
i <- 1 # set i to 1
while (i < 10) { # continue as long as i < 10
i <- i - 1 # ERROR! Here we decrease i by one for each step, hence the condition i < 10 is always met!
if (abs(i) == 50) { # if i is 50 or -50 we break out of the loop
print("Iteration stopped!")
break
}
}
## [1] "Iteration stopped!"
# sanity check
print(i)
## [1] -50
The next
statement is used to skip the current loop
execution and restarts the next iteration.
In order to print only even numbers and skip odd numbers of a given
sequence we may write the following code (if you never encountered the
modulo operator %%
before, ask your preferred search
engine!):
s <- seq(1, 25, 1)
for (i in s) {
if (i %% 2 == 1) { # modulo operator
next
} else {
print(i)
}
}
## [1] 2
## [1] 4
## [1] 6
## [1] 8
## [1] 10
## [1] 12
## [1] 14
## [1] 16
## [1] 18
## [1] 20
## [1] 22
## [1] 24
To conclude this section we apply our knowledge of functions and
control statements to build a small program, called
guess_a_number
. The program is all about guessing a random
integer within a given range of guesses.
guess_a_number <- function(max_value = 100, number_of_trials = 10) {
## the computer guesses a random number
## between 0 and max_value (default = 100)
vector_of_integers <- seq(1, max_value, 1)
num <- sample(vector_of_integers, 1)
## set trial value to 1
trial <- 1
### utility function to account for user input
## ask user for a guess
ask_for_guess <- function() {
cat("Guess a number between 0 and", max_value, "\n")
guess <- readline(prompt = "Enter an integer: ")
# make sure user entered an integer
if (!grepl("^[0-9]+$", guess)) {
cat("Please provide an integer\n")
return(ask_for_guess())
}
return(as.integer(guess))
}
## start the game
guess <- 101
## set conditions for game over
cat(paste("You have", number_of_trials, "trials to guess the number!\n"))
while (guess != num & trial <= number_of_trials) {
guess <- ask_for_guess()
if (guess == num) {
cat("Congratulations,", num, "is right.\n")
break # break as game is over
} else if (guess < num) {
cat("The number is bigger!\n")
} else if (guess > num) {
cat("The number is smaller!\n")
}
cat(paste(number_of_trials - trial, "trials left!\n\n"))
trial <- trial + 1 # increase the number of trials
}
if (guess != num) {
cat(paste("GAME OVER, the number was", num))
}
}
Now copy the code cell above to your R console and give it a try!
guess_a_number()
Feel free to adapt the function and play around with the arguments
max_value
and number_of_trials
.
Citation
The E-Learning project SOGA-R was developed at the Department of Earth Sciences by Kai Hartmann, Joachim Krois and Annette Rudolph. You can reach us via mail by soga[at]zedat.fu-berlin.de.
Please cite as follow: Hartmann, K., Krois, J., Rudolph, A. (2023): Statistics and Geodata Analysis using R (SOGA-R). Department of Earth Sciences, Freie Universitaet Berlin.