In [ ]:
## future import
from __future__ import division, print_function
In this unit, we are going to learn a new data structure with richer features compared to lists. The problem with lists, while flexible enough to store different data types in one variable, is that it does not provide us a way to "label" that information with meta data. For example in a list ['Andy', 28, 980.15]
, we would like to be able to associate with the value Andy
a label or key 'name'
so that we don't have to keep on recalling that a staff's name is located in the first position of the list.
Secondly, we will cover the for
and if else
construct.
Technically speaking, armed with the basic tools that we have learnt from the previous unit, we could go about writing code and basic scripts. But with these keywords, what can achieved is rather limited. Worse, the process of writing code will be tedious and unenlightening. From a scientific and analytic point of view, writing code is not only an ends to obtaining the results we want, but a way for us to structure our thinking and come to an understanding of the problem.
To use the dict
data structure and related methods.
To apply boolen conditionals to control if
, if else
and if elif
statements.
To use the for
loop to iterate through repetitive calculations.
Lists - while easy to create- have the weakness that one cannot easily retrieve data that has been already stored in it. Since the primary means of retrieving information in a list is via indexing and slicing, you need to know the exact integer positions of each data stored in the list. This can (and will often) lead to human errors in programming. Furthermore, an integer based recall is unenlightening. Other people who reads your code will find it difficult to understand what is being written.
To remedy this, Python has built into its base package a data structure called dictionaries. A dictionary is simply a key-value pairing like so $$(key_1, value_1), (key_2, value_2)\ldots, (key_n, value_n)$$ where $key_i$ are usually (but not always) strings and $value_i$ any Python object (int
, str
, list
and even other dict
!)
If my_dictionary
is a dictionary. Then calling my_dictionary[key_1]
will return you the value associated with key_1
in my_dictionary
, say value_1
.
Dictionaries are created using curly braces { }
. Inside the curly braces, we simply list down all the key-value pairs with a colon :
. Different pairs are seperated by a ,
.
In [ ]:
# creating a dictionary and assigning it to a variable
staff = {'name': 'Andy', 'age': 28, 'email': 'andy@company.com' }
In [ ]:
staff['name']
In [ ]:
staff['age']
In [ ]:
print(staff['email'])
In [ ]:
# A dictionary is of class dict
print(type(staff))
In [ ]:
# list of all keys, note the brackets at the end.
# .keys is a method associated to dictionaries
staff.keys()
In [ ]:
# list of all values, in no particular order
staff.values()
In [ ]:
# list all key-value pairings using .items
staff.items()
In [ ]:
# Hey, Andy mistakenly keyed in his age. He is actually 29 years old!
staff['age'] = 29
print(staff)
In [ ]:
# HR wants us to record down his staff ID.
staff['id'] = 12345
print(staff)
In [ ]:
# Let's check the list of keys
staff.keys()
An implication of this is that we can start with an empty dictionary and add keys as we go along.
And empty dictionary is created by assigning a variable to an instance of class dict
by calling dict()
.
In [ ]:
favourite_food = dict() # You could also type favourite_food = {}
print(favourite_food)
In [ ]:
# update your dictionary here
# and print the dictionary
print(favourite_food)
In [ ]:
staff.update({'salary': 980.15, 'department':'finance', 'colleagues': ['George', 'Liz']})
In [ ]:
# Who are Andy's colleagues? Enter answer below
In [ ]:
# Which department does he work in? Enter answer below
In [ ]:
my_favourite_things = dict(food="Assam Laksa", music="classical", number = 2)
In [ ]:
# my favourite number
my_favourite_things['number']
Note from the example above that when creating dictionaries, DO NOT enclose the keys within quotation marks. However, when accessing the value of a dictionary by its key, you MUST use quotation marks.
In [ ]:
# An error
my_favourite_things[food]
In [ ]:
# ...but this is correct
food = 'food'
my_favourite_things[food]
If
and boolen conditionalsAny algorithm worth its salt will have conditionals in it. Very rarely does an algorithm just proceed in a linear fashion, one step following another step. More often than not, one step will follow from another if some condition is satisfied.
This situation can be programmed in Python by using the if
keyword. The general construct of an if
statement looks like this:
if
condition :
do something
Python will only execute the code block do something
only if condition
evaluate to True
. Otherwise it ignores it. Notice the lack of { }
surrounding the code block to be executed. The Python interpreter relies on indentation and newline to "know" which code block is conditioned on.
This enforces good coding style and makes it so nice to program with Python. No more worrying about unmatched braces!
Since conditionals are logical statements, we need some binary comparison operators
==
logical equals to >
strictly greater than<
strictly less than!=
logical not equals to We can create compound statements by using the keywords or
and and
. Their bitwise versions are |
and &
.
In [ ]:
# examples of binary comparison
1 < 2
In [ ]:
# compound statements
1 < 2 or 1 == 2
In [ ]:
# using bitwise operators
1<2 & 1==2
Here is our first example using the if
keyword.
In [ ]:
x = 300
if x == 300:
print('This is Sparta!')
The variable x
has been assigned the value 300
. When Python sees the statement if x == 300:
Python checks the conditional x==300
and evaluates it. Since x was assigned the value 300
, the conditional is indeed True
. When this happens, it executes the indented code block below the conditional, in this case print
the string 'This is Sparta!'
In [ ]:
y = 13
print(y)
if y % 2 == 0:
print('This is an even number')
print("I guess it's odd then")
The conditional checks and see whether the remainder of 13 when divided by 2 is 0. (Recall that's what %
does.) Since 13 returns 1 remainder when divided by 2, the condition is false. Hence the string 'This is an even number'
is not printed.
if else
statementsIf you look at the previous example, one can see an obvious problem with this block of code. What if y=14
? Then the output would consist of two print statements 'This is an even number'
and followed by "I guess it's odd then"
. This is not desireable. In fact we want to print 'This is an even number'
only when y
is even and "I guess it's odd then"
only when y
is odd. To code this into Python, we use the else
keyword.
Code blocks under the else
keyword are executed when the conditionals evaluate to False
.
The format of of if else
statements are as below:
if
condition:
do something
else
:
do something else
In [ ]:
y = 22
if y%2 ==0:
print("{} is an even number".format(y))
else:
print("{} is an odd number".format(y))
In [ ]:
y = 13
if y%2 ==0:
print("{} is an even number".format(y))
else:
print("{} is an odd number".format(y))
In [ ]:
# Nested if else statements
y = 25
remainder = y%3
if remainder == 0:
print("{} is divisible by 3".format(y))
else:
print("{} is not divisible by 3".format(y))
if remainder ==1:
print("But has remainder {}".format(remainder))
else:
print("But has remainder {}".format(remainder))
Try re-executing the cell above with various values of y
and see the effect on the output.
Nested if else
statements make for horrible coding. It makes code hard to read and understand. Surely there must be a better way!
elif
to rescueWe use elif
when we need to test conditionals in a sequential manner. A sequence of conditionals is tested, one after another until the first True
condition is encountered. Then, the code block corresponding to that conditional is executed and program continues on without checking the other conditionals.
The format of elif
statements looks like this:
if
$condition_1$:
do code 1
elif
$condition_2$:
do code 2
$\vdots$
elif
$condition_n$:
do code n
elif
chains can be "terminated" either by an elif
statement itself or by an else
if there is no final conditional to be evaluated.
Let's try to refactor (recode) the nested if else
statements above using the elif
keyword.
In [ ]:
y=25
remainder = y%3
if remainder == 0:
div = 'is'
s = 'Hence'
elif remainder == 1:
div = 'is not'
s = 'But'
elif remainder == 2:
div = 'is not'
s = 'But'
print('{} {} divisible by 3\n{} has remainder {}'.format(y, div, s, remainder))
See how much more elegant this is instead of nested if else
statements.
Now let's try another example. This time we use elif
to program Python to assign letter grades to student marks on an an exam.
Here are the letter grade and their assigned intervals.
Grades | Interval |
---|---|
A | [80, 100] |
B | [70, 80) |
C | [60, 70) |
D | [50, 60) |
E | [45, 50) |
F | [0, 45) |
In [ ]:
marks = 78.35
if marks >= 80:
grade = 'A'
elif marks >= 70:
grade = 'B'
elif marks >= 60:
grade = 'C'
elif marks >= 50:
grade = 'D'
elif marks >= 45:
grade = 'E'
else:
grade = 'F'
print('Student obtained %.1f marks in the test and hence is awarded %s for this module' % (marks, grade))
As before, play around with the various values of marks to make sure that elif
structure is working as intended. Notice that I phrased the conditional only to check agains a lower bound. This is because Python will only execute the code block corresponding to first True
conditional in the elif
sequence. Even if subsequent conditionals evaluate to true, their code is not run.
for
loopMost algorithms consist of repeating a certain calculations or computer tasks in an almost similiar manner. To give a scenario, I could instruct Python to print
the names of three staff members to the output. I could go about fulfilling this task by writing the following code
print("Staff member Lisa")
print("Staff member Mark")
print("Staff member Andy")
Clearly this is repetitive and tedious. The for
keyword allows us to simplify code by writing a single intruction - here print
and looping this over a list
consisting of staff names [Lisa, Mark, Andy]
.
The general format of a for
loop is given by the following:
for item_n in list:
do code substituted with item_n
for
loop worksLet's start by doing the above by looping over the index of the list. The way it is usually done in Javascript or C is to declare a "counter variable" i
and increment it by one to perform the next loop of the algorithm. We could do something like this in Python.
In [ ]:
staff = ['Lisa', 'Mark', 'Andy']
for i in range(0,3): # range(0,3) is a function that produces a sequence of numbers in the form of a list: [0,1,2]
print("Staff member "+staff[i])
But Python allows us to write a more readable form of the for loop. So the following is equivalent to the above and is preferred.
In [ ]:
for staff_name in staff:
print("Staff member "+ staff_name)
Note that the "counter variable" staff_name
is actually a variable containing the current item in the list as the loop progresses. I could have used any name I wanted for the variable - I could use i
to represent the staff's name. But I chose staff_name
for readability purposes. As the variable staff_name
runs through each item of staff
, the code block print
is executed with the current value of staff_name
. Once that is done, the variable is updated with the next item in the list and the block is executed once more. This proceeds until the end of the list is reached and the for
loop terminates.
for
and if
statementsTo further control what each iteration of the for
loop does, we can combine if
statements within for
statements to trigger certain instructions when a particular iteration fulfils certain conditions.
If we need to exit a for
loop prematurely, we can use the break
keyword. This is usually used in conjuction with if
. When conditions for break
is fulfilled, the for
loop terminates immediately. Python will not evaluate for
statements for remaining items in the iterating list.
In [ ]:
# A common programming interview task. Print 'foo' if x is divisible by 3 and 'bar' if it is divisible by 5 and 'baz'
# if x is divisible by both 3 and 5. Do this for numbers 1 to 15.
for num in range(1,16): # range(1,16) produces a list of numbers started from 1 and ending at 15.
if num % 3 == 0 and num % 5 !=0:
print('%d foo' % (num))
elif num % 5 == 0 and num % 3 != 0:
print('%d bar' % (num))
elif num % 5 == 0 and num % 3 == 0:
print('%d baz' % (num))
else:
print('%d' % (num))
Here's a more mathematical usage of the for
statement. Suppose we want to compute the decimal expansion of $\sqrt{2}$ accurate to 3 decimal places.After searching Wikipedia, I came up with this recursive formula $$\begin{align*}a_0 &=1 \\ a_{n+1} &= \frac{a_n}{2}+\frac{1}{a_n}\end{align*}$$ Here's how we could implement this.
In [ ]:
max_iter = 10
a = 1
# Since _ is considered a valid variable name, we can use this to
# "suppress" counting indices.
for _ in range(0, max_iter):
a_next = a/2.0 + 1/a
if abs(a_next-a) < 1e-4: # You can use engineering format numbers in Python
print("Required accuracy found! Breaking out of the loop.")
break
a = a_next
print("Approximation of sqrt(2) is: %.3f" % (a))
In [ ]:
# Answer
Expected output:
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
In [ ]: