4. 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.

Tip

The discussion draws on examples from the EC139 lecture on present values and discounting. Make sure to revise the material from the lecture before engaging with this chapter.

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.

4.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 of sequence 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 in sequence

This might appear a bit difficult to follow but will become clear after a few examples.

4.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 statement print(1)

  • At the second iteration i takes the value of 2. Then the program executes the statement print(2)

  • and so forth until the tenth and final iteration.

4.1.2. Example 2#

As another example, suppose that you want to print the first 15 elements of the geometric progression

\[ \left\{5, 5\times\frac{1}{2}, 5\times\left(\frac{1}{2}\right)^2, 5\times\left(\frac{1}{2}\right)^3, ..., 5\times\left(\frac{1}{2}\right)^{14} \right\} \]

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
0.00030517578125

4.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
2.35098870164458e-37

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.

Exercise 4.1

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

\[ F_n = F_{n-1} + F_{n-2} \]

Write a for loop which finds the 37th Fibonacci number, \(F_{37}\).

4.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).

4.2.1. Evaluating geometric series#

In the lectures we have seen that 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\)

\[ \{A, AR, AR^2, AR^3, ...\} \]

The sum of the first \(n\) elements of the geometric progression is given by

(4.1)#\[A + AR + AR^2 + \dots + AR^{n-1} = A\frac{1-R^n}{1-R}\]

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

  1. It is more difficult and computationally intensive than using the formula.

  2. 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\)

\[ \left\{5, 5\times\frac{1}{2}, 5\times\left(\frac{1}{2}\right)^2, 5\times\left(\frac{1}{2}\right)^3, ..., 5\times\left(\frac{1}{2}\right)^{14} \right\} \]

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 (4.1) above. Plugging the relevant values into the formula yields a value

5*((1-(1/2)^15))/(1-1/2)
9.99969482421875

However, we can obtain the same result with a loop as demonstrated by the code below. The idea is the following

  1. 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 only sum<-x

  2. 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
9.99969482421875

As you can see the value we obtain is exactly the same as by using the analytical expression for the value of the sum.

Exercise 4.2

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.

This completes the discussion of Loops in R. We next turn attention to Plotting bivariate functions in R.