Loops in R
Contents
7. Loops in R#
This chapter is a quick introduction to loops. Following the discussion you should understand what loops are, and be able to use for loop in R.
The best way to work through the chapter is to read the discussion and then reproduce all code in your own R/RStudio installation. You can do this by copying code from a notebook cell to your R script and run the code line by line.
The chapter includes some exercises that allow you to test your understanding. It is recommended that you try these exercises on your own before choosing the option to reveal the solutions.
7.1. What is a loop?#
In programming a loop is a sequence of statements which is specified only once (in the code inside the body of the loop) but is carried out several times in a succession. Loops provide a convenient way to execute operations which can be carried out by a sequential application of the same procedure.
There are several types of loops but in these notes we will only make a use of for loops (other types of loops work in a similar way). A for loop specifies a fixed number of iterations over which the code inside the body of the loop will be executed.
In R a for loop is called through the following syntax
for (`index` in `sequence`)
{
statements (body of the loop)
}
Given a sequence (say a vector of numbers) specified in sequence
, the above loop will create a variable (in the above code referred to as index
) which
(1st iteration) initially will take the value of the first element of
sequence
after which the statements inside the body of the loop will be executed(2nd iteration) next
index
will take the value of the second element ofsequence
after which the statements inside the body of the loop will be executed again… and so forth until
index
has taken the value of all the elements insequence
This might appear a bit difficult to follow but will become clear after a few examples.
7.1.1. Example 1#
Note
To understand the following code you should know the function print()
which simply prints its argument on the screen. For example,
print(1)
returns
[1] 1
Similarly,
x <- 1
print(x)
returns
[1] 1
To see how a loop works consider the following very simple example. Suppose that for some reason we want to print the numbers from 1 to 10 on screen. One way to proceed is to call print 10 times
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)
print(7)
print(8)
print(9)
print(10)
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
For repetitive tasks like this it is more efficient to use a loop instead. A for loop that executes exactly the same sequence of statements can be invoked by
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
The code defined an index variable called i
and a sequence for i
to iterate over, 1:10
. At each iteration the program prints the value of i
.
At the first iteration
i
takes the value of 1. Then the program executes the statementprint(1)
At the second iteration
i
takes the value of 2. Then the program executes the statementprint(2)
and so forth until the tenth and final iteration.
7.1.2. Example 2#
As another example, suppose that you want to print the first 15 elements of the geometric progression
with first element \(5\) and common ratio \(1/2\).
Without loops, one way to do this is by the following sequence of repetitive statements:
# First element
x <- 5
print(x)
[1] 5
# Next element
x <- x*(1/2)
print(x)
[1] 2.5
# Next element
x <- x*(1/2)
print(x)
[1] 1.25
And so forth 12 more times.
Given the repetitive nature of the process we can use a for loop instead:
x <- 5
print(x)
for (i in 2:15)
{
x <- x*(1/2)
print(x)
}
[1] 5
[1] 2.5
[1] 1.25
[1] 0.625
[1] 0.3125
[1] 0.15625
[1] 0.078125
[1] 0.0390625
[1] 0.01953125
[1] 0.009765625
[1] 0.004882812
[1] 0.002441406
[1] 0.001220703
[1] 0.0006103516
[1] 0.0003051758
Notice that the first element of the progression is initialized outside of the loop because the statement inside the loop x <- x*(1/2)
requires that a value of x
is already defined.
Subsequently, we need the loop to iterate 14 times only. We achieved this by specifying a loop sequence of 14 elements (2:15
) although we could have specified any other sequence of 14 elements (e.g. 1:14
) as the value of i
does not enter inside the code in this instance.
Notice that after the loop is executed, x
has a value equal to its value from the last iteration.
x
7.1.3. Example 3#
Given the geometric progression above, suppose that we are not interested in printing all these numbers but simply in finding the value of the 125th element of the progression. This can be easily done with effectively the same code except for removing the print()
statements and making the loop iterate 124 times:
x <- 5
for (i in 2:125)
{
x <- x*(1/2)
}
print(x)
[1] 2.350989e-37
Note
When printing the values of “very small” or “very large” numbers R uses scientific e notation. Simply put, the output of the above code, the number 2.350989e-37
means nothing else but the number \(2.350989\times10^{-37}\) (basically, the syntax e
means “10 to the power”, and should not be confused with the Euler’s constant \(e\)). Alternatively, 1.5e+08
would mean nothing else but 150000000
.
This is convenient for very small or large numbers because if we were using non-scientific notation the output of the above command, 2.350989e-37
, would be printed as 0.0000000000000000000000000000000000002350989
, which is more difficult to compare to a number of a similar magnitude.
As a matter of fact notice that using a loop for the above calculation was not really necessary because we have a formula for the 125th element of the progression that we could have used instead and find the value with one operation only. Recall that for a geometric progression with first element \(A\) and common ration \(R\), the \(n\)th element of the progression is simply \(A\times R^{(n-1)}\). In our example, with \(A=5, R=1/2, n=125\) this translates to
5*(1/2)^124
which is much easier.
As a general principle we have no reason to use loops in situations like this and should favour analytical to numerical procedures as the former are computationally much less intensive (although both pieces of code execute fast enough not to spot the difference). However, the purpose of the discussion was more as an example of using loops rather than a suggestion that you should use a loop for such calculation.
Note: This exercise is relatively difficult. Don’t worry if you can’t solve it on your own but give it a try.
The Fibonacci sequence is a sequence of numbers, \(\{F_1, F_2, F_3,...\}\), such that
the first two elements of the sequence are \(F_1=0\) and \(F_2=1\)
following which each subsequent element is defined recursively by
Write a for loop which finds the 37th Fibonacci number, \(F_{37}\).
Hint
Make sure to initialize the first two elements of the sequence before the loop, e.g.
f_1<-0
andf_2<-1
, and then run the loop for 35 subsequent iterations.In the body of the loop you will have to set the value of the subsequent element of the sequence, and then reinitialize
f_1
andf_2
to correspond to the the elements before the current element.
You can think in terms of the following code without loops
## Initialize first two elements outside the loop
f_1 <- 0
f_2 <- 1
## Start loop
# Iteration 1
f_i <- f_1 + f_2
f_1 <- f_2
f_2 <- f_i
# And so forth
Solution to Exercise 7.1
The code below provides a solution to the exercise
f_1 <- 0
f_2 <- 1
for (i in 3:37)
{
f_i <- f_1 + f_2
f_1 <- f_2
f_2 <- f_i
}
f_i
14930352
7.2. Some further examples#
In what follows we consider some further examples of using for loops to make calculations in the context of geometric series (mathematics) and present values (economics).
7.2.1. Evaluating geometric series#
The geometric series (sum of elements of geometric progressions) can be evaluated analytically based on the following:
Geometric series formula
Consider a geometric progression with first element \(A\) and common ratio \(R\)
The sum of the first \(n\) elements of the geometric progression is given by
In what follows we will instead give an example of how to evaluate such geometric series numerically by using loops instead.
Note
Again, given that we have an exact analytical solution it is a poor practice to use loops because
It is more difficult and computationally intensive than using the formula.
The procedure using loops below induces further sources of numerical error than present if numerically evaluating based on the analytical solution.
Nonetheless, we proceed with the recognition that this is intended as an example on using loops rather than a suggestion that you should use loops to evaluate geometric series.
Consider the geometric progression with first element \(5\) and common ratio \(1/2\)
from Example 2 above. Suppose that you want to find the sum of the first 15 elements of the progression.
Note that you can find this directly by using (7.1) above. Plugging the relevant values into the formula yields a value
5*((1-(1/2)^15))/(1-1/2)
However, we can obtain the same result with a loop as demonstrated by the code below. The idea is the following
Initialize the initial value of the first element of the geometric progression
x<-5
and the sum of the geometric progression when using the first element onlysum<-x
Iterate 14 times (say from 2 to 15 so that the value of the index at a given iteration corresponds to the index of the element on the progression) and at each iteration set the value of
x
to the corresponding value iteratively (x<-x*(1/2)
) and update the sum to include the new element (sum<-sum+x
)
This is done below
x <- 5
sum <- x
for (i in 2:15)
{
x <- x*(1/2)
sum <- sum + x
}
sum
As you can see the value we obtain is exactly the same as by using the analytical expression for the value of the sum.
Consider an annuity bond which pays £100 each year for 20 years with the first payment occurring today.
The discount rate is 5%.
Use a for loop to calculate the fair price of the bond.
Solution to Exercise 7.2
The fair price of the bond is simply the present value of all its payments
which is the sum of the first 20 elements of a geometric progression with first element \(100\) and common ratio \(1/2\). By tweaking the code for a geometric progression above we can obtain an answer as follows
x <- 100
sum <- x
for (i in 2:20)
{
x <- x*(1/(1.05))
sum <- sum + x
}
sum
which yields
1308.5320859667
Note that this is the same as the value obtained if we use (7.1) as
This completes the discussion of Loops in R, as well as this set of notes on numerical methods. I hope that you have enjoyed it.
Thank you for reading and all the best!