Suppose we are interested in executing the same code over and over and over. As an example, suppose I wanted to write some code to print every student name and grade. Of course, I could write the following:
Name: Alex ... Grade: B
Name: Brooke ... Grade: A
Name: Carlos ... Grade: A
Name: Dasia ... Grade: B
Name: Enzo ... Grade: C
This code works, but there are some problems. First, writing this out so many times makes it prone to typos, even if just copying and pasting. Second, almost anything that is repetitive or has an identifiable pattern is easier for a computer than for a human. Lastly, what if there were 100 names instead of 5? What if 10,000? This is where loops come in.
Our first step is going to be creating vectors of names and grades. Ideally, you’d have these data in a spreadsheet/csv and could easily read it in using read.csv().
Second, we’re going to write our loop. The loop needs two things:
an iterator. Usually, people use i but it can be anything, of course.
loop bounds. This is the only thing that will change during each repetition. Since we have 5 names and grades, we’re going to loop over elements 1 through 5. We’ll use 1:5, or 1:length(namez) to be even more flexible.
Code
# for(iterator in bounds)# everything between { and } will be loopedfor(i in1:length(namez)){}
The iterator and bounds in for loops are similar to the iterator and bounds in \(\sum_{i = 1}^n\)
Let’s just fill the loop with a simple printing statement to illustrate what the loop does.
Remember, to access the first name in namez, we would use the following: namez[1]. Similarly, namez[2] would return the second element, and namez[length(namez)] would give the last element. Instead of putting a specific element in the square brackets, we can put the index variable there. Then, we can put this into our loop:
Finally, we can put this all together and generate our initial output.
Now, consider if we had many more names and grades. We could have thousands of names and grades and our little for loop would remain the same!
We are not limited to just numeric iterators/bounds. Sometimes, using non-numeric ones is helpful too:
We can also use loops to create or modify data. As an example, we’re going to build up the Fibonacci Sequence one element at a time.
Element \(n\) in the Fibonacci Sequence is simply a sum of the previous two elements. Explicitly, \(F_n = F_{n-1} + F_{n-2}\). Usually, people start the sequence with 0 and 1, which makes the third element equal to 1 (1 + 0), the fourth element equal to 2 (1 + 1), the fifth element equal to 3 (2 + 1), and so on. Our goal is to generate the first \(n\) elements of the sequence.
Again, the formula for any element \(n\) is just \(F_n = F_{n-1} + F_{n-2}\). So, to calculate \(F_n\), we need these other two numbers. However, to get \(F_{n-2}\), for example, we need \(F_{n-3}\) and \(F_{n-4}\). Obviously, this continues back until we arrive at \(F_1\) and \(F_2\). This suggests that we’ll need to calculate each element in the sequence until we arrive at \(F_{n}\).
Let’s start with the third element. Since we have v <- c(0, 1) already, we can write v[3] <- v[2] + v[1]. This can also be written as: v[3] <- v[3-1] + v[3-2]. Once we have v[3] established, we can calculate v[4] as v[3] + v[2], or v[4] <- v[4-1] + v[4-2]. We would repeat this for 5, 6, 7, and all the way until \(n\). Hopefully, you can see that we could also generalize this code to look like v[i] <- v[i-1] + v[i-2] inside of a for loop. Our loop bounds would start at 3 (since elements 1 and 2 are established as 0 and 1 already), and we would continue the loop until \(n\).
Solution
Code
n <-10v <-c(0, 1)for(i in3:n){ v[i] <- v[i-1] + v[i-2]}print(v)
Output
[1] 0 1 1 2 3 5 8 13 21 34
Conditionals
We can build “logic” into our loops by adding in if statements, too. Take a look at the following example using names and grades again. Here, I will change what gets printed based on the grade the individual obtained.
Name: Alex ... you're doing a good job!
Name: Brooke ... crushin' it!
Name: Carlos ... crushin' it!
Name: Dasia ... you're doing a good job!
Name: Enzo ... keep studying!
If you remember, in Module 1.7 we colored points based on certain characteristics. We did this by first setting all colors to be the same value, and then changed the color of certain observations based on their values. See below.
We can use loops to achieve a similar outcome. You can run the code snippets above and below to see for yourself that the results are the same.
ifelse()
While both of these solutions arrive at the same answer, there is a better option.
First, in general, loops are relatively slow in R. It might not seem so when dealing with small samples, but it becomes noticeable as the data grow larger.
Second, as people often say, “lazy” programming is good programming! We should be writing as little as possible1, without sacrificing coherence/readability, to minimize mistakes, bugs, etc.
Introducing: ifelse(). This is a function that accepts three arguments:
test: an object which can be coerced to logical mode. In other words, some logical vector like v > 5
yes: return values for true elements of test. In other words, what should be the output when v > 5 is TRUE?
no: return values for false elements of test. In other words, what should be the output when v > 5 is FALSE?
You can think of ifelse() as creating a for loop with if() statements inside of it. In fact, we can even nest ifelse statements. For example, consider these data on U.S. Senate vote on the use of force against Iraq in 2002. For each observation, we want to assign some value (here, I chose to assign some text) by party and by vote.
A Note on Writing Your Own Functions
Throughout this lesson (and the last several), we have been using functions that someone else wrote, like cat(), length(), and ifelse(). R also lets you write your own functions with function(). For example:
Code
double_it <-function(x){return(x *2)}double_it(4)
Writing your own functions is a powerful skill, and you will see it in other R tutorials and code online. However, we are not going to cover it in this course. Everything we need can be done with the tools we have already covered.
Footnotes
Except for code comments! Always comment your code.↩︎