PythonBook
PythonBook
Using Python
Adam Cunningham
University at Buffalo
Department of Biostatistics
This work is licensed under the Creative Commons Attribution 4.0 International License.
To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send
a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
Contents
I Getting Started 7
1 Running Python 8
1.1 Install Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 Jupyter Notebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.1 The ”Files” Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 The ”Running” Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.3 The ”Clusters” Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.4 The ”Conda” Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.5 Starting a New Notebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.6 Magics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.7 Markdown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.8 LAT
EX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
II Programming Python 15
3 Containers 26
3.1 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.3 Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.4 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3
4.4 For Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.5 While Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.6 Break and Continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.7 Error Handling with Try-Except . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.8 Reading and Writing Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6 NumPy 53
6.1 Array Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.2 Array Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6.3 Array Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.4 Array Indexing and Slicing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
6.5 Indexing with Integer Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.6 Indexing with Boolean Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7 Matplotlib 65
7.1 Basic Plotting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7.2 A More Complex Plotting Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.3 Bar Plots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
7.4 Polar Plots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.5 Histograms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.6 Pie Charts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7.7 Contour Plots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.8 Slope Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.9 Stream Plots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.10 Multiple Plots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
7.11 Formatting Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.12 Formatting Mathematical Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8 Differential Equations 78
8.1 First-Order Differential Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.2 Higher Order Linear Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
8.3 Systems of Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9 Additional Topics 82
9.1 Loading Numerical Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.3 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.4 Random Number Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.5 Sound Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.6 Linear Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
IV Programming Tips 90
10 Programming Style 91
10.1 Choosing Good Variable Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.2 Choosing Good Function Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.3 No “Magic Numbers” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.4 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.5 Errors and Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
11 Further Reading 94
Index 95
5
Preface
This book covers material used in the courses ”MTH 306: Differential Equations” and
”MTH 337: Introduction to Scientific and Mathematical Computing” taught at the Uni-
versity at Buffalo. The following areas are covered:
• Programming using Python, the scientific computing package NumPy, and the plot-
ting library Matplotlib.
• Python packages available for solving differential equations, random number gen-
eration, linear programming, animation, and loading numerical, image, and sound
data.
• Programming tips relating to good programming style.
6
I Getting Started
7
1 Running Python
The examples in this book use Python 3.5. It is recommended that you use the Anaconda
distribution, which is available free on Windows, Mac and Linux and contains all the
packages needed (NumPy, SciPy, Matplotlib, Jupyter Notebook).
8
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
The development enviroment used in this book is the Jupyter Notebook. This provides:
On starting Jupyter, a window with several tabs is displayed. The first of these is the
”Files” tab. This provides a view of all the files in the folder where Jupyter Notebook was
started, similar to a folder view in ”Windows Explorer”.
Files can be selected in this window then opened, duplicated, renamed or deleted. The
button can be used to create a new text file, folder, terminal, or Jupyter Notebook.
The ”Running” tab shows the currently active terminals or notebooks in Jupyter, and can
be used to close them down.
The third tab is the ”Clusters” tab, which we will not consider in this class.
The ”Conda” tab is used to manage the add-on packages that are available for Python.
These provide additional functionality not present in the core system.
9
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
Installing a new package is done by selecting it from the list of available packages in the
left-hand window, then clicking the button to install it. The package will be displayed
in the right-hand window once it has been downloaded and installed.
A new Jupyter Notebook can be created from the ”Files” tab using the button.
The notebook opens in a web browser, and contains:
Title bar
Menu bar
Tool bar
Cell
10
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
Menubar functions
Menu Item Functions Available
File Open, close, copy, save, or rename the current notebook.
Export the notebook to a different format (HTML, PDF etc).
Edit Cut, copy, paste or delete cells.
Split and merge cells.
Move cells up or down.
Find and replace text in the notebook.
View Turn the header, toolbar, or cell toolbars on or off.
Insert Insert new cells into the notebook.
Cell Run cells (i.e. execute the code they contain)
Set the cell type (Code/Markdown)
Turn cell output on or off.
Kernel Interrupt or restart the Python ”kernel”. Interrupting the kernel is
used if code is taking too long to execute. Restarting the kernel
starts Python again in a clean state, without any additional variables
or functions loaded.
Widgets Embed widgets (controls for interactive data)
Help Help with the Notebook, Python, and the most important add-on
packages (NumPy, SciPy, Matplotlib).
Toolbar functions
Button Function
Save current notebook
Cut/copy/paste a cell
11
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
Code and text are entered in cells, which can be of different types. The main types we
will use are:
The type can be selected by either using the ”Cell Type” pull-down menu in the
toolbar, or Cell → Cell Type in the menubar.
1.2.6 Magics
Magics are instructions that perform specialized tasks. They are entered and executed in
code cells, and prefaced by ”%” for a line magic (which just applies to one line) or ”%%”
for a cell magic (which applies to the whole cell). The main ones we will be using are:
• %matplotlib inline sets up the notebook so that plots are drawn inline (in the
notebook itself).
• %timeit hcodei records the time it takes to run a line of Python code.
• %%timeit records the time it takes to run all the Python code in a cell.
%%timeit x = range(10000)
max(x) The line ”x = range(10000)”
is run once but not timed. The
1000 loops, best of 3: 884 µs per ”max(x)” line is timed.
loop
Note that the %%timeit magic must be the first line in the code cell.
12
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
1.2.7 Markdown
Text can be added to Jupyter Notebooks using Markdown cells. Markdown is a language
that can be used to specify formatted text such as italic and bold text, lists, hyperlinks,
tables and images. Some examples are shown below.
# An h1 header An h1 header
## An h2 header An h2 header
### An h3 header An h3 header
*italic* italic
**bold** bold

