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…
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
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
# }
# }