My Python notes
- 1 Something about
print
- 2 Import modules
- 3 Variables and objects
- 3.1 Strings
- 3.2 Booleans
- 3.3 Numbers
- 3.4 Type casting
- 3.5 List
- 4
if
statements - 5 Loops
- 5.1
for
loops - 5.2
while
loops
- 5.1
- 6 Defining function
- 7 Debugging and troubleshooting
- 7.1 Built-in exception types:
- 7.2 Raise error
- 7.3 Code review
- 7.4 Interact with
input()
function
- 8 Namespace and scope
- 9 N-dimensional array:
ndarray
type- 9.1 Special arrays
- 9.2 Element-wise operations
- 9.3 Matrix operations and linear algebra
- 9.4 matrix operations using sub-module
linalg
- 9.5 Indexing and slicing Numpy arrays
- 10 Basic reading and writing data from/to an external file
- 10.1 Using
open()
and.close()
- 10.2 Using the
with
statement - 10.3 Writing to a file
- 10.1 Using
- 11 Plotting with
matplotlib.pyplot
- 11.1 Basic of plots
- 11.2 Customising plots
- 11.2.1 Setting plot axis properties
- 12 SciPy
- 12.1 Interpolating some noisy data
- 12.2 Summary statistics, reading a file
- 12.3 Optimisation
- 13 Tuples
- 14 Dictionaries
- 15 Compreshensions
- 16 Designing algorithms
- 16.1 Recursion
- 16.2 A process for problem-solving
1 Something about print
syntax | explanation |
---|---|
print(str1, str2, sep = "") |
print strings separately |
print(str1 + str2) |
print strings as one string |
print(f'{my_var}') |
f-string interpret what inside the {} |
print('%type' %my_var) |
%d : integer; %f : float; %s : string; %b : binary; (doc) |
print(\n) |
print use escape characters |
print(f'{my_var:n[type]}') |
print the variable at a certain length |
{:<} , {:>} , {:^} |
left aligned, right aligned, centred |
2 Import modules
- Import a module as a separate namespace
import numpy as np
print(np.cos(np.pi))
- Import something from a module into the main namespace
from numpy import cos, pi
print(cos(pi))
3 Variables and objects
type()
: check data types.
-
Numeric:
int
,float
,complex
; -
Sequence:
str
,list
,range
,tuple
-
Boolean
-
Set
-
Dictionary
-
number objects and string objects
3.1 Strings
syntax | explanation |
---|---|
my_str[i] |
returns the \(i+1\)th character in string |
my_str[-i] |
returns the \(i\)th from the end |
len(my_str) |
the length of the string |
max(my_str) , min(my_str) |
alphabetically |
sorted(my_str) or my_str.sort() |
from smallest to largest |
my_str.capitalize() |
capitalize the first letter of the string |
my_str.split() |
split the string |
3.2 Booleans
Operators:
syntax | explanation |
---|---|
and , or , not |
logical operators |
== , < , <= , != |
comparison operators |
3.3 Numbers
Floating points numbers: 1.0e-6
is \(1 \times 10^{-6}\).
syntax | explanation |
---|---|
+ , - , * , / |
addition, subtraction, multiplication, division |
** |
exponentiation |
// |
integer division |
% |
modulus; the remainder of a deviation |
math.isclose(n1, n2, rel_tol=1e-6)
: Boolean variable if n1 and n2 is close.
3.4 Type casting
int()
, float()
, str()
.
bool(0)
and bool('')
are false.
3.5 List
List can contain almost any other object.
my_list = ['my', 1, 4.5, ['you', 'they'], 432, -2.3, 33]
emplist = []
[sth] * n # [sth, s]
print(list_1 + list_2)
print('hellow'+'world') # "helloworld"
my_list.append('extar') # add an item to the end of a list
print(my_list[3][1]) # the outcome is "they"
print(sorted(numberlist)) # sort a list
print(12 * my_list) # create a list with 12 repetition of my_list
print('you' in my_list[3]) # check if something is in a list
Useful tools:
syntax | explanation |
---|---|
len(my_list) |
the length of the list |
my_list.append('a new element') |
add a new element to the list |
Slicing:
a = [2, 5, 4, 8, 8]
print(a[1:3]) # the 2nd and 3rd | [5, 4]
print(a[2:]) # the 3rd to the last | [4, 8, 8]
print(a[:-2]) # the 1st to the 3rd from the last | [2, 5, 4]
syntax | explanation |
---|---|
my_list[start:stop] |
from start to stop-1 共stop-start个 |
my_list[start:] |
from start to len(l)-1 |
my_list[:stop] |
from 0 to stop-1 |
my_list[start:stop:step] |
from start to stop-1, with increment step |
my_list[::step] |
from 0 to len(l)-1, with increment step |
my_list[::] , my_list[:] |
all the elements |
- If
j
\(=\)i
, thena[i:j]
anda[i:j:k]
are empty lists, for any value ofk
. - If
j
\(<\)i
, thena[i:j]
anda[i:j:k]
are empty lists, for positive values ofk
.
4 if
statements
if my_condition:
[some instructions]
my_condition
is a Boolean object.
if cond_1:
[some instructions]
elif cond_2:
[other instructions]
else:
[other instructions]
5 Loops
5.1 for
loops
for i in my_seq:
[some instructions]
- ranges: a sequence type
syntax | explanation |
---|---|
range(j) |
\(0\), \(1\), \(2\), ..., \(j-1\); 共\(j\)个. |
range(i, j) |
\(i\), \(i+1\), \(i+2\), ..., \(j-1\); 共\(j-i\)个. |
range(i, j, k) |
\(i\), \(i+k\), \(i+2k\), ..., \(i+m\); \(i+m \leq j-1\). |
5.2 while
loops
while my_condition:
[some instructions]
Use break
to exit the loop conditionally:
while my_condition:
[some instructions]
if my_condition:
break
6 Defining function
def my_func(inputs):
[function body]
return outputs
Outputs can be variables separated by commas.
Docstrings of functions:
In general, a docstring should consist of:
- a brief (1 or 2 sentences) description of what the function does,
- a description of all input arguments and their type,
- a description of all outputs and their type.
def some_func(some_input):
'''
a one-sentence description of the function
Input:
some_input (type): description.
Output:
some_output (type): description.
'''
# the main body
return your_output
Use help(some_func)
to see the function's docstring.
7 Debugging and troubleshooting
7.1 Built-in exception types:
- IndexError: a sequence subscript is out of range. r instance, here, we're trying to access
my_list[4]
, butmy_list
only has elements up tomy_list[3]
. - NameError: the variable referred to does not exist -- there is no box in memory with this label. This often comes up when you mistype a variable name.
- SyntaxError: the code is not syntactically correct -- it is not valid Python, so Python doesn't know how to interpret it.
- TypeError: a very common error to see when learning Python! This means that an operation or a function is applied to an object of the wrong type
- ValueError: raised when an operation or function is applied to an object with the right type, but an invalid value. For example, the
int()
function can cast a string to an integer, if the string can be interpreted as a number.
7.2 Raise error
if Cond:
raise ValueError('Error massages')
Catch errors:
try:
# do something
except:
# if the bit in "try" produces a runtime error
Use eval()
to evaluate operations:
# Put all operations in a list, as *strings*
operations = ['...', '...', ...]
# Use eval() to try all of them, print('error')
# if they don't work
for op in operations:
try:
eval(op)
except:
print('error')
7.3 Code review
- Is the code easy to read and to understand?
- Are there any bugs? Does the code produce the intended result, without errors?
- Could this code easily be re-used or adapted in other contexts?
- Is the implementation efficient? Is there too much data stored in memory that we don't need? Is there another way to program this, which takes less time to compute?
7.4 Interact with input()
function
your_inpt = float(input('Please enter something and press enter:'))
8 Namespace and scope
Namespace: a set of names, together with the objects they refer to.
The function's namespace is deleted once the function exits (that is, once the command func()
has finished executing).
Scope: a part of your code from where you can access a certain namespace. local scope and global scope.
9 N-dimensional array: ndarray
type
np.array()
: create an array; takes a list as an input and returns an array where the rows are the elements of the list. (numpy.array — NumPy v1.21 Manual)
1-D Numpy arrays are similar to lists.
import numpy as np
# create a vector
my_vec = np.array(my_list)
# create a matrix
my_mat = np.array(a_list_of_lists, dtype=None)
# dtype is for specifying the type of its elements.
9.1 Special arrays
- create a \(n \times p\) matrix of zeros
mat_zeros = np.zeros([n, m])
- create a vector of ones of length \(n\)
vec_ones = np.ones(n)
- create an \(n \times n\) identity matrix
id_n_mat = np.eye(n)
- create a \(n \times p\) matrix of pseudo-random numbers between 0 and 1, sampled from a uniform distribution
randm_mat = np.random.random([n, p])
randm_mat = np.random.random([n, p]) * (b - a) + a # sampled from a unif(a, b)
- create a 1D array with a range from m to n with step \(p\)
mat_1d = np.arange(m, n, p)
- retrieve the dimensions of an array, as a tuple; a property of
ndarray
objects
print(mat_zero.shape) # no brackets for .shape
.reshape()
take one input argument and reshape the array.
eg. To construct the matrix
\[M = \begin{pmatrix} 0.1 & 0.2 & 0.3 \\ 0.4 & 0.5 & 0.6 \\ 0.7 & 0.8 & 0.9 \end{pmatrix} \]# First way: giving the list of rows explicitly M1 = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]]) print(M1) # Second way: using range() and .reshape() # Note that range() returns a sequence, which we can therefore use # directly as the input argument for np.array() M2 = 0.1 * np.array(range(1, 10)).reshape((3, 3)) print(M2) # Third way: using np.arange() and .reshape M3 = np.arange(0.1, 1, 0.1).reshape((3, 3)) print(M3)
9.2 Element-wise operations
element-wise (+
, -
, *
, /
, **
) operations can be used between:
- two arrays of the same size
- an array and a scalar
np.dot()
can be used as *
.
9.3 Matrix operations and linear algebra
syntax | explanation |
---|---|
np.matmul(A, B) or A @ B |
\(AB\) (matrix products) |
A.T or A.transpose() |
\(A^T\) (transpose) |
np.matmul(A, v) or A @ v |
\(Av\) |
np.matmul(v, B) or v @ B |
\(v^TB\) |
np.matmul(v, v) or v @ v |
\(v^2\) |
9.4 matrix operations using sub-module linalg
(Linear algebra (numpy.linalg) — NumPy v1.21 Manual)
Prefix with np.linalg.
when using.
syntax | explanation |
---|---|
np.linalg.eigvals(A) |
eigenvalues of a matrix (\(Ax = \lambda x\)) |
eig_val_A, eig_vec_A = np.linalg.eig(A) |
eigenvalues and right eigenvectors |
Q, R = np.linalg.qr(A) |
Q-R matrix decomposition |
U, S, V = np.linalg.svd(A) |
singular value decomposition |
np.linalg.inv(A) |
\(A^{-1}\) (inverse) |
np.linalg.det(A) |
$ |
np.linalg.solve(A, b) |
Solve \(Ax = b\) for \(x\) |
9.5 Indexing and slicing Numpy arrays
v[i]
is the \(i+1\)th element of the vector v
.
A[i, j]
is the element in the \(i+1\)th row and \(j+1\)th column of the matrix A
.
X[i, j, k, h, ...]
is used to index elements for tensors in higher dimensions.
eg. an example of how you can use Boolean indexing to extract elements of an array which fulfill certain conditions.
# Create a random number generator rng = np.random.default_rng() # Create a random 3x5 matrix of integers between 1 and 10 A = rng.integers(1, 11, size=[3, 5]) print(A, '\n') # Create a Boolean array the same shape as A, with True where the corresponding # elements of A are smaller than 5, and False elsewhere A5 = A < 5 print(A5, '\n') # Use A5 to return all elements of A smaller than 5 (in a 1D array) print(A[A5], '\n') # Display the rows of A starting at row 1, and columns ending at column 2 print(A[1:, :3], '\n') # Display the elements of that sub-matrix which are smaller than 5 print(A[1:, :3][A5[1:, :3]], '\n') # Reassign all elements of A which are greater than or equal to 5 with the value 100 A[np.logical_not(A5)] = 100 print(A)
10 Basic reading and writing data from/to an external file
10.1 Using open()
and .close()
myfile = open('mytextfile.txt', mode='r') # assign the opened file as a file object `myfile`
contents = myfile.read() # save the read file as `contents`
myfile.close() # close `myfile`
mode | description |
---|---|
r |
default, for reading |
r+ |
for both reading and writing |
w |
for writing only |
w+ |
for both writing and reading |
... | ... |
10.2 Using the with
statement
Use the with
statement to automatically closes the file when the block ends.
Use the .readline()
method of file objects to read the file line by line. .readlines()
reads all lines in at once. The return is a list.
with open('mytextfile.txt', 'r') as myfile:
lines_content = myfile.readline()
# read the first line
print(lines_content)
Read line by line:
# first approach: using 'readline'
with open('mytextfile.txt','r') as myfile:
# Initialise an empty list to store the lines
all_lines = []
# if line is empty, the end of file is reached
while True:
# use readline to read the next line...
line = myfile.readline()
if not line:
break
all_lines.append(line)
print(all_lines)
# 2
with open('mytextfile.txt','r') as myfile:
all_lines = myfile.readlines()
print(all_lines)
print(all_lines[1])
10.3 Writing to a file
Create a new file and open it by using open()
with the argument 'w'
.
Use the .write()
method of file objects to write into the file.
with open('mytextfile_new.txt', 'w') as mynewfile:
mynewfile.write(text1 + '\n')
mynewfile.write(text2 + '\n')
11 Plotting with matplotlib.pyplot
11.1 Basic of plots
Import the plotting tools frommatplotlib.pyplot
with
%matplotlib notebook # a notebook-wide setting for jupyter notebook
import matplotlib.pyplot as plt
%matplotlib inline # toggle back to the default behaviour
A basic plot can be generated using
plt.plot([x], y, [fmt], [x2], y2, [fmt2])
fmt can be:
-
color: Specifying Colors — Matplotlib 3.4.3 documentation
-
marker:
.
,,
,o
...matplotlib.markers — Matplotlib 3.4.3 documentation and 4 more pages
-
markersize
-
linestyle or ls:
':'
,-.
,--
,-
-
Linestyles — Matplotlib 3.4.3 documentation and 4 more pages
-
linewidth or lw
-
...
matplotlib.pyplot.plot — Matplotlib 3.4.3 documentation
The x-axis with \(n\) points from \(s\) to \(e\) can be created as
x = np.linspace(s, e, n)
Use plt.show()
to print the plot.
eg. plot \(y = x sin(x)\).
import numpy as np %matplotlib notebook import matplotlib.pyplot as plt # Create an x-axis with 1000 points x = np.linspace(0., 2*np.pi, 1000) # Evaluate the function at all these points y = x * np.sin(x) # Create the plot and display it plt.plot(x, y, 'k-')cv, plt.show()
Figures and axes are objects in python.
plt.subplots()
is used to create a new figure object.
fig, ax = plt.subplots(m, n, [figsize]) # figsize=(a, b)
# like `par(mfrow = c(m, n)) in r
Use .plot()
as a method of the Axes
object
ax[i, j].plot(x, y, [fmt]) # plot on row, jth col
Use .subplots_adjust()
to adjust the layout of subplots
plt.subplots_adjust(hspace=0.5, wspace=0.5)
parameters | description |
---|---|
left | The position of the left edge of the subplots, as a fraction of the figure width. |
right | The position of the right edge of the subplots, as a fraction of the figure width. |
bottom | The position of the bottom edge of the subplots, as a fraction of the figure height. |
top | The position of the top edge of the subplots, as a fraction of the figure height. |
wspace | The width of the padding between subplots, as a fraction of the average Axes width. |
hspace | The height of the padding between subplots, as a fraction of the average Axes height. |
11.2 Customising plots
11.2.1 Setting plot axis properties
- Axis limits:
.set_xlim()
and.set_ylim()
methods ofAxes
objects.
my_ax.set_xlim([start, end])
my_ax.set_ylim([start, end])
-
Axis labels:
.set_xlabel()
andset_ylabel()
methods ofAxes
objects.my_ax.set_xlabel(r"$x$ axis", fontsize=12) my_ax.set_ylabel(str, [kwargs])
kwargs: text properties controling the appearance of the label.
-
Adding a legend:
ax.legend()
would label all curves of the plot in theax
. But the label should be set inside the.plot()
usinglable=
argument, and can contain \(LaTeX\) code.
# .legend() will use the "label" arguments for each curve
ax.legend(loc='lower right', fontsize=14)
loc: combinations of 'center'
, 'upper'
, 'lower'
, 'left'
, 'right'
; and 'best'
matplotlib.legend — Matplotlib 3.4.3 documentation and 5 more pages
- Adding grid lines:
plt.grid()
12 SciPy
12.1 Interpolating some noisy data
import scipy.interpolate
import scipy as scp
# Make an interpolating function - this one goes through all of the
# data points, and joins them with a piecewise cubic function:
scp.interpolate.interp1d(x, y, kind='cubic')
# Now fit a spline using a different function that takes a smoothing
# parameter s, this spline won't go through all of the points but will
# look smoother:
f_smooth = scp.interpolate.UnivariateSpline(x, y, s=4)
# return a function object
12.2 Summary statistics, reading a file
import numpy as np
import scipy.stats as stats
# basic summary statistics:
stats.describe(germany)
# Find the 5% and 95% percentile locations:
np.percentile(germany, (5, 95))
# Find the Pearson's and Spearman's correlation coefficients
# between two columns:
print(stats.pearsonr(germany, denmark))
print(stats.spearmanr(germany, bulgaria))
# mode, count
mode, count = stats.mode(bulgaria)
# Display the results in a table, highlight correlated columns
colourmap = plt.cm.get_cmap('Greens') # get a colourmap function to map colours to numbers
colours = colourmap(pearson_coeffs)
fig, ax = plt.subplots(1, 1, figsize=(7, 2))
ax.table(cellText=pearson_coeffs,
rowLabels=countries,
colLabels=countries,
cellColours=colours,
loc='center',
cellLoc='center')
ax.axis('tight')
ax.axis('off')
plt.show()
# create a linear regression object using stats.linregress
regr = stats.linregress(germany, denmark)
12.3 Optimisation
scipy.optimize.linprog()
from scipy.optimize import linprog
A = np.array([[6, 3],
[3, -1],
[1, 0.25]])
b = np.array([40, 0, 4], dtype=float)
c = np.array([-30, -10], dtype=float)
solution = linprog(c, A, b)
results = A @ solution.x
13 Tuples
Tuples are immutable lists.
a_tuple = (4, 6, -2, 4, 0, 0)
# nest tuples in lists
b_tuple = [(1, 2), (), e, ('this', 'maybe')]
A useful feature is that variables can be unpacked from a tuple, meaning that we can, for example, assign the value of each element in a tuple to a different variable, in one line:
u, v, w = (3.4, 1, 'friday')
print(f'{u}, {w}')
# Swap 2 values
u, w = w, u
print(f'{u}, {w}')
Note: Lists, tuples, and strings are examples of sequences, meaning that their elements (for a str
, its characters) are ordered, and indexed by a number representing their position. Index slicing can also be used on any sequence type.
14 Dictionaries
A Python dictionary is a set of *key-value* pairs. Dictionary values can be any object (e.g. numbers, sequences, Booleans, even other nested dictionaries).
scores = {'Alice': 80, 'Bob': 64, 'Charlie': 72}
print(scores['Bob'])
print(scores) # Print the dictionary object
print(list(scores.items())) # Print dict items as a list of tuples
print(list(scores.keys())) # Print all keys in a list
print(list(scores.values())) # Print all values in a list
Outputs:
64
{'Alice': 80, 'Bob': 64, 'Charlie': 72}
[('Alice', 80), ('Bob', 64), ('Charlie', 72)]
['Alice', 'Bob', 'Charlie']
[80, 64, 72]
We can add and modify dictionary entries, or check whether a key exists in a dictionary:
Create an empty dictionary
my_dict = {}
Add 3 new items -- note that we don't need to append with dictionaries.
my_dict['First item'] = (4, 5)
my_dict['Second item'] = 'blue'
my_dict[(0, 1)] = True
print(my_dict)
print(len(my_dict))
Modify one item.
my_dict['Second item'] = 8.77
print(my_dict)
Check if a key (not a value!) exists in the dictionary.
print((0, 1) in my_dict)
print((4, 5) in my_dict)
{'First item': (4, 5), 'Second item': 'blue', (0, 1): True}
3
{'First item': (4, 5), 'Second item': 8.77, (0, 1): True}
True
False
Uses the .items()
method to loop over a dictionary and return a list of tuples.
my_dic.items()
15 Compreshensions
A combination of sequence, for loop and if statement. To construct a new sequence from an old sequence, with operation for each element of the original sequence. Can be nested.
[experession for item in seq if condition]
[expression1 if condition else expression2 for item in seq]
The same for dictionaries, tuples, etc.
# Flatten a matrix to a list -- here, "i" is a list of the rows of A
A = np.random.randint(1, 10, size=[3, 3])
print(f'A =\n{A}\n')
print([j for i in A for j in i])
16 Designing algorithms
16.1 Recursion
A procedure or function that calls itself in order to provide an answer.
Note that, just like in the mathematical expression, we need to specify the initial value explicitly -- otherwise, we would go on forever!
def fib_rec(p, q, n):
'''
Return the nth element of the (p,q)-Fibonacci sequence.
Input:
p (int), positive integer, first coefficient
q (int), positive integer, second coefficient
n (int), positive integer
Output:
Fn (int), the nth element of the sequence, defined by
F(n) = pF(n-1) + qF(n-2).
'''
if n == 1 or n == 2:
return 1
else:
return p*fib_rec(p, q, n-1) + q*fib_rec(p, q, n-2)
print(fib_rec(1, 1, 15))
print(fib_rec(6, 4, 10))
print(fib_rec(2, 1, 35))
16.2 A process for problem-solving
The Algorithm Design Manual
- What is the end goal of the problem? What should the final result look like?
- What data do I have? What object types are best suited to store/handle which data?
- What should the rough structure of the algorithm be? What essential techniques are needed – loops/decisions, for/while...
- Can I break the problem down into sub-tasks?
- Does it have a recursive structure – can I do a little bit of it, and leave myself with a problem that looks almost the same?
- How do I know when I get an answer – can I check the answer? (sometimes answers that are hard to get are easy to check!)
7-step guide to problem-solving
-
Step 1: Work an Example
The first step is to work one instance of the problem by hand.
-
Step 2: Write Down What You Did
The second step is to write down precisely what was done in Step 1.
-
Step 3: Generalize
In Step 3, the student should generalize from the specific values they used in Step 1 to any values of the parameters.
Students who get stuck here should repeat Steps 1 and 2 with different parameters
to help see the pattern. -
Step 4: Test by Hand
Step 4 is to test your algorithm by hand. Using different values for the parameters.
-
Step 5: Translate to Code
Any step that is too complex to translate into one or two statements should become its own function: students repeat the Seven Steps for that function and call it in this algorithm.
-
Step 6: Test
Step 6 is to test the program, by running actual test cases on the code. If a
test case fails, students proceed to Step 7. -
Step 7: Debug
Step 7 is to debug failed test cases.