Lesson 7 | 15 April 2020

Nested Loops

As a reminder, here is the general format of a nested loop:

# loop (condition) {
#   block of instructions
#   loop (condition) {
#     block of instructions
#   }
# }

Example 1

Let’s go through last time’s example again:

Picture the passing of a year…

  1. The outer loop can be considered as the start of a month.
  2. The inner loop can be considered as the days of that month.
  3. When a month starts i.e. the outer loop is read by the system, the control enters the inner loop. It executes it completely( i.e. for all 31 days). It then goes back to the outer loop.
months <- c("Janurary", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "Decemeber")

# Months w/ 31 days: 1,3,5,7,8,10,12
# Months w/ 29 days: 2
# Months w/ 30 days: 4,6,9,11

# **Update** Mistake was caught - need 'else if' statement instead of 'if' statement

for (m in seq(1:length(months))) {
  #print(m)
  print(months[m])
  
  # Conditional Initializers
  if (m==4|m ==6|m== 9|m==11) { 
    #  & (element-wise) && and | (element-wise) ||, for logical AND and OR
    days <- -1 }
  
  else if (m==2) {
    days <- -2
  }
  else {
    days <- 0
  }
  for (day in seq(1:31)) {
    days <- days + 1
  }
  print(days) # print days of each month
} # end of the year  
## [1] "Janurary"
## [1] 31
## [1] "Feburary"
## [1] 29
## [1] "March"
## [1] 31
## [1] "April"
## [1] 30
## [1] "May"
## [1] 31
## [1] "June"
## [1] 30
## [1] "July"
## [1] 31
## [1] "August"
## [1] 31
## [1] "September"
## [1] 30
## [1] "October"
## [1] 31
## [1] "November"
## [1] 30
## [1] "Decemeber"
## [1] 31

Example 2

Curving grades with ease!

Let’s say you have 6 students and they have taken 4 tests total in your class. However, some tests went worse than others overall, so you want to curve those tests but don’t want to do it all by hand. Let’s instead make a nested for loop that will do those calculations for you!

Before we jump in, let’s quickly revisit the differneces between lists and vectors in R.

A vector stores elements of the same type or converts obliquely, whereas a list holds different data types like numerical, character, logical, etc. Lists are recursive but vectors are non-recursive. The vector is one-dimensional, whereas the list is a multidimensional object.

Why do I bring this up again? Because, under the hood, there is a lot happening that we don’t necessarily see because R is a high-level langauge. Revisiting why these differences occur can help one understand why certain functions do not work on lists but do work on vectors.

For example, let’s look at this list of lists of grades. Then, try creating a vector of vectors and compare the difference when you print it.

# List of lists:

grades_ll <- list( list(79,85,70,96), # Each list is a student's test grades in a class
                list(60,71,82,92),
                list(77,78,76,79),
                list(82,91,88,97),
                list(63,92,95,82))
grades_ll
## [[1]]
## [[1]][[1]]
## [1] 79
## 
## [[1]][[2]]
## [1] 85
## 
## [[1]][[3]]
## [1] 70
## 
## [[1]][[4]]
## [1] 96
## 
## 
## [[2]]
## [[2]][[1]]
## [1] 60
## 
## [[2]][[2]]
## [1] 71
## 
## [[2]][[3]]
## [1] 82
## 
## [[2]][[4]]
## [1] 92
## 
## 
## [[3]]
## [[3]][[1]]
## [1] 77
## 
## [[3]][[2]]
## [1] 78
## 
## [[3]][[3]]
## [1] 76
## 
## [[3]][[4]]
## [1] 79
## 
## 
## [[4]]
## [[4]][[1]]
## [1] 82
## 
## [[4]][[2]]
## [1] 91
## 
## [[4]][[3]]
## [1] 88
## 
## [[4]][[4]]
## [1] 97
## 
## 
## [[5]]
## [[5]][[1]]
## [1] 63
## 
## [[5]][[2]]
## [1] 92
## 
## [[5]][[3]]
## [1] 95
## 
## [[5]][[4]]
## [1] 82
# Vector of vectors:

grades_vv <- c( c(79,85,70,96), # Each vector is a student's test grades in a class
                c(60,71,82,92),
                c(77,78,76,79),
                c(82,91,88,97),
                c(63,92,95,82))
grades_vv
##  [1] 79 85 70 96 60 71 82 92 77 78 76 79 82 91 88 97 63 92 95 82

Finally, try creating and printing a list of vectors. See what happens.

# List of vectors:

grades_lv <- list( c(79,85,70,96), # Each vector is a student's test grades in a class
                c(60,71,82,92),
                c(77,78,76,79),
                c(82,91,88,97),
                c(63,92,95,82))
grades_lv
## [[1]]
## [1] 79 85 70 96
## 
## [[2]]
## [1] 60 71 82 92
## 
## [[3]]
## [1] 77 78 76 79
## 
## [[4]]
## [1] 82 91 88 97
## 
## [[5]]
## [1] 63 92 95 82

So which do we want to use? We’re going to go with a list of vectors because vectors don’t have any ‘dimensional strings’ attached like the lists do. Therefore, it makes mathematical functions much easier to computer when you have a vector than a list. I’ll show you why but for now let’s go back to the main problem.

So, as teacher, you need curve your students’ grades, but figure you might as well make a code for it so that you don’t have to do this by hand every time in the future. This is one way you decide to go about it:

grades_lv <- list( c(79,85,70,96), # Each vector is a student's test grades in a class
                c(60,71,82,92),
                c(77,78,76,79),
                c(82,91,88,97),
                c(63,92,95,82))

# Make an empty matrix:

# Want to create a matrix that will:
# (1) "curve" the first test and the third by +4 points
# (2) create a new column that calculates the mean of the test grades per student

iterations = length(grades_lv)
variables = length(grades_lv[[1]]) + 1 # + 1 is for the new column
matrix <- matrix(ncol=variables, nrow=iterations)

# Populating an empty matrix with a nested for loop:

# Initalizers/counts

stud_n <- 0

for (student in grades_lv) { # loop over rows
  mean <- mean(student)
  stud_n <- stud_n + 1
  test_n <- 0
  for (test_grade in student) { # loop over columns within a row
    test_n <- test_n + 1
    if (test_n%%2 != 0) { # What is common between the first and third column?
      test_grade <- test_grade + 4
      cat("Curved grade is ", test_grade)
    }
    # populating the matrix 
    matrix[stud_n,5] <- mean # matrix[row_n, col_n]
    matrix[stud_n, test_n] <- test_grade
    
  }
}
## Curved grade is  83Curved grade is  74Curved grade is  64Curved grade is  86Curved grade is  81Curved grade is  80Curved grade is  86Curved grade is  92Curved grade is  67Curved grade is  99
matrix
##      [,1] [,2] [,3] [,4]  [,5]
## [1,]   83   85   74   96 82.50
## [2,]   64   71   86   92 76.25
## [3,]   81   78   80   79 77.50
## [4,]   86   91   92   97 89.50
## [5,]   67   92   99   82 83.00
grades_lv
## [[1]]
## [1] 79 85 70 96
## 
## [[2]]
## [1] 60 71 82 92
## 
## [[3]]
## [1] 77 78 76 79
## 
## [[4]]
## [1] 82 91 88 97
## 
## [[5]]
## [1] 63 92 95 82

Recap

When looping over rows and columns, here is a general skeleton template.

# row_n <- row_n + 1
# for (row in list_of_vectors) {
#   col_n <- col_n + 1
#   block of instructions
#   for (col in row) {
#     block of instructions
#   }
# }