# Loops and Lists¶

`for`

Loops and Lifetime Savings¶

Because Python is quick and easy (once you have a handle of it), it serves as a useful tool for playing out ideas. For example, suppose that we want to visualize a savings decision:

Once you begin contributing to a savings account, assume that you put away \(x\) dollars per year and expect an annual return of \(r\). How much money will you have saved in \(55\) years?

Begin by defining the *variables* you need

```
x = 5000
r = 0.06
```

Now, *compound* the returns you get over the next \(55\) years. Imagine that today is year 0. At the end of year 0, you’ll have saved \(x \times (1+r)\). Then, after another, you’re year zero savings grow by another \((1+r)\), and your year one savings (since you put away \(x\) more each year), also grows by \((1+r)\). The following table helps visualize the idea:

Time |
Value of Money Saved at Year 0 |
Value of Money Saved at Year 1 |
Value of Money Saved at Year 2 |
Value of Money Saved at Year 3 |
---|---|---|---|---|

0 |
\(x\) |
. |
. |
. |

1 |
\(x(1+r)\) |
\(x\) |
. |
. |

2 |
\(x(1+r)^2\) |
\(x(1+r)\) |
\(x\) |
. |

3 |
\(x(1+r)^3\) |
\(x(1+r)^2\) |
\(x(1+r)\) |
\(x\) |

Add up the values across each column to get the total savings at the *beginning* of a given year. This sum is reported in the following table:

Time |
Total Savings (beginning of year) |
Total Savings (algebraically re-arranged) |
---|---|---|

0 |
\(x\) |
\(x\) |

1 |
\(x(1+r)+x\) |
\(x(1+r)+x\) |

2 |
\(x(1+r)^2+x(1+r)+x\) |
\(\Big[\)\(x(1+r)+x\)\(\Big](1+r)+x\) |

3 |
\(x(1+r)^3+x(1+r)^2+x(1+r)+x\) |
\(\Big[\)\(x(1+r)^2+x(1+r)+x\)\(\Big](1+r)+x\) |

Comparing values across columns two and three of the above table, a convenient pattern emerges. For instance, the value in year 2, column three equals \(x\) plus \((1+r)\) times the value in year 1, column two.

If we want to calculate the value of your savings acount at the beginning of year \(55\), we need to tell Python to compound returns for \(55\) years. The really awful way to do this is to tell Python to calculate \(x(1+r)^{55}+x(1+r)^{54}+x(1+r)^{53}+...+x(1+r)^2+x(1+r)+x\). The programmer’s way to do this is to use the pattern we pointed out in the above table, and make use of a *for loop* that operates over a range of years.

If we wanted to loop over years \(0\) through \(3\), we could tell Python to use a `for`

loop to iterate over a list of the numbers \(0\) through \(3\). A `for`

loop has four components to it:

the word

`for`

: tells Python to execute a`for`

loopa variable name : this serves as a

*loop index*. You’ll see in a moment what this index does. A common name used here is the letter`i`

the word

`in`

: tells Python that the loop index will be contained within ‘’something’’.a list : this is the ‘’something’’ that we loop over. Every item in the list gets used once.

```
for i in [0, 1, 2, 3]:
print(i)
```

```
0
1
2
3
```

What a `for`