A horizontal line
A horizontal line
***
13
CHAPTER 1. RUNNING PYTHON 1.2. JUPYTER NOTEBOOK
1.2.8 LATEX
Mathematical expressions in Markdown cells are specified using the typesetting language
LATEX. These expressions are identified using $hformulai$ for an inline formula (displayed
within a line of text), or $$hformulai$$ for a larger formula displayed on a separate line.
Superscripts
$x^2$ x2
Subscripts
$x_1$ x1
Fractions
1
$\frac{1}{2}$ 2
Greek Letters
$\alpha, \beta, \gamma, \ldots, \omega$ α, β, γ, . . . , ω
Series
Pn
$\sum_{i = 1}^n$ i=1
Integrals
Rb
$\int_a^b$ a
Square Roots
√
$\sqrt{a + b}$ a+b
Overline
$\bar{x}$ x̄
Brackets
$\{1, 2, \ldots, n\}$ {1, 2, . . . , n}
Matrices
" #
1 2
$\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}$
3 4
14
II Programming Python
15
2 Python Data Types
Python is a flexible and powerful high-level language that is well suited to scientific and
mathematical computing. It has been designed with a clear and expressive syntax with a
focus on ensuring that code is readable.
2.1 Numbers
• Integers.
• Floats (reals).
• Complex numbers (pairs of floats).
Python will automatically convert numbers from one type to another when appropriate.
For example, adding two integers yields an integer, but adding an integer and a float
yields a float. The main arithmetic operations are +, -, *, /, and **. Operations
are evaluated in standard order - Parentheses, Exponentiation, Multiplication, Division,
Addition, Subtraction. To avoid possible ambiguity, use parentheses to make the order of
evaluation clear.
3 + 2
Addition
5
3 - 2
Subtraction
1
3 * 2
Multiplication
6
3 / 2
Division
1.5
16
CHAPTER 2. PYTHON DATA TYPES 2.1. NUMBERS
3**2
Exponentiation ( not 3^2)
9
Some other useful operations are floor division (//), modulus (%), and absolute value
(abs).
12 % 5
Modulus returns the remain-
2 der.
abs(-88)
abs returns the absolute value.
88
Python has a built-in complex number type, and knows the rules of complex arithmetic.
1 + 2j
Generate a complex number (j
(1+2j) is used instead of i).
complex(1, 2)
Another way to generate a
(1+2j) complex number.
z = 1 + 2j
w = 3 - 1j Note that a ’1’ is needed in
front of the j.
17
CHAPTER 2. PYTHON DATA TYPES 2.2. BOOLEANS
z + w
Complex addition.
(4+1j)
z * w
Complex multiplication.
(5+5j)
2.2 Booleans
Python also has a Boolean type, which only takes the values True or False. These can
also be used like numbers, where True has the value 1 and False the value 0.
True or False
Logical disjunction
True
not True
Logical negation
False
True + 41
True has the numerical value
42 1.
False * 41
False has the numerical
0 value 0.
2.3 Strings
Strings are sequences of characters. They are identified by surrounding quote marks.
18
CHAPTER 2. PYTHON DATA TYPES 2.3. STRINGS
A ”\” within a string is used to specify special characters such as newlines and tabs.
string1 = "abc\ndef"
print(string1)
The ”\n” character specifies a
abc newline.
def
string2 = "abc\tdef"
print(string2) The ”\t” character specifies a
tab.
abc def
• Indexing obtains characters from the string using a single integer to identify the
position of the character.
• Slicing obtains a substring using start:stop:step to specify characters to select.
• Indexing and slicing are zero-based - the first character is at position 0.
• Indexing and slicing is ”up to but not including” the stop position.
• A ”:” can be used to select all characters either before or after a given position.
a b c d e
0 1 2 3 4
19
CHAPTER 2. PYTHON DATA TYPES 2.3. STRINGS
String methods are commands specific to strings that carry out some action on the string.
They are called by following the string with a ’.’ followed by the method name.
"mth337".capitalize()
capitalize capitalizes the first
"Mth337" letter.
20
CHAPTER 2. PYTHON DATA TYPES 2.4. FORMATTING STRINGS
String methods can also be used for counting the number of times a given substring occurs
and for finding where it occurs.
Strings can be formatted using the format function. This allows ”replacement fields”
surrounded by curly brackets {} in a string to be replaced by some other data. ”Format
specifications” within replacement fields define how data is to be formatted, including the
field width, padding and number of decimal places.
• Empty replacement fields {} are filled in order with the arguments given.
• Numbers inside replacement fields specify arguments by position, starting with zero.
Format specifications for the field width and padding are provided after a colon ’:’.
• A field width can be specified using :n where n is an integer specifying the number
of characters in the field.
• If the data has less characters than the field width, the default is to insert extra
spaces on the right i.e. to ”pad” on the right.
21
CHAPTER 2. PYTHON DATA TYPES 2.5. TYPE CONVERSIONS
• To pad on the left, use :>n. To explicitly pad on the right, use :<n. The > and <
are like direction arrows ”sending” the data to the indicated side of the field.
• Data can be centered in a replacement field using :ˆn.
Format specifications for floats allow tables of numerical data to be neatly printed and
aligned.
Objects can be explicitly converted from one type to another, as long as the conversion
makes sense. This is called type casting .
• Ints can be cast to floats, and both ints and floats can be cast to complex numbers.
• Complex numbers can’t be converted to ints or floats.
22
CHAPTER 2. PYTHON DATA TYPES 2.5. TYPE CONVERSIONS
Casting is done using the functions bool, int, float, complex, and str.
bool(1)
Convert integer to boolean.
True
bool(0)
Zero equates to False.
False
bool("")
An empty string is also False.
False
int("22")
Convert string to int.
22
float("4.567")
Convert string to float.
4.567
complex("1+2j")
Convert string to complex.
(1+2j)
float(10)
Convert integer to float.
10.0
complex(10)
Convert integer to complex
(10+0j) number.
str(True)
Convert boolean to string.
"True"
23
CHAPTER 2. PYTHON DATA TYPES 2.6. VARIABLE NAMES
str(1)
Convert integer 1 to string
"1" ”1”.
str(1.234)
Convert float to string.
"1.234"
y = x + 3
print(y) Assign y the value of x + 3.
8
a, b = 2, 3
print(a, b) Multiple variables can be as-
signed at the same time.
2 3
24
CHAPTER 2. PYTHON DATA TYPES 2.6. VARIABLE NAMES
z = 3
z += 2
print(z) Same as z = z + 2.
z -= 1
print(z)
Same as z = z - 1.
4
z *= 3
print(z)
Same as z = z * 3.
12
z /= 2
print(z) Same as z = z / 2.
6
z %= 5
print(z) Same as z = z % 5.
1
25
3 Containers
We often need a way to group objects of a similar kind together. Sometimes the order of
the objects is important, as in a mathematical sequence {a0 , a1 , a2 , . . .}, and sometimes
the order is irrelevant, as in the set {’cat’, ’dog’, ’hippogriff’}.
Python provides several types of container that can be used to group objects together.
Containers differ in the following ways:
• Mutable versus immutable. Mutable containers can be modified after they have been
created; immutable containers cannot.
• Ordered versus unordered. The items in an ordered container are stored in a fixed
sequence; those in an unordered container are not.
• Indexable versus non-indexable. The items in an indexed container can be retrieved
using a key; those in a non-indexable container cannot.
3.1 Lists
26
CHAPTER 3. CONTAINERS 3.1. LISTS
list1 = [1, 2, 3]
list2 = [4, 5, 6] Adding two lists makes a new
list1 + list2 list by concatenation.
[1, 2, 3, 4, 5, 6]
list1 * 3
Multiplying a list by an integer
[1, 2, 3, 1, 2, 3, 1, 2, 3] repeats the list.
list3 = []
print(list3) list3 is an empty list.
[]
list4 = list()
print(list4) Another way to create an
empty list.
[]
Lists can be indexed and sliced in the same way as strings, using square brackets. Indexing
and slicing can also be used with the ”=” assignment operator to change the elements of
a list.
primes[::-1]
One way to reverse a list.
[17, 13, 11, 7, 5, 3, 2]
27
CHAPTER 3. CONTAINERS 3.1. LISTS
List methods exist for changing, adding and deleting elements. These all modify an
existing list.
mylist.insert(0, "z")
print(mylist) insert inserts an element at a
given position.
["z", "a", "b", "c", "d", "e", "f"]
mylist.remove("z")
print(mylist) remove removes the first in-
stance of an item from a list.
["a", "b", "c", "d", "e"]
Methods also exist for counting items in a list and where they occur.
28
CHAPTER 3. CONTAINERS 3.2. TUPLES
numbers.sort()
print(numbers) sort sorts a list in place, mod-
ifying the existing list.
[2, 3, 5, 10, 26]
The min and max functions find the smallest and largest items in a list.
3.2 Tuples
Tuples are containers like lists, with the difference being that they are immutable - once
defined, elements cannot be changed or added. Tuples are identified by surrounding
standard parentheses, ().
Tuples are simpler and more efficient than lists in terms of memory use and performance.
They are often preferred for ”temporary” variables that will not need to be modified. They
can also be used as dictionary keys, which lists cannot.
29
CHAPTER 3. CONTAINERS 3.3. SETS
Any comma-separated sequence of values defines a tuple, which can be used to assign
values to multiple variables at a time.
tuple2 = 1, 2, 3
print(tuple2) A comma-separated sequence
of values defines a tuple.
(1, 2, 3)
3.3 Sets
Sets are containers with the same meaning they do in mathematics - unordered collections
of items with no duplicates. Sets are identified by surrounding curly brackets, {}.
30
CHAPTER 3. CONTAINERS 3.3. SETS
myset = {1, 2, 3}
print(myset) Sets are created using curly
brackets.
set([1, 2, 3])
print(set())
set([]) creates an empty set.
set([])
The standard mathematical operations for sets are all built into Python.
set1 = {1, 2, 3}
set2 = {3, 4, 5} Create 2 sets.
1 in set1
in tests for set membership.
True
set1 | set2
Set union (the union operator
{1, 2, 3, 4, 5} can also be used).
set1 - set2
Set difference (can also use the
{1, 2} difference operator).
31
CHAPTER 3. CONTAINERS 3.4. DICTIONARIES
3.4 Dictionaries
Dictionaries are containers where items are accessed by a key. This makes them different
from sequence type objects such as strings, lists, and tuples, where items are accessed by
position.
dict1["y"] = 10
Dictionary values can be
print(mydict)
changed using the “=”
{"x": 1, "y": 10, "z": 3} assignment operator.
dict1["w"] = 0
New key:value pairs can be
print(mydict)
assigned using the “=” assign-
{"x": 1, "y": 10, "z": 3, "w": 0} ment operator.
dict1.get("a", 42)
get can also return a default
42 value.
dict2 = {}
print(dict2)
Creating an empty dictionary.
{}
32
CHAPTER 3. CONTAINERS 3.4. DICTIONARIES
dict3 = dict()
print(dict3) Another way to create an
empty dictionary.
{}
It is an error to attempt to access a dictionary using a key that does not exist.
This can be avoided by using the get method, which returns a default value if
the key is not found.
33
4 Controlling the Flow
Boolean expressions are statements that either evaluate to True or False. An important
use of these expressions is for tests in conditional code that only executes if some condition
is met. Examples of Boolean expressions include the standard comparison operators below.
5 == 5
Check for equality.
True
5 != 5
Check for inequality,
False
3 < 2
Less than.
False
3 <= 3
Less than or equals.
True
Note that any empty container evaluates to False in a Boolean expression. Examples
include empty strings (””), lists ([]), and dictionaries ({}).
4.2 If Statements
Python if statements provide a way to execute a block of code only if some condition is
True.
if statement
if <condition>:
<code to execute when condition True>
<following code>
34
CHAPTER 4. CONTROLLING THE FLOW 4.2. IF STATEMENTS
Note that:
An else statement can be added after an if statement is complete. This will be followed
by the code to execute if the condition is False.
if-else statement
if <condition>:
<code to execute when condition True>
else:
<code to execute when condition False>
<following code>
x, y = 2**3, 3**2
if x < y:
print("x < y") The block of code following
else: the else statement executes if
print("x >= y") the condition is not met.
x < y
Multiple elif statements (short for else-if) can be added to create a series of conditions
that are tested in turn until one succeeds. Each elif must also be followed by a condition
and a colon.
35
CHAPTER 4. CONTROLLING THE FLOW 4.3. CONDITIONAL EXPRESSIONS
elif statement
if <condition 1>:
<code to execute when condition 1 True>
elif <condition 2>:
<code to execute when condition 2 True>
else:
<code to execute if neither condition is True>
<following code>
score = 88
if score >= 90:
print("A")
elif score >= 80:
print("B")
Only the first two conditions
elif score >= 70:
are tested - the rest are
print("C")
skipped since the second con-
elif score >= 60:
dition is True.
print("D")
else:
print("F")
It often happens that we want to assign a variable name some value if a condition is True,
and another value if a condition is False. Using an if statement, we would have:
if <condition>:
x = <true_value>
else:
x = <false_value>
Python provides an elegant way to do the same thing in a single line using a conditional
expression.
Conditional expression
x = <true_value> if <condition> else <false_value>
36
CHAPTER 4. CONTROLLING THE FLOW 4.4. FOR LOOPS
x = 22
Note that x % 2 returns the
parity = "odd" if x % 2 else "even"
remainder when x is divided by
print(x, "has", parity, "parity")
2. Any nonzero value evalu-
22 has even parity ates as True.
Python for loops provide a way to iterate (loop) over the items in a list, string, tuple, or
any other iterable object, executing a block of code on each pass through the loop.
for loop
for <iteration variable(s)> in <iterable>:
<code to execute each time>
<following code>
Note that:
Sequence objects, such as strings, lists and tuples, can be iterated over as follows.
37
CHAPTER 4. CONTROLLING THE FLOW 4.4. FOR LOOPS
The range function generates integers in a given range. It is often used inside a for loop
to iterate over some sequence of integers.
• The start, stop and step parameters to range are similar to those used to slice
lists and strings.
• Integers are only generated by range as needed, rather than as a list.
for i in range(3):
print(i) range(n) generates n consec-
utive integers, starting at 0
0
and ending at n - 1.
1
2
total = 0
for i in range(1, 6):
total += i
Sum the numbers from 1 to 5.
print(total)
15
38
CHAPTER 4. CONTROLLING THE FLOW 4.4. FOR LOOPS
Dictionary elements consist of key:value pairs. When iterated over, variables can be bound
to the key, the value, or both.
We can also iterate in parallel over multiple lists of equal length by using zip. This
generates a sequence of tuples, with one element of each tuple drawn from each list.
39
CHAPTER 4. CONTROLLING THE FLOW 4.5. WHILE LOOPS
Python while loops execute a block of code repeatedly as long as some condition is met.
while loop
while <condition>:
<code to execute repeatedly>
<following code>
Note that for the loop to terminate, the code must change some part of the hconditioni
so that it eventually returns False.
i = 3
while i > 0: The variable i is printed while
print(i) it remains greater than zero.
i -= 1 The code inside the loop must
change the value of i to ensure
3 that the loop eventually termi-
2 nates.
1
Sometimes we need to end a loop early, either by ending just the current iteration, or by
quitting the whole loop. The statements break and continue provide a way to do this.
• To end the loop completely and jump to the following code, use the break statement.
• To end the current iteration and skip to the next item in the loop, use the continue
statement. This can often help to avoid nested if-else statements.
vowels = "aeiou"
for char in "jabbawocky":
if char in vowels: The for loop is terminated by
print("First vowel is", char) break once the first vowel is
break found.
First vowel is a
40
CHAPTER 4. CONTROLLING THE FLOW 4.7. ERROR HANDLING WITH TRY-EXCEPT
total = 0
for char in "jabbawocky":
if char in vowels:
Skip over the vowels using
continue
continue, and just count the
total += 1
consonants.
print(total, "consonants found")
7 consonants found
If an invalid operation is attempted when running code then an error is usually gener-
ated. Examples include dividing by zero, indexing past the end of a sequence, creating a
floating point number that is too large, or adding two arrays of different sizes. In these
circumstances, the code typically breaks at the point of the invalid operation.
A try-except statement can be used to handle errors more deliberately than having the
code break. This allows us to first try to execute some code that we’re not sure will work,
and then execute some other code if it doesn’t.
try-except statement
try:
<code to attempt to execute>
except:
<code to execute only when an error occurs>
Errors typically have a type associated with them, which specifies the kind of error that has
occurred. Examples include ZeroDivisionError, IndexError, OverflowError, and ValueError.
The ”except” part of a try-except statement can be specialized to handle these different
kinds of error.
try-except statement with named error types
try:
<code to attempt to execute>
except ErrorType:
<code to only execute when the named error occurs>
The following example shows how to gracefully handle an error resulting from division by
zero.
41
CHAPTER 4. CONTROLLING THE FLOW 4.8. READING AND WRITING FILES
for i in range(-2,3):
The number 10 is being di-
print(10/i)
vided by a sequence of inte-
-5.0 gers, one of which happens to
-10.0 be zero. Without error han-
Traceback (most recent call last): dling, the code breaks when
File "hstdini", line 2, in hmodulei the zero is encountered and a
ZeroDivisionError: division by zero ”ZeroDivisionError” is raised.
for i in range(-2,3):
try:
print(10/i)
except ZeroDivisionError: Using a try-except statement
print("Can't divide by zero!") to handle the ZeroDivision-
Error allows the loop to run to
-5.0
completion without breaking.
-10.0
Can’t divide by zero!
10.0
5.0
Several reports for this class will involve reading and analyzing data that has been stored
in a file. This typically involves three steps:
• Open the file using the open function. This returns a file handle - an object we then
use to access the text that the file contains.
• Process the file, either line-by-line, or as a single text string.
• Close the file. This is done using the close function.
It is possible to read in the entire contents of a file in one go using the functions read
and readlines. However, we may not need to read the entire contents into memory if we
are dealing with a large file and just want to extract some information from the text. In
this case, it is preferable to iterate over the lines of text that the file contains.
The following examples assume that a file named ”firstnames.txt” has been created in
the directory that Python was started in. This file contains the three lines:
”firstnames.txt”
Leonard
Penny
Sheldon
42
CHAPTER 4. CONTROLLING THE FLOW 4.8. READING AND WRITING FILES
input_file.close()
Close ”firstnames.txt” using
close.
input_file = open("firstnames.txt")
first_names = input_file.read() read reads in the whole file
input_file.close() as a single string. The new-
first_names lines at the end of each line
are shown as ”\n” characters.
"Leonard\nPenny\nSheldon\n"
print(first_names)
Printing a string causes the
Leonard newline characters ”\n” to be
Penny rendered as new lines.
Sheldon
input_file = open("firstnames.txt")
data = input_file.readlines()
readlines reads in the whole
input_file.close()
file as a list, with each line as
data
a separate string.
["Leonard\n", "Penny\n", "Sheldon\n"]
Files can also be opened for writing using the ”w” option to open.
43
CHAPTER 4. CONTROLLING THE FLOW 4.8. READING AND WRITING FILES
input_file = open("names.txt")
last_names = input_file.read()
input_file.close()
print(last_names) Check that the ”names.txt”
file has been written correctly.
Hofstadter
?
Cooper
44
5 Packaging and Reusing Code
5.1 Functions
Functions provide a way to reuse a block of code by giving it a name. The code inside
the function can then be executed by calling the function name. Using functions makes
code more understandable and easier to maintain.
• Complex problems are better understood when broken down into simpler steps. By
putting the code for each step into a well-named function, the overall structure of
the solution is easier to see and understand.
• Duplicating code in two or more places makes code longer and more difficult to
maintain. This is because changes to the code need to be made each place the code
is duplicated, leading to problems if the code is not kept in sync. Putting duplicated
code into a function which is called wherever the code is needed avoids this problem.
• Variables defined and used inside a function only exist inside the function. They
are said to be local variables, as opposed to global variables that can be accessed
everywhere. Local variables allow the same variable name to be used safely in different
parts of a program. This is because modifying the variable inside the function only
changes its value inside the function, without affecting the rest of the program.
Functions can be defined with the option of passing in additional data to be used inside the
function. The variables used to identify this additional data are the function parameters,
and the particular values passed in when the function is called are the function arguments.
The def statement is used to define functions in Python, with the syntax:
def statement
def <name> (<parameters>):
"""documentation string"""
<code>
45
CHAPTER 5. PACKAGING AND REUSING CODE 5.1. FUNCTIONS
• An optional documentation string can be added at the start of the function (before
the code) to describe what the function does. This string is usually enclosed in triple
quotes.
def minmax(data):
return min(data), max(data)
Multiple values are returned as
print(minmax([1, 3, 7, 2, 10])) a tuple.
(1, 10)
Using keyword arguments allows default values to be assigned. This is particularly useful
when a function can be called with many different options, and avoids having to call
functions with a long list of arguments.
46
CHAPTER 5. PACKAGING AND REUSING CODE 5.1. FUNCTIONS
close_enough(1, 1.05)
The default tolerance of 0.1 is
True used in this case.
If the number of arguments is not known in advance, functions can be defined to take a
variable number of positional arguments and/or a variable number of keyword arguments.
We are unlikely to be using these options ourselves, although they occur frequently in the
documentation for Matplotlib.
• The positional arguments are specified as *args and are available as a tuple. In-
dividual positional arguments can then be accessed by indexing into the tuple by
position.
• The keyword arguments are specified as **kwargs and are available as a dictionary.
Individual keyword arguments can then be accessed by indexing into this dictionary
by key.
We may on occasion need a simple function that will only be used in a single place, and
not want to have to define and name a separate function for this purpose. In this case
we can define an anonymous or lambda function just in the place where it is needed. The
syntax for a lambda function is:
lambda statement
lambda <arguments> : <code>
The lambda statement returns an unnamed function which takes the arguments given
before the colon, and returns the result of executing the code after the colon. Typical
uses for lambda functions are where one function needs to be passed in as an argument
to a different function.
47
CHAPTER 5. PACKAGING AND REUSING CODE 5.2. MODULES
5.2 Modules
import numpy
print(numpy.arcsin(1)) Everything in numpy can be
used, prefaced by ”numpy”.
1.57079632679
If we want to know what a module contains, we can use the dir function. This returns a
list of all the variable and function names in the module.
48
CHAPTER 5. PACKAGING AND REUSING CODE 5.3. COMPREHENSIONS
import math
print(dir(math))
5.3 Comprehensions
Often we want to create a container by modifying and filtering the elements of some other
sequence. Comprehensions provide an elegant way to do this, similar to mathematical set-
builder notation. For list comprehensions, the syntax is:
List comprehension
[<expression> for <variables> in <iterable> if <condition>]
The code in hexpressioni is evaluated for each item in the hcontaineri, and the result
becomes an element of the new list. The hconditioni does not have to be present but, if
it is, only elements which satisfy the condition become incorporated into the new list.
Dictionary comprehension
{<key>:<value> for <variables> in <iterable> if <condition>}
49
CHAPTER 5. PACKAGING AND REUSING CODE 5.4. GENERATOR EXPRESSIONS
We often want to iterate over the elements of some sequence, but don’t need to save the
sequence. In these cases, a list comprehension is an unnecessary overhead since the list
is created and saved in memory for a single use. A more efficient alternative is to use a
”generator expression”.
Generator expressions have almost the same syntax as a list comprehension, but use
parentheses instead of square brackets.
Generator expression
(<expression> for <variables> in <iterable> if <condition>)
5.5 Comments
Comments are text that is included in the code but not executed. They are used to
document and explain what the code is doing. Python allows two forms of comment.
• A hash symbol # means that the rest of the line is a comment, and is not to be
executed.
50
CHAPTER 5. PACKAGING AND REUSING CODE 5.5. COMMENTS
51
III Numerical Computing
52
6 NumPy
NumPy (Numerical Python) is the fundamental package for scientific computing with
Python. It defines a new kind of container - the ndarray (usually just referred to as
an array) - that supports fast and efficient computation. NumPy also defines the basic
routines for accessing and manipulating these arrays.
Arrays have the following properties (among others):
[[ 0 1 2 3 4 ]
[ 5 6 7 8 9 ]
[ 10 11 12 13 14 ]
[ 15 16 17 18 19 ]]
The main differences between NumPy arrays and Python lists are:
• The objects in a NumPy array must all be of the same type - booleans, integers,
floats, complex numbers or strings.
• The size of an array is fixed at creation, and can’t be changed later.
• Arrays can be multi-dimensional.
• Mathematical operations can be applied directly to arrays. When this is done they
are applied elementwise to the array, generating another array as output. This is
much faster than iterating over a list.
• Indexing for arrays is more powerful than that for lists, and includes indexing using
integer and boolean arrays.
• Slicing an array produces a view of the original array, not a copy. Modifying this
view will change the original array.
NumPy is well documented online, with a standard tutorial and good introductory tutorial
available.
53
CHAPTER 6. NUMPY 6.1. ARRAY CREATION
• From a list. The elements of the list need to all be of the same type, or of a kind
that can all be cast to the same type. For example, a list consisting of both integers
and floats will generate an array of floats, since the integers can all be converted to
floats.
• According to a given shape. The array will be initialized differently depending on the
function used.
• From another array. The new array will be of the same shape as the existing array,
and could either be a copy, or initialized with some other values.
• As a result of an operation on other arrays. The standard mathematical operators
can all be applied directly to arrays. The result is an array of the same shape where
the operation has been performed separately on corresponding elements.
The following functions are the main ones we use in this class.
array Create an array from a list.
linspace Return an array of evenly spaced numbers over a specified interval.
arange Return an array of evenly spaced integers within a given interval.
empty Return an a new array of a given shape and type, without initializing entries.
zeros Return an a new array of a given shape and type, filled with zeros.
ones Return an a new array of a given shape and type, filled with ones.
empty like Return a new array with the same shape and type as a given array.
zeros like Return an array of zeros with the same shape and type as a given array.
ones like Return an array of ones with the same shape and type as a given array.
copy Return an array copy of the given object.
meshgrid Returns a pair of 2D x and y grid arrays from 1D x and y coordinate arrays.
These functions are illustrated below. The ”np” alias is standard practice when using
NumPy.
import numpy as np
x = np.array([1, 2, 3]) array(object) creates an array
print(x) from a list - note that arrays
are printed without commas.
[1 2 3]
54
CHAPTER 6. NUMPY 6.1. ARRAY CREATION
The functions empty, zeros and ones all take a shape argument and create an array of
that shape, initialized as appropriate.
x = np.empty((3, 2))
print(x) empty(shape) returns an ar-
ray of shape shape, initially
[[ 6.93946206e-310 6.93946206e-310]
filled with garbage.
[ 6.36598737e-314 6.36598737e-314]
[ 6.36598737e-314 0.00000000e+000]]
x = np.zeros((2, 3))
zeros(shape) returns an array
print(x)
of shape shape filled with ze-
[[ 0. 0. 0.] ros - note the default type is
[ 0. 0. 0.]] float.
Arrays can be created directly from other arrays using empty like, zeros like, ones like
and copy.
x = np.arange(3, dtype=float)
print(x) Create an array of floats using
arange.
[0. 1. 2.]
55
CHAPTER 6. NUMPY 6.1. ARRAY CREATION
y = np.empty_like(x)
print(y) y has the same shape as x, but
is initially filled with garbage.
[ 0.00000000e+000 6.51913678e+091
6.95022185e-310]
y = np.zeros_like(x)
print(y) y has the same shape as x, but
is initialized with zeros.
[ 0. 0. 0.]
y = np.ones_like(x)
print(y) y has the same shape as x, but
is initialized with ones.
[ 1. 1. 1.]
y = np.copy(x)
print(y) y is a copy of x - changing y
will not change x.
[0. 1. 2.]
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
X, Y = meshgrid(x, y)
0 1 2
0 0 0 0
1 1 1 1
2 2 2 2
x = np.arange(4)
y = np.arange(3) meshgrid creates 2D x- and
X, Y = np.meshgrid(x, y) y- coordinate arrays from 1D
x- and y- coordinate arrays.
56
CHAPTER 6. NUMPY 6.2. ARRAY PROPERTIES
print(X)
X is a 2D array containing just
[[0 1 2 3] the x-coordinates of points in
[0 1 2 3] the xy plane.
[0 1 2 3]]
print(Y)
Y is a 2D array containing just
[[0 0 0 0] the y-coordinates of points in
[1 1 1 1] the xy plane.
[2 2 2 2]]
The function meshgrid is often used when we want to apply a function of two variables
to points in the x-y plane. The following example uses the arrays X and Y above.
import numpy as np
x = np.arange(6)
type(x) x is of type numpy.ndarray.
numpy.ndarray
x.dtype
dtype returns the element
dtype("int64") type - a 64-bit integer.
x.shape
x is a 1-dimensional array with
(6,) 6 elements in the first axis.
It is possible to create an array from the elements of an existing array, but with the
properties changed.
57
CHAPTER 6. NUMPY 6.3. ARRAY OPERATIONS
• The number of dimensions and size of each dimension can be changed using reshape
(as long as the total number of elements is unchanged).
• The dtype can be changed using astype.
import numpy as np
x = np.arange(4) Create an array of consecutive
print(x) integers using arange.
[0 1 2 3]
y = array([3, 2, 5, 1])
Create a second array
58
CHAPTER 6. NUMPY 6.3. ARRAY OPERATIONS
print(x)
print(y)
The elements of x are added
print(x + y)
to the corresponding elements
[0 1 2 3] of y on an element-by-element
[3 2 5 1] basis.
[3 3 7 4]
Comparison operators and other Boolean expressions are also applied on an element-by-
element basis. The Boolean expression is applied to every element of the array, with each
result becoming an element in a new boolean array.
x = np.arange(5)
print(x) The Boolean expression is
print(x % 2 == 0) evaluated for each element
separately, resulting in an ar-
[0 1 2 3 4] ray of booleans.
[ True False True False True]
x = np.arange(4)
y = np.array([3, 2, 5, 1])
print(x) The comparison is done on an
print(y) elementwise basis between ele-
print(x < y) ments of arrays x amd y. The
result is an array of booleans.
[0 1 2 3]
[3 2 5 1]
[ True True True False]
NumPy contains vectorized versions of all the basic mathematical functions. Note that
they need to be imported before we can use them. Some examples are given below.
x = np.arange(3)
print(x) Create an array.
[0 1 2]
59
CHAPTER 6. NUMPY 6.4. ARRAY INDEXING AND SLICING
print(np.exp(x))
exp is the exponential opera-
[ 1. 2.71828183 7.3890561 ] tor.
Arrays can be indexed and sliced using square brackets in the same way as lists.
60
CHAPTER 6. NUMPY 6.5. INDEXING WITH INTEGER ARRAYS
import numpy as np
x = np.arange(20).reshape((4, 5))
print(x) reshape provides a fast way to
create a 2D array from a 1D
[[ 0 1 2 3 4]
array.
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
Although slicing can be used to return a subset of elements from an array, its use is
restricted to either selecting consecutive elements, or to selecting elements that are sep-
arated by the same fixed amount. We sometimes want to select instead some arbitrary
subset of an array, possibly even selecting the same element more than once. Integer array
indexing provides a way to do this.
61
CHAPTER 6. NUMPY 6.5. INDEXING WITH INTEGER ARRAYS
x 0 1 4 9 16 25 36 49 64
index 2 5
x[index] 4 25
import numpy as np
x = np.arange(9)**2 First create the array using
print(x) arange, then square each el-
ement.
[ 0 1 4 9 16 25 36 49 64]
The index can be an integer array of any shape, and can include duplicates as shown
below.
The target array can also be a multidimensional array. In this case, the elements selected
by the index array will be the array elements of the target.
62
CHAPTER 6. NUMPY 6.6. INDEXING WITH BOOLEAN ARRAYS
We can also index using arrays of booleans. In this case, the indexing array acts like
a mask, letting through only the elements in the target array that correspond to True
values in the indexing array.
x 0 1 2 3
x[mask] 0 1 3
import numpy as np
x = np.arange(4)
mask = np.array([True, True, False, True])
Only the elements with a
print(x)
matching True in the mask
print(x[mask])
are selected.
[0 1 2 3]
[0 1 3]
A common use of this technique is to select the elements of an array that satisfy some
condition. This can be done by first applying the condition to the array to generate an
array of booleans, then using the resulting array as an index. The result is that only the
elements for which the condition holds true are selected.
63
CHAPTER 6. NUMPY 6.6. INDEXING WITH BOOLEAN ARRAYS
x 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
x % 3 == 0 T F F T F F T F F T F F T F F T F F T F
x[x % 3 == 0] 0 3 6 9 12 15 18
The logical operators not, or and and do not get applied elementwise when
applied to NumPy arrays. The functions logical not, logical or and logi-
cal and need to be used instead.
64
7 Matplotlib
Matplotlib is a 2D plotting library for Python. It can be used to generate graphs, his-
tograms, bar charts, contour plots, polar plots, scatter plots, and many other kinds of
mathematical graphics. The ”pyplot” interface provides a MATLAB-like interface for sim-
ple plotting, and is the main one we will be using in class. The online reference provides
a full description of the available functions. A good tutorial is also available online.
The following commands are the main ones used for creating and formatting graphs.
plot Plot lines and/or markers.
scatter Scatter plot of x vs y.
show Display a figure.
title Set a title for the graph.
xlabel/ylabel Set labels for the x and y axes.
xlim/ylim Get or set the range of x and y values to be displayed.
xticks/yticks Get or set the locations and labels for the tick marks on the x and y axes.
subplot Plot multiple graphs in one figure.
figure Create a new figure.
fill between Fill the area between two curves.
legend Put a legend on the graph.
Colors, line styles, and marker styles can all be set to create customized graphs. These
are usually specified as strings, with the most frequently used options as follows.
65
CHAPTER 7. MATPLOTLIB 7.1. BASIC PLOTTING
Functions can be graphed using a call to plot(x, y ), followed by a call to show. Note
that:
• The x parameter contains the x-coordinates of the points to plot, and the y parameter
contains the y-coordinates.
• We need to import the required NumPy and Matplotlib functions for array manipu-
lation and plotting.
• If using Jupyter Notebook, calling the IPython magic %matplotlib inline calls show
automatically when the cell is executed.
• The default plotting behavior is to connect the points with a blue line.
The following example plots the exponential function in the range [0, 5].
160
140
120
100
80
60
40
20
0
0 1 2 3 4 5
66
CHAPTER 7. MATPLOTLIB 7.2. A MORE COMPLEX PLOTTING EXAMPLE
A range of options are available for customizing plots. These are illustrated in the example
below, which plots a sine and cosine curve on the same graph. Note that:
• The third argument to plot can be used to set colors, line types and marker types.
• Multiple plots can be drawn on one figure, followed by a single call to show.
title
1.0 Sine and Cosine
sine
cosine
0.5
legend
0.0
default style
xlim
1.0
0 π/2 π 3π/2 2π
xticks theta xlabel
67
CHAPTER 7. MATPLOTLIB 7.3. BAR PLOTS
Bar Plot
import matplotlib.pyplot as plt # Import plotting functions
grades = ['A', 'B', 'C', 'D', 'F'] # Used to label the bars
freqs = [30, 35, 20, 10, 5] # Bar heights are frequencies
width = 0.8 # Relative width of each bar
ticks = [width/2 + i for i in range(5)] # Ticks in center of the bars
plt.bar(range(5), freqs, fc='c', ec='b') # fc/ec are face/edge colors
plt.xticks(ticks, grades) # Place labels for the bars
plt.ylim(0, 40) # Set the space at the top
plt.title('Grade distribution') # Add a title
plt.xlabel('Grade') # Add a label for the x-axis
plt.ylabel('Frequency (%)') # Add a label for the y-axis
plt.show() # Finally, show the figure
title
40 Grade distribution
35
30
25
Frequency (%)
fc = 'c'
20 ylim
width = .8
15 ec = 'b'
ylabel
10
0
A B C D F
xticks Grade xlabel
68
CHAPTER 7. MATPLOTLIB 7.4. POLAR PLOTS
The function polar is used to create polar plots. These plot radius against angle in polar
coordinates.
• The first argument to polar is an array of angles, and the second argument the
corresponding radii.
• Colors, line types and marker types are specified in the same way as plot.
• Polar can be called multiple times, followed by a single call to show.
The following example shows a polar plot with the marker style to ”d” (diamond) and the
color set to ”m” (magenta).
Polar
90°plot
135° 45°
3.0
2.5
2.0
1.5
1.0
0.5
180° 0°
225° 315°
270°
69
CHAPTER 7. MATPLOTLIB 7.5. HISTOGRAMS
7.5 Histograms
The function hist is used to plot histograms. These group numerical data into ”bins”,
usually of equal width, in order to show how the data is distributed.
• Each bin covers a range of values, with the height of each bin indicating the number
of points falling in that range.
• The first argument is an array or sequence of arrays.
• The bins argument specifies the number of bins to use.
• The range argument specifies the range of values to include.
The following example plots a histogram of 1000 samples drawn from a uniform probability
distribution over [0, 1).
Plotting a Histogram
import numpy as np # Make random.rand available
import matplotlib.pyplot as plt # Import plotting functions
title
70 Uniform distribution
bins = 20 fc = 'g'
60
50
40
Frequency
30
ylabel 20
10
range = (0, 1)
0
0.0 0.2 0.4 0.6 0.8 1.0
Value xlabel
70
CHAPTER 7. MATPLOTLIB 7.6. PIE CHARTS
The function pie is used to create pie charts. These are a type of graph in which a circle
is divided into wedges that each represent a proportion of the whole.
• The first argument to pie is a sequence of values used for the wedge sizes.
• The labels argument is a sequence of strings providing the labels for each wedge.
• The shadow argument is a boolean specifying whether to draw a shadow beneath
the pie.
The following example shows a pie chart with shadow set to True .
title
MTH 337 Grade Distribution
A
labels
shadow=True D
C
B
71
CHAPTER 7. MATPLOTLIB 7.7. CONTOUR PLOTS
The functions contour and contourf are used for contour plots and filled contour plots
respectively. These are projections of a graph surface onto a plane, with the contours
showing the level curves of the graph.
• The first two arguments are one dimensional arrays representing the x- and y-
cooordinates of the points to plot.
• The third coordinate is a two dimensional array representing the z-coordinates.
• Contour levels are automatically set, although they can be customized.
• A colorbar can be added to display the level curves.
The following examples are of a filled and unfilled contour plot of the two-dimensional
2 2
Gaussian function, f (x, y) = e−(x +y ) .
72
CHAPTER 7. MATPLOTLIB 7.8. SLOPE FIELDS
The function quiver plots a two dimensional field of arrows, also known as a slope field.
• If four or more arguments are present, the first two arguments are one dimensional
arrays representing the x- and y-cooordinates of the arrows.
• The next two required arguments are two dimensional arrays representing the x- and
y-components of the arrow vectors.
• An optional argument can be used to change the individual colors of the arrows.
Keyword arguments allow the shape and appearance of the arrows to be customized.
The following example is of a quiver plot of the system of ordinary differential equations,
dx dy
= 1 − y2, =y−x
dt dt
Plotting a Slope Field
import numpy as np # Imports linspace, meshgrid
import matplotlib.pyplot as plt # Import plotting functions
Quiver plot
2.0
1.5
1.0
0.5
0.0
y
0.5
1.0
1.5
2.0
3 2 1 0 1 2 3
x
73
CHAPTER 7. MATPLOTLIB 7.9. STREAM PLOTS
The function streamplot plots the streamlines of a vector flow. This is similar to a slope
field, but with the arrows of the slope field connected into continuous curves.
• The first two arguments are one dimensional arrays representing the x- and y-
cooordinates of the vectors.
• The next two arguments are two dimensional arrays representing the x- and y-
components of the vectors.
• Keyword arguments allow the density, color, and thickness of the streamlines to be
customized.
The following example is of a stream plot of the same system of ordinary differential
equations used in the previous slope field example.
Plotting a Stream Plot
import numpy as np # Imports linspace, meshgrid
import matplotlib.pyplot as plt # Import plotting functions
Stream plot
2.0
1.5
1.0
0.5
0.0
y
0.5
1.0
1.5
2.0
3 2 1 0 1 2 3
x
74
CHAPTER 7. MATPLOTLIB 7.10. MULTIPLE PLOTS
The function subplot is used to plot multiple graphs on a single figure. This divides a
figure into a grid of rows and columns, with plotting done in the currently active subplot.
• Calls to subplot specify the number of rows, number of columns, and subplot number.
• Subplots are numbered from left to right, row by row, starting with 1 in the top left.
• All plotting is done in the location specified in the most recent call to subplot.
• If there are less than 10 rows, columns and subplots, subplot can be called with a
string argument. For example, subplot(2, 3, 4) is the same as subplot(”234”).
The example below uses 2 rows and 3 columns. The ”subplot” calls displayed on the
figure show which call corresponds to each grid location.
5 5 5
4 4 4
3 3 3
2 subplot(2,3,1) 2 subplot(2,3,2) 2 subplot(2,3,3)
1 1 1
0 0 0
0 2 4 6 8 10 0 2 4 6 8 10 0 2 4 6 8 10
5 5 5
4 4 4
3 3 3
2 subplot(2,3,4) 2 subplot(2,3,5) 2 subplot(2,3,6)
1 1 1
0 0 0
0 2 4 6 8 10 0 2 4 6 8 10 0 2 4 6 8 10
75
CHAPTER 7. MATPLOTLIB 7.11. FORMATTING TEXT
The function text is used to add a text string to a plot at a given position.
• The first three positional arguments specify the x-position, y-position, and text string.
• The fontsize argument specifies the size of the font to use.
• The fontstyle argument specifies the style of font to use (’normal’, ’italic’ etc).
• The fontweight argument specifies how heavy the font should be (’normal’, ’bold’).
• The family argument specifies the font family to use (’serif’, ’sans-serif’ etc).
• The color argument specifies the color of the text.
These options can be combined together (for example, to specify text that is bold, red,
italic and 14-point). The example below illustrates the use of these options.
Formatting Text
import matplotlib.pyplot as plt # Import plotting functions
sizes = [10,12,14,16,18]
for sizepos, size in enumerate(sizes):
plt.text(0, sizepos, "Font size = {}".format(size), fontsize=size)
76
CHAPTER 7. MATPLOTLIB 7.12. FORMATTING MATHEMATICAL EXPRESSIONS
The example below illustrates several examples of mathematical expressions using LATEX.
Formatting Mathematical Expressions with LATEX
import numpy as np # Import arange, cumsum, pi
import matplotlib.pyplot as plt # Import plotting functions
x = np.arange(1,10)
y1 = np.cumsum(1/x) # cumsum calculates the cumulative sum
y2 = np.cumsum(1/(x**2))
width = 0.4
plt.bar(x, y1, width=width, fc='c',label=r'$\sum_{i=1}^n \frac{1}{i}$')
plt.bar(x+width, y2, width=width, fc='y', label=r'$\sum_{i=1}^n \frac{1}{i^2}$')
ticks = x + width # Shift the x-ticks to center on the bars
xlabels = [str(val) for val in x] # Labels must be strings
plt.xticks(ticks, xlabels)
ticks = [1, np.pi**2/6]
ylabels = [r'$1$', r'$\pi^2/6$'] # Note that \pi renders as a symbol
plt.yticks(ticks, ylabels)
plt.xlabel(r'$n$')
plt.legend(loc='upper left')
plt.title('Partial sum of p-series for p = 1, 2')
π 2 /6
1 2 3 4 5 6 7 8 9
n
77
8 Differential Equations
The value returned by odeint is an array of solutions to the differential equation for each
time in the array t. The first value in the solution is the initial value y0.
To apply odeint to an n-th order differential equation, we need to first solve for the n-th
derivative:
dn x dn−1 x
dx
= f t, x, , . . . , n−1
dtn dt dt
and then convert to a first-order system as follows:
dx d2 x dn−1 x
y = x, , 2 , . . . , n−1
dt dt dt
So:
dx d2 x dn x dx d2 x dn−1 x
d dx
y= , ,..., n = , , . . . , f t, x, , . . . , n−1
dt dt dt2 dt dt dt2 dt dt
The function defined by func then needs to accept the array y as an argument and return
an array of derivatives corresponding to the elements of y.
78
CHAPTER 8. DIFFERENTIAL EQUATIONS
8.1. FIRST-ORDER DIFFERENTIAL EQUATIONS
8
Population
0 2 4 6 8 10
time
79
CHAPTER 8. DIFFERENTIAL EQUATIONS 8.2. HIGHER ORDER LINEAR EQUATIONS
0.00
0.25
0.50
0.75
0 1 2 3 4 5
time
80
CHAPTER 8. DIFFERENTIAL EQUATIONS 8.3. SYSTEMS OF EQUATIONS
The following example solves a system of equations known as a predator-prey model. The
state is defined by the population of predators y and their prey x, coupled by the pair of
equations:
dx dy
= x(a − py) = y(−b + qx)
dt dt
The constants a, p, b and q control rates of growth and decline due to interactions between
the species, and to natural rates of birth and death.
Solving a System of Equations Using odeint
import numpy as np # Import linspace
import matplotlib.pyplot as plt # Import plotting functions
from scipy.integrate import odeint
Predator-Prey Model
70
60
50
x, y
40
30
prey
20 predators
0 10 20 30 40 50 60 70 80
time
81
9 Additional Topics
We often need to load files containing numerical data into a NumPy array for further
processing and display. Such data files typically consist of:
• Header information. This describes what the data represents and how it is formatted.
• A set of rows of numerical data. Each row contains the same number of values,
separated by some string such as a comma or tab.
The NumPy function numpy.loadtxt can be used to load such data. This returns a NumPy
array, where each row corresponds to a line in the data file. The first argument to this
function is the data file name. Some of the optional keyword arguments are shown below.
• dtype. This is the data type of values in the array, which are floats by default.
• delimiter. This is the string used to separate values in each row. By default, any
whitespace such as spaces or tabs are considered delimiters.
• skiprows. This is the number of rows to ignore at the start of the file before reading
in data. It is usually used to skip over the header information, and defaults to 0.
The example shown below uses a file called “weather.dat”, which contains the following:
82
CHAPTER 9. ADDITIONAL TOPICS 9.2. IMAGES
9.2 Images
Matplotlib provides functions for saving, reading, and displaying images. These images
are either 2- or 3-dimensional NumPy arrays. In both cases, the first two axes of the array
correspond to the rows and columns of the image. The third axis corresponds to the color
of the pixel at each (column, row) coordinate.
• For a 2D array, the array values are floats in the range [0, 1]. These represent the
luminance (brightness) of a grayscale image from black (0) to white (1).
• For a 3D array, the third axis can have either 3 or 4 elements. In both cases, the
first three elements correspond to the red, green, and blue components of the pixel
color. These can be either floats in the range [0, 1], or 8-bit integers of type ’uint8’.
A fourth element corresponds to an “alpha” value representing transparency.
img = np.zeros((100, 100, 3)) # Create an image array of 100 rows and columns.
img[:60,:60,0] = 1. # Set the top-left corner to red.
img[40:,40:,1] = 1. # Set the lower-right corner to green.
img[20:80,20:80,2] = 1. # Set the center square to blue.
83
CHAPTER 9. ADDITIONAL TOPICS 9.3. ANIMATION
9.3 Animation
An animation consists of a sequence of frames which are displayed one after the other.
Animation using Matplotlib essentially involves updating the data associated with some
drawn object or objects (such as points or lines), and redrawing these objects. Producing
an animation therefore involves the following steps:
• fig. This is the figure in which the animation is to be drawn. It can be obtained
using either the Matplotlib figure or subplots functions.
• func. This specifies the function to call to perform a single step of the animation. It
should take a single argument which is the frame number (an integer). The frame
number is used to update the values of drawn objects such as points or lines. If the
blit keyword argument is True, this function should return a tuple of the modified
objects that need to be redrawn.
The following example for Jupyter Notebook animates a point circling the origin with
constant angular velocity. The animate function is defined to update the position of the
point based on the frame number.
84
CHAPTER 9. ADDITIONAL TOPICS 9.3. ANIMATION
Note that in Jupyter Notebook the IPython magic we need to use is %matplotlib rather
than %matplotlib inline. Inline graphs in Jupyter Notebook are static, meaning that
once drawn, they cannot be updated. Using %matplotlib generates graphs in a separate
window, where the updated data can be displayed.
85
CHAPTER 9. ADDITIONAL TOPICS 9.4. RANDOM NUMBER GENERATION
NumPy provides a library of functions for random number generation in the numpy.random
module. These return either a sample, or an array of samples of a given size, drawn from
a given probability distribution. The main functions we use are:
random.rand Samples are drawn from a uniform distribution over [0, 1).
random.randint Samples are integers drawn from a given range.
random.randn Samples are drawn from the “standard normal” distribution.
random.normal Samples are drawn from a normal (Gaussian) distribution.
random.choice Samples are drawn from a given list or 1D array.
The following examples illustrate the use of these functions.
86
CHAPTER 9. ADDITIONAL TOPICS 9.5. SOUND FILES
Sound is a vibration that propagates through a medium such as air as a wave of pressure
and displacement. Recording devices such as microphones convert this wave to an elec-
trical signal. This signal is then sampled at regular intervals and converted to a sequence
of numbers, which correspond to the wave amplitude at given times.
The WAV file format is a standard for storing such audio data without compression. WAV
files contain two main pieces of information:
• The rate at which the wave has been sampled, usually 44,100 times per second.
• The audio data, usually with 16 bits used per sample. This allows 216 = 65,536
different amplitude levels to be represented.
The module scipy.io.wavfile provides functions to read and write such files.
scipy.io.wavfile.read Read a WAV file, returning the sample rate and the data.
scipy.io.wavfile.write Write a NumPy array as a WAV file.
The following example creates and saves a WAV file with a single frequency at middle C,
then plots the first 1000 samples of the data.
87
CHAPTER 9. ADDITIONAL TOPICS 9.6. LINEAR PROGRAMMING
Linear programming problems are a special class of optimization problem. They involve
finding the maximum (or minimum) of some linear objective function f (x) of a vector of
variables x = (x1 , x2 , . . . , xn ), subject to a set of linear equality and inequality constraints.
Since the objective function and constraints are linear, we can represent the problem as:
Maximize cT x, where the vector c contains the coefficients of the objective function,
subject to Aub ∗ x ≤ bub , where Aub is a matrix and bub a vector,
and Aeq ∗ x = beq , where Aeq is a matrix and beq a vector.
An example of such a problem would be: x = {x1 , x2 }. Maximize f (x) = 2x1 + 3x2
subject to the inequality constraints (i) 0 ≤ x1 ≤ 80, (ii) x2 ≥ 0, (iii) x1 + x2 ≤ 100, and
(iv) x1 + 2x2 ≤ 160.
This example is graphed below, showing the level curves of f (x).
88
CHAPTER 9. ADDITIONAL TOPICS 9.6. LINEAR PROGRAMMING
beq given above. An optional bounds argument represents the range of permissible values
that the variables can take, with None used to indicate no limit.
Applying linprog to this problem is done as shown below.
This yields the correct solution for x1 and x2 , as seen in the graph above:
[ 40. 60.]
Note that linprog finds the minimum of f (x). To find the maximum, the
negative of the c coefficient values needs to be used instead.
89
IV Programming Tips
90
10 Programming Style
This chapter contains some tips on how to make programs easier to read and understand.
Programs are written first and foremost to be understood by human beings, not by com-
puters. Ideally, it should be possible a year from now for you to pick up the code that
you’re writing today and still understand what you were doing and why.
Good variable names make reading and debugging a program much easier. Well chosen
names are easy to decipher, and make the intent clear without additional comments.
• A variable name should fully and accurately describe the data it represents. As an
example, date may be ambiguous whereas current_date is not. A good technique
is to state in words what the variable represents, and use that for the name.
• Names that are too short don’t convey enough meaning. For example, using d for
date or cd for current date is almost meaningless. Research shows that programs
with variable names that are about 9 to 15 characters long are easiest to understand
and debug.
• Variable names should be problem-oriented, refering to the problem domain, not how
the problem is being solved. For example, planet_velocity refers to the problem,
but vector_3d refers to how this information is being represented.
• Loop indices are often given short, simple names such as i, j and k. This is okay
here, since these variables are just used in the loop, then thrown away.
• If loops are nested, longer index names such as row and column can help avoid
confusion.
• Boolean variables should have names that imply either True or False. For example,
prime_found implies that either a prime has been found, or it hasn’t.
• Boolean variables should be positive. For example, use prime_found rather than
prime_not_found, since negative names are difficult to read (particularly if they are
negated).
• Named constants should be in uppercase and refer to what the constant represents
rather than the value it has. For example, if you want to use the same color blue
for the font in every title, then define the color in one place as TITLE_FONT_COLOR
rather than FONT_BLUE. If you later decide to have red rather than blue titles, just
redefine TITLE_FONT_COLOR and it keeps the same meaning.
91
CHAPTER 10. PROGRAMMING STYLE 10.2. CHOOSING GOOD FUNCTION NAMES
The recommended style for naming functions in Python is to use all lowercase letters,
separated by underscores as necessary. As with variable names, good function names can
help make the intent of the code much easier to decipher.
• For procedures (functions that do something and don’t return a value), use a verb
followed by an object. An example would be plot_prime_distribution.
• For functions that return values, use a description of what the returned value repre-
sents. An example would be miles_to_kilometers.
• Don’t use generic names such as calculate_stuff or numbered functions such
as function1. These don’t tell you what the function does, and make the code
difficult to follow.
• Describe everything that the function does, and make the function name as long as
is necessary to do so. If the function name is too long, it may be a sign that the
function itself is trying to do too much. In this case, the solution is to use shorter
functions which perform just one task.
Magic numbers are numbers such as 168 or 9.81 that appear in a program without
explanation. The problem with such numbers is that the meaning is unclear from just
reading the number itself.
• Numbers should be replaced with named constants which are defined in one place,
close to the start of your code file.
• Named constants make code more readable. It’s a lot easier to understand what
HOURS_PER_WEEK is referring to than the number 168.
• If a number needs to change, named constants allow this change to be done in one
place easily and reliably.
10.4 Comments
It’s not necessary to comment every line of code, and ”obvious” comments which just
repeat what the code does should be avoided. For example, the endline comment in the
following code is redundant and does nothing to explain what the code is for.
x += 1 # Add 1 to x
• ”Intent” comments explain the purpose of the code. They operate at the level of the
problem (why the code was written) - rather than at the programming-language level
(how the code operates). Intent is often one of the hardest things to understand
when reading code written by another programmer.
92
CHAPTER 10. PROGRAMMING STYLE 10.5. ERRORS AND DEBUGGING
• ”Summary” comments distill several lines of code into one or two sentences. These
can be scanned faster than the code itself to quickly understand what the code is
doing. For example, suppose you are creating several different graphs for a report. A
summary comment before each set of plotting commands can describe which figure
in the report the code is producing.
Endline comments are those at the end of a line, after the code. They are best avoided
for a number of reasons.
• Endline comments are short by necessity as they need to fit into the space remaining
on a line. This means that they tend to be cryptic and uninformative.
• Endline comments are difficult to keep aligned (particularly as the code changes),
and if they’re not aligned they become messy and interfere with the visual structure
of the code.
A final note is to get in the habit of documenting code files. At the top of every file,
include a block comment describing the contents of the file, the author, and the date the
file was created. An example would be:
In the event of an error being generated, IPython will typically give as much information
as possible about the error. If this information is not sufficient, the %debug magic will
start the IPython debugger. This lets the current values of variables inside a function be
examined, and allows code to be stepped through one line at a time.
93
11 Further Reading
The following books may prove useful for further study or reference.
94
Index
95
INDEX INDEX
quiver, 73 values, 39
variable names, 24, 91
random numbers, 86
random.normal, 86 WAV files, 87
random.rand, 86 while, 40
random.randint, 60, 86
random.randn, 86 zeros, 55
range, 38 zeros like, 55
read, 42, 43 zip, 39
96