R offers different control flow structures, which allow us to control the flow of execution of a script:


if-else Statements

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"

Switch

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

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
Show code
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

Guess a number

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.

Creative Commons License
You may use this project freely under the Creative Commons Attribution-ShareAlike 4.0 International License.

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.