Control Flow#
Prerequisites
Booleans section in Basics
Outcomes
Asset pricing and NPV
Understand basic principles of pricing assets with deterministic payoffs
Apply programming with iteration and conditionals to asset pricing examples
Conditionals
Understand what a conditional is
Be able to construct
if
/elif
/else
conditional blocksUnderstand how conditionals can be used to selectively execute blocks of code
Iteration
Understand what an iterable is
Be able to write
for
andwhile
loopsUnderstand the keywords
break
andcontinue
Net Present Values and Asset Pricing#
In this lecture, we’ll introduce two related topics from economics:
Net present valuations
Asset pricing
These topics will motivate some of the programming we do in this course.
In economics and finance, “assets” provide a stream of payoffs.
These “assets” can be concrete or abstract: a stock pays dividends over time, a bond pays interest, an apple tree provides apples, a job pays wages, and an education provides possible jobs (which, in turn, pay wages).
When deciding the price to pay for an asset or how to choose between different alternatives, we need to take into account that most people would prefer to receive 1 today vs. 1 next year.
This reflection on consumer preferences leads to the notion of a discount rate. If you are indifferent between receiving 1.00 today and 1.10 next year, then the discount rate over the next year is \(r = 0.10\).
If we assume that an individuals preferences are consistent over time, then we can apply that same discount rate to valuing assets further into the future.
For example, we would expect that the consumer would be indifferent between consuming 1.00 today and \((1+r)(1+r) = 1.21\) dollars two years from now (i.e. discount twice).
Inverting this formula, 1 delivered two years from now is equivalent to \(\frac{1}{(1+r)^2}\) today.
Exercise
See exercise 1 in the exercise list.
Net Present Value#
If an asset pays a stream of payoffs over multiple time periods, then we can use a discount rate to calculate the value to the consumer of a entire sequence of payoffs.
Most generally, we enumerate each discrete time period (e.g. year, month, day) by the index \(t\) where today is \(t=0\) and the asset lives for \(T\) periods.
List the payoff at each time period as \(y_t\), which we will assume, for now, is known in advance.
Then if the discount factor is \(r \geq 0\), the consumer “values” the payoff \(y_t\) delivered at time \(t\) as \(\frac{1}{(1+r)^t}y_t\) where we note that if \(t=0\), the value is just the current payoff \(y_0\).
Using this logic, we can write an expression for the value of the entire sequence of payoffs with a sum.
If \(y_t\) is a constant, then we can compute this sum with a simple formula!
Below, we present some useful formulas that come from infinite series that we will use to get our net present value formula.
For any constant \(0 < \beta < 1\) and integer value \(\tau > 0\),
In the case of an asset which pays one dollar until time \(T\), we can use these formulas, taking \(\beta = \frac{1}{1+r}\) and \(T = \tau\), to find
Note that we can also consider an asset that lives and pays forever if \(T= \infty\), and from (2), the value of an asset which pays 1 forever is \(\frac{1+r}{r}\).
Conditional Statements and Blocks#
Sometimes, we will only want to execute some piece of code if a certain condition is met.
These conditions can be anything.
For example, we might add to total sales if the transaction value is positive, but add to total returns if the value is negative.
Or, we might want to add up all incurred costs, only if the transaction happened before a certain date.
We use conditionals to run particular pieces of code when certain criterion are met.
Conditionals are closely tied to booleans, so if you don’t remember what those are, go back to the basics lecture for a refresher.
The basic syntax for conditionals is
if condition:
# code to run when condition is True
else:
# code to run if no conditions above are True
Note that immediately following the condition, there is a colon and that the next line begins with blank spaces.
Using 4 spaces is a very strong convention, so that is what we do — we recommend that you do the same.
Also note that the else
clause is optional.
Let’s see some simple examples.
if True:
print("This is where `True` code is run")
This is where `True` code is run
Alternatively, you could have a test which returns a booleans
if 1 < 2:
print("This is where `True` code is run")
This is where `True` code is run
This example is equivalent to just typing the print statement, but the example below isn’t…
if False:
print("This is where `True` code is run")
Or
if 1 > 2:
print("This is where `True` code is run")
Notice that when you run the cells above nothing is printed.
That is because the condition for the if
statement was not true, so the code
inside the indented block was never run.
This also allows us to demonstrate the role of indentation in determining the “block” of code.
val = False
if val is True: # check an expression
print("This is where `True` code is run")
print("More code in the if block")
print("Code runs after 'if' block, regardless of val")
Code runs after 'if' block, regardless of val
Exercise
See exercise 2 in the exercise list.
The next example shows us how else
works.
val = (2 == 4) # returns False
if val is True:
print("This is where `True` code is run")
else:
print("This is where `False` code is run")
print("More else code")
print("Code runs after 'if' block, regardless of val")
This is where `False` code is run
More else code
Code runs after 'if' block, regardless of val
The if False: ...
part of this example is the same as the example
before, but now, we added an else:
clause.
In this case, because the conditional for the if
statement was not
True
, the if code block was not executed, but the else
block was.
Finally, the Condition is True
is assumed in the if
statement, and is often left out. For example, the following are identical
if (1 < 2) is True:
print("1 < 2")
if 1 < 2:
print("1 < 2")
1 < 2
1 < 2
Exercise
See exercise 3 in the exercise list.
Exercise
See exercise 4 in the exercise list.
elif
clauses#
Sometimes, you have more than one condition you want to check.
For example, you might want to run a different set of code based on which quarter a particular transaction took place in.
In this case you could check whether the date is in Q1, or in Q2, or in Q3, or if not any of these it must be in Q4.
The way to express this type of conditional is to use one or more elif
clause in addition to the if
and the else
.
The syntax is
if condition1:
# code to run when condition1 is True
elif condition2:
# code to run when condition2 is True
elif condition3:
# code to run when condition3 is True
else:
# code to run when none of the above are true
You can include as many elif
clauses as you want.
As before, the else
part is optional.
Here’s how we might express the quarter example referred to above.
import datetime
halloween = datetime.date(2017, 10, 31)
if halloween.month > 9:
print("Halloween is in Q4")
elif halloween.month > 6:
print("Halloween is in Q3")
elif halloween.month > 3:
print("Halloween is in Q2")
else:
print("Halloween is in Q1")
Halloween is in Q4
Note that when there are multiple if
or elif
conditions, only the code
corresponding to the first true clause is run.
We saw this in action above.
We know that when halloween.month > 9
is true, then halloween.month > 6
and halloween.month > 3
must also be true, but only the code block
associated with halloween.month > 9
was printed.
Iteration#
When doing computations or analyzing data, we often need to repeat certain operations a finite number of times or until some condition is met.
Examples include processing all data files in a directory (folder), aggregating revenues and costs for every period in a year, or computing the net present value of certain assets. (In fact, later in this section, we will verify the equations that we wrote down above.)
These are all examples of a programming concept called iteration.
We feel the concept is best understood through example, so we will present a contrived example and then discuss the details behind doing iteration in Python.
A Contrived Example#
Suppose we wanted to print out the first 10 integers and their squares.
We could do something like this.
print(f"1**2 = {1**2}")
print(f"2**2 = {2**2}")
print(f"3**2 = {3**2}")
print(f"4**2 = {4**2}")
# .. and so on until 10
1**2 = 1
2**2 = 4
3**2 = 9
4**2 = 16
As you can see, the code above is repetitive.
For each integer, the code is exactly the same except for the two places where the “current” integer appears.
Suppose that I asked you to write the same print statement for an int stored in
a variable named i
.
You might write the following code:
print(f"{i}**2 = {i**2}")
This more general version of the operation suggests a strategy for achieving our
goal with less repetition: have a variable i
take on the values 1 through 10
(Quiz: How can we use range
to create the numbers 1 to 10?) and run the line
of code above for each new value of i
.
This can be accomplished with a for
loop!
for i in range(1, 11):
print(f"{i}**2 = {i**2}")
1**2 = 1
2**2 = 4
3**2 = 9
4**2 = 16
5**2 = 25
6**2 = 36
7**2 = 49
8**2 = 64
9**2 = 81
10**2 = 100
Whoa, what just happened?
The integer i
took on the values in range(1, 11)
one by one and
for each new value it did the operations in the indented block (here
just one line that called the print
function).
for
Loops#
The general structure of a standard for
loop is as follows.
for item in iterable:
# operation 1 with item
# operation 2 with item
# ...
# operation N with item
where iterable
is anything capable of producing one item at a time (see
here for official
definition from the Python team).
We’ve actually already seen some of the most common iterables!
Lists, tuples, dicts, and range/zip/enumerate objects are all iterables.
Note that we can have as many operations as we want inside the indented block.
We will refer to the indented block as the “body” of the loop.
When the for loop is executed, item
will take on one value from iterable
at a time and execute the loop body for each value.
Exercise
See exercise 5 in the exercise list.
When iterating, each item
in iterable
might actually contain more than
one value.
Recall that tuples (and lists) can be unpacked directly into variables.
tup = (4, "test")
i, x = tup
print(f"i = {i}, x = {x}, tup = {tup}")
i = 4, x = test, tup = (4, 'test')
Also, recall that the value of a enumerate(iterable)
is a tuple of the
form (i, x)
where iterable[i] == x
.
When we use enumerate
in a for loop, we can “unpack” both values at the same
time as follows:
# revenue by quarter
company_revenue = [5.12, 5.20, 5.50, 6.50]
for index, value in enumerate(company_revenue):
print(f"quarter {index} revenue is ${value} million")
quarter 0 revenue is $5.12 million
quarter 1 revenue is $5.2 million
quarter 2 revenue is $5.5 million
quarter 3 revenue is $6.5 million
Similarly, the index can be used to access another vector.
cities = ["Phoenix", "Austin", "San Diego", "New York"]
states = ["Arizona", "Texas", "California", "New York"]
for index, city in enumerate(cities):
state = states[index]
print(f"{city} is in {state}")
Phoenix is in Arizona
Austin is in Texas
San Diego is in California
New York is in New York
Exercise
See exercise 6 in the exercise list.
while
Loops#
A related but slightly different form of iteration is to repeat something until some condition is met.
This is typically achieved using a while
loop.
The structure of a while loop is
while True_condition:
# repeat these steps
where True_condition
is some conditional statement that should evaluate to
True
when iterations should continue and False
when Python should stop
iterating.
For example, suppose we wanted to know the smallest N
such that
\(\sum_{i=0}^N i > 1000\).
We figure this out using a while loop as follows.
total = 0
i = 0
while total <= 1000:
i = i + 1
total = total + i
print("The answer is", i)
The answer is 45
Let’s check our work.
# Should be just less than 1000 because range(45) goes from 0 to 44
sum(range(45))
990
# should be between 990 + 45 = 1035
sum(range(46))
1035
A warning: one common programming error with while loops is to forget to set the variable you use in the condition prior to executing. For example, take the following code which correctly sets a counter
i = 0
And then executes a while loop
while i < 3:
print(i)
i = i + 1
print("done")
0
1
2
done
No problems. But if you were to execute the above cell again, or another cell, the i=3
remains, and code is never executed (since i < 3
begins as False).
while i < 3:
print(i)
i = i + 1
print("done")
done
Exercise
See exercise 7 in the exercise list.
break
and continue
#
break
Out of a Loop#
Sometimes we want to stop a loop early if some condition is met.
Let’s revisit the example of finding the smallest N
such that
\(\sum_{i=0}^N i > 1000\).
Clearly N
must be less than 1000, so we know we will find the answer
if we start with a for
loop over all items in range(1001)
.
Then, we can keep a running total as we proceed and tell Python to stop iterating through our range once total goes above 1000.
total = 0
for i in range(1001):
total = total + i
if total > 1000:
break
print("The answer is", i)
The answer is 45
Exercise
See exercise 8 in the exercise list.
continue
to the Next Iteration#
Sometimes we might want to stop the body of a loop early if a condition is met.
To do this we can use the continue
keyword.
The basic syntax for doing this is:
for item in iterable:
# always do these operations
if condition:
continue
# only do these operations if condition is False
Inside the loop body, Python will stop that loop iteration of the loop and continue directly to the next iteration when it encounters the continue
statement.
For example, suppose I ask you to loop over the numbers 1 to 10 and print out
the message “{i} An odd number!” whenever the number i
is odd, and do
nothing otherwise.
You can use continue to do this as follows:
for i in range(1, 11):
if i % 2 == 0: # an even number... This is modulus division
continue
print(i, "is an odd number!")
1 is an odd number!
3 is an odd number!
5 is an odd number!
7 is an odd number!
9 is an odd number!
Exercise
See exercise 9 in the exercise list.
Comprehension#
Often, we will want to perform a very simple operation for every element of some iterable and create a new iterable with these values.
This could be done by writing a for loop and saving each value, but often using what is called a comprehension is more readable.
Like many Python concepts, a comprehension is easiest to understand through example.
Imagine that we have a list x
with a list of numbers. We would like to create a list x2
which has
the squared values of x.
x = list(range(4))
# Create squared values with a loop
x2_loop = []
for x_val in x:
x2_loop.append(x_val**2)
# Create squared values with a comprehension
x2_comp = [x_val**2 for x_val in x]
print(x2_loop)
print(x2_comp)
[0, 1, 4, 9]
[0, 1, 4, 9]
Notice that much of the same text appears when we do the operation in the loop and when we do the operation with the comprehension.
We need to specify what we are iterating over – in both cases, this is
for x_val in x
.We need to square each element
x_val**2
.It needs to be stored somewhere – in
x2_loop
, this is done by appending each element to a list, and inx2_comp
, this is done automatically because the operation is enclosed in a list.
We can do comprehension with many different types of iterables, so we demonstrate a few more below.
# Create a dictionary from lists
tickers = ["AAPL", "GOOGL", "TVIX"]
prices = [175.96, 1047.43, 8.38]
d = {key: value for key, value in zip(tickers, prices)}
d
{'AAPL': 175.96, 'GOOGL': 1047.43, 'TVIX': 8.38}
# Create a list from a dictionary
d = {"AMZN": "Seattle", "TVIX": "Zurich", "AAPL": "Cupertino"}
hq_cities = [d[ticker] for ticker in d.keys()]
hq_cities
['Seattle', 'Zurich', 'Cupertino']
import math
# List from list
x = range(10)
sin_x = [math.sin(x_val) for x_val in x]
sin_x
[0.0,
0.8414709848078965,
0.9092974268256817,
0.1411200080598672,
-0.7568024953079282,
-0.9589242746631385,
-0.27941549819892586,
0.6569865987187891,
0.9893582466233818,
0.4121184852417566]
Exercise
See exercise 10 in the exercise list.
Finally, we can use this approach to build complicated nested dictionaries.
gdp_data = [9.607, 10.48, 11.06]
years = [2013, 2014, 2015]
exports = [ {"manufacturing": 2.4, "agriculture": 1.5, "services": 0.5},
{"manufacturing": 2.5, "agriculture": 1.4, "services": 0.9},
{"manufacturing": 2.7, "agriculture": 1.4, "services": 1.5}]
data = zip(years, gdp_data,exports)
data_dict = {year : {"gdp" : gdp, "exports": exports} for year, gdp, exports in data}
print(data_dict)
# total exports by year
[data_dict[year]["exports"]["services"] for year in data_dict.keys()]
{2013: {'gdp': 9.607, 'exports': {'manufacturing': 2.4, 'agriculture': 1.5, 'services': 0.5}}, 2014: {'gdp': 10.48, 'exports': {'manufacturing': 2.5, 'agriculture': 1.4, 'services': 0.9}}, 2015: {'gdp': 11.06, 'exports': {'manufacturing': 2.7, 'agriculture': 1.4, 'services': 1.5}}}
[0.5, 0.9, 1.5]
Exercises#
Exercise 1#
Government bonds are often issued as zero-coupon bonds meaning that they make no payments throughout the entire time that they are held, but, rather make a single payment at the time of maturity.
How much should you be willing to pay for a zero-coupon bond that paid 100 in 10 years with an interest rate of 5%?
# your code here
Exercise 2#
Run the following two variations on the code with only a single change in the indentation.
After, modify the x
to print 3
and then 2, 3
instead.
x = 1
if x > 0:
print("1")
print("2")
print("3")
x = 1
if x > 0:
print("1")
print("2") # changed the indentation
print("3")
Exercise 3#
Using the code cell below as a start, print "Good afternoon"
if the
current_time
is past noon.
Otherwise, do nothing.
Hint
Write some conditional based on current_time.hour
.
import datetime
current_time = datetime.datetime.now()
## your code here
more text after
Exercise 4#
In this example, you will generate a random number between 0 and 1 and then display “x > 0.5” or “x < 0.5” depending on the value of the number.
This also introduces a new package numpy.random
for
drawing random numbers (more in the randomness lecture).
import numpy as np
x = np.random.random()
print(f"x = {x}")
## your code here
Exercise 5#
In economics, when an individual has some knowledge, skills, or education which provides them with a source of future income, we call it human capital.
When a student graduating from high school is considering whether to continue with post-secondary education, they may consider that it gives them higher paying jobs in the future, but requires that they don’t begin working until after graduation.
Consider the simplified example where a student has perfectly forecastable employment and is given two choices:
Begin working immediately and make 40,000 a year until they retire 40 years later.
Pay 5,000 a year for the next 4 years to attend university, then get a job paying 50,000 a year until they retire 40 years after making the college attendance decision.
Should the student enroll in school if the discount rate is r = 0.05?
# Discount rate
r = 0.05
# High school wage
w_hs = 40_000
# College wage and cost of college
c_college = 5_000
w_college = 50_000
# Compute npv of being a hs worker
# Compute npv of attending college
# Compute npv of being a college worker
# Is npv_collegeworker - npv_collegecost > npv_hsworker
Exercise 6#
Instead of the above, write a for loop that uses the lists of cities
and states below to print the same “{city} is in {state}” using
a zip
instead of an enumerate
.
Hint
Try using zip
cities = ["Phoenix", "Austin", "San Diego", "New York"]
states = ["Arizona", "Texas", "California", "New York"]
# Your code here
Exercise 7#
Companies often invest in training their employees to raise their productivity. Economists sometimes wonder why companies spend this money when this incentivizes other companies to hire their employees away with higher salaries since employees gain human capital from training?
Let’s say that it costs a company 25,000 dollars to teach their employees Python, but it raises their output by 2,500 per month. How many months would an employee need to stay for the company to find it profitable to pay for their employees to learn Python if their discount rate is r = 0.01?
# Define cost of teaching python
cost = 25_000
r = 0.01
# Per month value
added_value = 2500
n_months = 0
total_npv = 0.0
# Put condition below here
while False: # (replace False with your condition here)
n_months = n_months + 1 # Increment how many months they've worked
# Increase total_npv
Exercise 8#
Try to find the index of the first value in x
that is greater than 0.999 using a for loop and break
.
Hint
try iterating over range(len(x))
.
x = np.random.rand(10_000)
# Your code here
Exercise 9#
Write a for loop that adds up all values in x
that are greater than
or equal to 0.5.
Use the continue
word to end the body of the loop early for all values
of x
that are less than 0.5.
Hint
Try starting your loop with for value in x:
instead of
iterating over the indices of x
.
x = np.random.rand(10_000)
# Your code here
Exercise 10#
Returning to our previous example: print “{city} is in {state}” for each combination
using a zip
and a comprehension.
Hint
Try using zip
cities = ["Phoenix", "Austin", "San Diego", "New York"]
states = ["Arizona", "Texas", "California", "New York"]
# your code here