loop does is tells Python to loop over the list specified (in this case, the list of numbers \(0\) through \(4\)). For each item in that list, Python sets the loop index variable (in this case, we defined the index variable to be named `i`

) equal to that value of the list and then executes every step within the for loop (all of the indented lines of code underneath the line `for`

. Here, our for loop only specifies one action: print out the value of the loop index.

In our motivating example, we want Python to work over a long range of years. In this case, years \(0\) through \(55\).
This would be tedious to type out explicitly into `[0, 1, 2, ..., 54, 55]`

. Python has a trick for this, but there’s one caveat to keep in mind. As children, we learn to start counting with the number \(1\). Python, like most programming languages, starts counting with the number \(0\). Thus, the built-in function `range()`

uses this starts-at-zero convention.

```
for t in range(5):
print(t)
```

```
0
1
2
3
4
```

As demonstrated above, `range(5)`

tells Python that the loop should iterate over numbers \(0\) through \(4\). Generally, we use `range(n)`

to obtain numbers \(0\) through \(n-1\) (*not* \(0\) through \(n\)). Thus, we get \(n\) numbers out of `range(n)`

. If we want to iterate up through year \(55\), we would therefore use the statement `range(56)`

.

To calculate the value of your savings after \(55\) years, we simply have to type out:

```
savings = 0 # initially, 0 dollars in savings account
for i in range(56):
savings = savings*(1+r) + x # each year, pre-existings savings grows by (1+r) and an additional x is added
print(savings)
```

```
2094111.7408149564
```

## Lists¶

A Python `list`

holds a list of items in an ordered way. For example, a list of favorite things might include

```
favorite_things1 = ['raindrops on roses', 'whiskers on kittens', 'bright copper kettles', 'warm woolen mittens']
```

for Julie Andrews, or

```
favorite_things2 = ["breakfast at Tiffany's", 'bottles of bubbles', 'girls with tattoos who like getting in trouble']
```

for Ariana Grande.

When we say lists are ordered, it’s because we can enter `favorite_things1[0]`

and get back `'raindrops on roses'`

as Julie Andrew’s first favorite thing. Likewise `favorite_things2[1]`

tells us that `'bottles of bubbles'`

is Ariana Grande’s second favorite thing. Remember, Python starts counting at zero!

Lists can hold whatever you want. It could be strings (as shown above), numbers, or even other variables. Consider a list of daily stock returns

```
daily_stock_returns = [0.03, 0.01, -0.02, 0.01, -0.01]
```

Let’s input this into the following code block for use throughout this section.

```
daily_stock_returns = [0.03, 0.01, -0.02, 0.01, -0.01]
```

If we want the stock return for day 0, we simply type

```
daily_stock_returns[0]
```

```
0.03
```

and Python gives us back the value. Now suppose that the five daily stock returns corresponded to a week of stock return data. How do we convert this data into a weekly return?

In case the \(\Pi\) symbol is unfamiliar, it means multiply. Just like \(\sum_{i=1}^3 x_i\) means \(x_1 + x_2 + x_3\), the statement \(\Pi_{i=1}^3 x_i\) means \(x_1 * x_2 * x_3\).

The simple way to do this is

```
(-1) + (1+daily_stock_returns[0]) * (1+daily_stock_returns[1]) * (1+daily_stock_returns[2]) * (1+daily_stock_returns[3]) * (1+daily_stock_returns[4])
```

```
0.019392050600000044
```

That looks annoying to type out, doesn’t it? Now imagine calculating an annual return from 252 days of daily trading data. This would be prohbitively tedious.

Luckily, we’re learning how to program, which means we can take advantage of useful things like `for`

loops to make our lives easier.

```
weekly_return = 1
for i in range(0,5):
weekly_return = weekly_return*(1+daily_stock_returns[i])
weekly_return -= 1
print(weekly_return)
```

```
0.019392050600000044
```

Stop to think for a moment about why we set `weekly_return`

equal to \(1\) before beginning the `for`

loop.

Most, if not all, variable types in Python have special functions that exist for those variable types. For example, string variables contain text, and so these variables have `.upper()`

and `.lower()`

functions that can be used with them.

```
my_str = 'Finance Data Science'
print(my_str.upper())
```

```
FINANCE DATA SCIENCE
```

```
print(my_str.lower())
```

```
finance data science
```

For list variables, two important functions are `.append()`

and `.pop()`

. These functions add and remove data from the list, respectively.

```
my_list = [1, 2, 3]
my_list.append(4)
print(my_list)
```

```
[1, 2, 3, 4]
```

The `.append()`

function takes as an argument the data that you want to append to a list, and then adds that data to the end of an existing list. Note that, as with `.upper()`

or `.lower()`

for strings, here the function starts with a `.`

and follows the variable name. This is different than using a function like `print()`

or, from earlier in this chapter, `wacc()`

. The difference exists because `.append()`

is a function defined specifically for list variable types.

```
my_list = [1, 2, 3]
my_list.pop()
print(my_list)
```

```
[1, 2]
```

Because `.pop()`

removes data from a list, you don’t necessarily need to say *what* data gets deleted. By default, if you do not supply an argument to `.pop()`

Python will delete the last item from the list. To specify an element to delete, select it based on that elements position in the list.

```
my_list = [1, 2, 3]
my_list.pop(0)
print(my_list)
```

```
[2, 3]
```

In Python, `for`

loops operate on lists of information. For example, the command `for i in range(5)`

tells Python to operate on the list of numbers from \(0\) to \(4\). Likewise, `for i in ['cat', 'dog', 'bird']`

tells Python to operate on the list of strings ‘cat’, ‘dog’, and ‘bird’. Notice, therfore, that the list of items we loop over does not have to use numbers! For each element in the given list, Python applies certain commands to that item. These commands are the lines of code that appear within a `for`

loop (the indented section of code that follows the `for`

statement.

When the `for`

loop operation is only a single line long, there is a special shortcut that we can use. This shortcut is called **list comprehension**. Consider the task of subtracting the risk free rate from the list of daily returns in order to compute an excess return. For simplicity, assume the risk free rate is constant and equal to \(0.01\). One way of achieving this is with the following `for`

loop.

```
excess_returns = []
for i in range(5):
excess_returns.append( daily_stock_returns[i] - 0.01 )
print(excess_returns)
```

```
[0.019999999999999997, 0.0, -0.03, 0.0, -0.02]
```

Before showing off list comprehension, let’s stop and think about one potential issue with the above code. In the `for`

loop above, we explicity refer to the number \(5\) when defining the range of the loop. That’s a bad idea. What if we add more stock return data to our list, and then re-run the above code? In that scenario, the `for`

loop won’t execute as intended, since it will still only calculate excess returns for the first five days.

A better way to loop over a list is to use the `len()`

function (‘len’ stands for ‘length’, but Python is nice and only expects us to type out three characters instead of six). The function `len()`

can calculate the length of a list.

```
len(daily_stock_returns)
```

```
5
```

Hence, we improve uppon our `for`

loop above by removing the explicit reference to the number \(5\).

```
excess_returns = []
for i in range( len(daily_stock_returns) ):
excess_returns.append( daily_stock_returns[i] - 0.01 )
print(excess_returns)
```

```
[0.019999999999999997, 0.0, -0.03, 0.0, -0.02]
```

Now, the above code will work as expected, regardless of how many items are in the `daily_stock_returns`

list.

An alternative to using `len()`

and referencing list elements by their index position (i.e. doing `daily_stock_returns[i]`

) is to simply tell Python that we want to loop over `daily_stock_returns`

. Thus, rather than telling Python we want to loop over `range(5)`

or over `range( len(daily_stock_returns) )`

, we can simply tell Python to loop over `daily_stock_returns`

directly.

```
for i in daily_stock_returns:
print(i)
```

```
0.03
0.01
-0.02
0.01
-0.01
```

Thus, an alternative improvement to the `for`

loop is to do the following.

```
excess_returns = []
for i in daily_stock_returns:
excess_returns.append( i - .01 )
print(excess_returns)
```

```
[0.019999999999999997, 0.0, -0.03, 0.0, -0.02]
```

If you don’t need to use the list index position inside the `for`

loop (as is the case here), it’s better to loop over the list directly, rather than a list of numbers coresponding to the index positions. The reason is that this form of the `for`

loop is the cleanest. It requires the least amount of typing, and it is the easiest to read.

Now that we’ve seen that we can loop over a list directly (rather than looping over index positions for the list), we can explain list comprehension. List comprehension takes a simple `for`

loop (with one line of code inside the loop) and converts the loop into a neater, shorter piece of code. Consider the following example.

```
[i for i in daily_stock_returns]
```

```
[0.03, 0.01, -0.02, 0.01, -0.01]
```

The statment is wrapped up in square brackets, indicating a `list`

variable. Within these square brackets, we tell Python to add to this list the value `i`

, where we define `i`

as any element of `daily_stock_returns`

.

Rather than adding simply `i`

to this new list, we could add some function of `i`

. For instance, we can substract the risk free rate.

```
[i - .01 for i in daily_stock_returns]
```

```
[0.019999999999999997, 0.0, -0.03, 0.0, -0.02]
```

Hence, the easiest way to compute excess returns is with the simple, one-line statement:

```
excess_returns = [i - .01 for i in daily_stock_returns]
print(excess_returns)
```

```
[0.019999999999999997, 0.0, -0.03, 0.0, -0.02]
```

Because list comprehension is just a shortcut to writting out a simple `for`

loop, a person could use Python perfectly fine without knowing how to use list comprehension. However, it’s a popular shortcut, so we highlight it here because it will be used throught this text.

`for`

Loops and Delayed Savings¶

Consider a slightly modified version of the lifetime savings problem provided above.

Once you begin contributing to a savings account, assume that you put away \(x\) dollars per year and expect an annual return of \(r\). How valuable is it to start saving now as opposed to \(10\) years from now?

Again, begin by defining some values for \(x\) and \(r\).

```
x = 5000
r = 0.06
```

Next, define two list variables. These will store the value of savings accounts over time. In the previous example, we updated the value for the savings account each year. Here, we record the value of the account at each point in time. This is useful because we can then plot the data and explore this delayed savings problem more carefully.

```
save_now = [x] # value of saving account when savings starts immediately
save_later = [0] # value of saving account when savings starts 10 years from now
```

These list variables store one value each (the value \(x\) and the value \(0\)), which corresponds to the value of the savings account at the beginning of year \(0\). Under the “save-now” plan, \(x\) is put away immediately. Under the “save-later” plan, \(x\) is not put away until year 10.

Now, what we want to do is think about what happens each year. In the first scenario (begin saving immediately), the savings account grows by `(1+r)`

each year, plus an additional `x`

that gets added in. In the second scenario, the savings account only begins growing after year 10, and contributions only begin in year 10. Before that, nothing happens.

Using `if`

/`else`

logic, a `for`

loop, and the `.append()`

function for lists, we can evaluate these two savings plans.

We’ll begin with the “save-now” savings plan, since this is similar to the problem from the beginning of the chapter. A difference implemented here is that we call the `.append()`

function to add values to the `save_now`

list.

```
for i in range(1,56): # loop over i from 1 to 55
account_value_last_year = save_now[i-1] # e.g. for i = 1 (year 1), loop up account value at i = 0 (year 0)
account_value_next_year = account_value_last_year * (1+r) + x
save_now.append( account_value_next_year )
```

At the beginning of the chapter, we defined a variable `savings`

, and updated that variable’s value each loop iteration. Here, the list variable `save_now`

keeps track of *each* of the savings account values (that is, the value of the account at year 0, at year 1, …, at year 55). This is useful for plotting a figure, which we’ll do at the end of the chapter.

Next, we modify the above `for`

loop to work on the “save-later” plan. The important distinction here is that, for the first ten years (list index values \(0-9\), since Python starts counting at \(0\)), no money is put in to the savings account. Thus, we set the first ten values of the `save_later`

list equal to \(0\).

```
for i in range(1,56):
if i > 9:
save_later.append( save_later[i-1]*(1+r) + x )
else:
save_later.append( 0 )
```

Plotting will be covered in greater detail in a later chapter, so don’t worry about understanding the meaning of the following code block. The important takeaway is what’s presented in the figure it creates.

```
%matplotlib inline
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
plt.plot(range(0,56), save_now, color='b', label='Now')
plt.plot(range(0,56), save_later, color='r', label='Later', linestyle='--')
plt.legend()
plt.show()
```