Note: if you are a matlab user, you may want to look at
In python, there are a few ways to manage collections of information:
list
is built-in to the language, and can contain any typetuple
is also built-in to the language, and is similar to a list, but is immutable.numpy
package in python and is useful for holding collections of numbers.A list in python is enclosed by square brackets, and can contain any types (including other lists):
# A list of integers:
x = [1,2,3,4]
# A list of strings:
fruits = ['apple','banana','orange']
# A list of many things, including other lists!
my_list = [ 2, 'a string', 5, 3.14, x, fruits ]
print(my_list)
print(my_list.index(x)) # find where x appears (4)
print(my_list[2]) # 5
[2, 'a string', 5, 3.14, [1, 2, 3, 4], ['apple', 'banana', 'orange']] 4 5
You'll note from this example that python arrays are 0-based. The first element in the list is accessed via [0] while the second is [1], etc.
You can think of a list in python as an array in Matlab, C/C++, Fortran, etc. The primary difference is that, in Python, a list can have different types (strings, integeres, etc.) in the same list/array.
___Caution___: copying arrays in python is unique:
a = [1,2,3]
b = a
makes b
alias to a
. This means that
b[1]=5
also changes a[1]
to 5:
print(a,b)
[1, 5, 3] [1, 5, 3]
To make a full copy of an array, use the copy
method:
b = a # b and a are the same array
c = a.copy() # c and a are different arrays with the same contents
c[2] = 19
print(a,b,c)
[1, 5, 3] [1, 5, 3] [1, 5, 19]
Frequently we want to perform operations on lists
Method | Description | Example |
---|---|---|
len |
Returns the number of entries in the list | len(my_list) |
append(entry) |
Append an entry to the list | my_list.append( 'another entry' ) |
insert(index,entry) |
Insert an entry in the list | my_list.insert(2,10) |
extend(list2) |
append a list | my_list.append( ('a','b',x) ) |
+ |
append a list (same as extend ) |
my_list + my_list |
remove(entry) |
Append an entry to the list | my_list.remove( x ) |
sort() |
sort the list - only works when the list is homogeneous | x.sort() |
reverse() |
reverses the entries in the list | my_list.reverse() |
pop(index) |
return and remove the element in the list at index |
my_list.pop(3) |
count(entry) |
return the number of occurences of entry in the list |
my_list.count(2) |
index(entry) |
return the index at which the first occurence of entry occurs |
my_list.index(x) |
You can also use the keyword in
with a list:
fruits = ['apple','pear','banana','payapa']
if 'banana' in fruits:
print('Yes!')
else:
print('No!')
Yes!
Indexing a list is done with the []
operator, and is 0-based (0 indicates the first entry in the list). For example, given:
fruits = ['apple','banana','grapefruit','orange']
the following table shows various indexing operations:
Operation | Result | Description |
---|---|---|
fruits[1] |
banana |
Accesses the second element in the list |
fruits[-1] |
orange |
Accesses the last element in the list |
fruits[-2] |
grapefruit |
Accesses the second to last element in the list |
fruits[:2] |
[apple,banana] |
The first two elements in the list |
fruits[1:3] |
[banana,grapefruit] |
The second and third elements in the list |
The :
operator allows us to perform slicing, accessing a subset of the entries in a list.
You can also use slicing to replace elements of a list. For example:
x = [1,2,3,4]
y = x[1:3] # note that this doesn't make a copy! y is refering to part of x
y.reverse()
print("y: ", y)
x[0:2] = y
print("x: ", x)
y: [3, 2] x: [3, 2, 3, 4]
There are a few ways to loop over lists. This is perhaps best illustrated by a few examples.
fruits = ['apple','banana','orange']
for i in fruits:
print(i)
apple banana orange
Here, i
is an iterator that represents each entry in the list
Here we use the range()
function, which builds a range space for the loop. This allows us to use i
as an index:
fruits = ['apple','banana','orange']
for i in range(0,len(fruits)):
print('Fruit {} = {}'.format(i,fruits[i]))
Fruit 0 = apple Fruit 1 = banana Fruit 2 = orange
range(lo,hi)
creates a range of integers from lo
to hi-1
List comprehensions can be used to quickly build lists conforming to specific patterns. For example, if we wanted to build a list $$x_i=i^2, \quad i=1\ldots4$$ we can do this by:
x = [i**2 for i in range(1,4)]
print(x)
[1, 4, 9]
Similarly, to achieve $y_i=2^i, \; i=1\ldots 8$, we can do:
y = [2**i for i in range(1,9)]
print(y)
[2, 4, 8, 16, 32, 64, 128, 256]
List comprehensions provide a relatively simple syntax to build these types of lists.
In python, a tuple is like a list, but has a few differences:
()
rather than []
Elements in a tuple are accessed in the same way as lists, using the []
operator.
Generally, you will use lists rather than tuples.
The list functionality in python is not as useful as it could be when it comes to numerical operations. For example, you cannot perform mathematical operations on lists. This is where numpy comes in.
Unlike a Python list, a Numpy arrays is homogeneous - they contain entries of the same type (e.g., integer, real, complex).
Here and below, we will assume that you have:
import numpy as np
so that we can use np.
to shorten reference to numpy functions.
A numpy array is characterized by its shape and the type of elements it contains.
x = np.array( [1,2,3] ) # a 1-dimensional row vector
y = np.array( [ [1,2,3],[4,5,6] ] ) # a 2-dimensional matrix
print(x.shape,x)
print(y.shape,y)
(3,) [1 2 3] (2, 3) [[1 2 3] [4 5 6]]
x = np.array( [1,2,3], dtype=complex )
print(x) ## [ 1.+0.j 2.+0.j 3.+0.j ]
[1.+0.j 2.+0.j 3.+0.j]
Function | Description |
---|---|
linspace(lo,hi,npts) |
Builds a 1D array with a npts entries between lo and hi |
arange(lo,hi,spacing) |
Builds a 1D array spaced with spacing starting at lo and ending near hi |
logspace(lo,hi,npts) |
Builds a 1D array with npts points between $10^\mathrm{lo}$ and $10^\mathrm{hi}$ |
meshgrid(x,y,...) |
Given vectors specifying the range of each axis, builds a grid. |
Command | Description |
---|---|
empty(shape) |
Build an empty array. |
empty_like(a) |
Build an empty array shaped like a |
eye(N) |
Build a 2D identity matrix with N rows and columns |
ones(shape) |
build an array of ones with the specified shape |
ones_like(a) |
Build an array of ones shaped like a |
zeros(shape) |
build an array of zeros with the specified shape |
zeros_like(a) |
Build an array of zeros shaped like a |
full(shape,val) |
Build an array of the specified shape filled with val |
full_like(a,val) |
Build an array shaped like a filled with val |
random.random(shape) |
Build an array of random numbers with the specified shape |
Examples:
x = np.ones( [1,3] ) # 3-element row vector
y = np.empty_like( x ) # empty 3-element row vector
z = np.zeros( [3,3] ) # 3x3 matrix
p = np.full_like(z,np.pi) # 3x3 matrix full of 𝜋
r = np.random.random([2,3])
All of these can have an additional dypte
argument to specify the type of array to build (see above).
For more information on many other ways of building arrays, see the numpy docs
Most of the functions mentioned above such as zeros
, ones
, full
, eye
, etc. will create matrices as well - just give the appropriate shape.
Additionally, the diag function is very useful:
diag(v,k)
builds a matrix with v
on its k
th diagonal. For example, the following builds a tridiagonal matrix:n = 5
d = np.full(n,-3)
ud = np.full(n-1,1)
mat = np.diag(d,0) + np.diag(ud,1) + np.diag(ud,-1)
diag(m,k)
extracts the k
th diagonal of the matrix m
m = np.random.random([5,5])
# extract the main diagonal of m
md = np.diag(m,0)
There are many array manipulation tools. Some of the more frequently used ones include:
Function | Description |
---|---|
reshape(a,shape) |
Reshape the data in a to a shape |
ndarray.flat |
Obtain an iterator over the array |
transpose(a) |
Transposes the array |
tile(a,nrep) |
Tile a nrep times. nrep can be an array. |
flip(a,axis) |
Reverse the elements along the axis dimension of a . |
fliplr(a) |
Flip the array in the left/right direction |
flipud(a) |
Flip the array in the up/down direction. |
a = np.linspace(0,4,10)
a[3] = -2
for i in a:
print(i)
0.0 0.4444444444444444 0.8888888888888888 -2.0 1.7777777777777777 2.2222222222222223 2.6666666666666665 3.1111111111111107 3.5555555555555554 4.0
You can also slice arrays:
a = np.random.random([4,4])
print(a)
a[1:2,1:3]
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(x[1:7:2])
[[0.18403801 0.52644116 0.60619046 0.67778473] [0.16377359 0.53305578 0.83653102 0.45975799] [0.26448895 0.33457493 0.81659841 0.81666402] [0.14163167 0.46372596 0.6559922 0.39512843]] [1 3 5]
And slice from the "back" of arrays:
a = np.arange(0,10,1)
print(a[-3:4:-1]) # start at third-to last, end at fifth
[7 6 5]
And slice all after:
a = np.arange(0,10,1)
print(a[5:])
[5 6 7 8 9]
Numpy arrays support typical mathematical operations like +
, -
, *
(element-wise multipy), /
(element-wise divide) and **
(element-wise exponentiation) provided the arrays are the same shape.
Numpy provides numerous mathematical functions that operate on arrays. For example:
x = np.linspace(-np.pi,np.pi)
y = np.sin(x)
z = x**2
n = 10
np.prod( np.arange(1,n+1,1) ) # the factorial of n
3628800
Given the arrays $A = \left[\begin{array}{cc} 1 & 2\\ 3 & 4 \end{array}\right]$, $b=\left(\begin{array}{c} 5\\6\end{array}\right)$, and $c=\left(\begin{array}{c} 7\\8\end{array}\right)$,
A = np.array([[1,2],[3,4]])
b = np.array([[5],[6]])
c = np.array([[7],[8]])
For those coming from Matlab, the most familiar application of elemental multiplication is on arrays of the same shape. However, elemental multiplication does not require arrays to have the same shape. For example:
Operation | Description | Result |
---|---|---|
A * A |
Square elements in A |
$\begin{array}{cc} 1&4\\9&16\end{array}$ |
b * c |
Element-wise multiplication of b and c |
$\begin{array}{c} 35\\48\end{array}$ |
A * b |
Multiply a 2x2 by a 2x1. Results in a 2x2 | $\begin{array}{cc} 5&10\\18&24\end{array}$ |
b.transpose() * c |
Element-Multiply a row vector to a column vector | $\begin{array}{cc} 35&42\\40&48\end{array}$ |
While *
implies elemental multiplication, @
implies matrix multiplication.
Here are some example operations on the above arrays:
Operation | Result |
---|---|
A @ b |
$\begin{array}{c} 17 \\ 39 \end{array}$ |
b.transpose() @ c |
39 |
b@c |
error |
b.transpose() @ A @ c |
433 |
Among the other things that you should be aware of:
numpy.fft
module.numpy.linalg
module. This includes things like dot products, matrix products, norms, matrix decomositions, etc.