Dr Jon Shiach, Department of Computing and Mathematics, Manchester Metropolitan University
On successful completion of this page readers will be able to:
In mathematics, vectors can be expressed as either a row or column of elements and matrices are a rectangular array of elements. An individual element in the vector $\mathbf{a}$ is identified by an index denoted using a subscript, e.g., $a_i$. The index is the position of the element in the vector starting at 1 for the first element. An individual element in a matrix is identified by two indices denoted in a subscript, e.g., $[A]_{ij}$, the first number corresponding to the row number and the second number corresponding to the column number.
$$\mathbf{a} = \begin{pmatrix} a_1, & a_2, & \cdots & a_n \end{pmatrix}, \qquad
\mathbf{b} = \begin{pmatrix} b_1 \\ b_2 \\ \vdots \\ b_n \end{pmatrix}, \qquad
A = \begin{pmatrix}
a_{11} & a_{12} & \cdots & a_{1n} \\
a_{21} & a_{22} & \cdots & a_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m1} & a_{m2} & \cdots & a_{mn}
\end{pmatrix}.$$
In computer programming, a vector or matrix is represented using an array. Arrays can be one-dimensional where they contain a single row or column of elements, similar to a vector, or two-dimensional array similar to a matrix. It is possible to have higher dimensional arrays but this is not recommended as it can over complicate a program.
To work with matrices and arrays in Python we can import the NumPy library. NumPy is short for 'numerical Python' and a library containing functions which are very useful for scientific computing. To import NumPy execute the code cell below so that all other code cells can use NumPy commands.
import numpy as np
The use of this command means we can call the commands from NumPy using the prefix np.
To define a one-dimensional array in Python we can use the np.array
command (numpy.array help page).
A = np.array([a1, a2, ... , an ])
It is standard practice (although not necessary) in programming to use an uppercase character for the first character in the array name. This helps to differentiate between arrays and variables.
The commands below defines and prints the array corresponding to the vector $\mathbf{a} = (1, 2, 3)$. Enter them into the code cell below and execute it (don't forget to execute the code cell above to use NumPy commands).
A = np.array([ 1, 2, 3 ])
print(A)
A = np.array([ 1, 2, 3 ])
print(A)
[1 2 3]
Note that the elements in the row vector are contained within square bracket and elements are separated using commas. To define a two-dimensional array (i.e., a matrix) we input multiple row vectors separated by commas.
A = np.array([[ a11, a12, ..., a1n ],
[ a21, a22, ..., a2n ],
: : :
[ am1, am2, ..., amn ]])
Define arrays for the following matrices
(i) $A = \begin{pmatrix} 1 & -2 \\ 0 & 5 \end{pmatrix}$
A = np.array([[ 1, -2 ],
[ 0, 5 ]])
print(A)
A = np.array([[ 1, -2 ],
[ 0, 5 ]])
print(A)
[[ 1 -2] [ 0 5]]
(ii) $B = \begin{pmatrix} 2 \\ -4 \\ 5 \end{pmatrix}$
B = np.array([ [2], [-4], [5] ])
print(B)
B = np.array([ [2], [-4], [5] ])
print(B)
[[ 2] [-4] [ 5]]
(iii) $C = \begin{pmatrix} 1 & 0 & 7 \\ 4 & 7 & 5 \end{pmatrix}$
C = np.array([[ 1, 0, 7 ],
[ 4, 7, 5 ]])
print(C)
C = np.array([[ 1, 0, 7 ],
[ 4, 7, 5 ]])
print(C)
[[1 0 7] [4 7 5]]
(a) $A = \begin{pmatrix}6, & 2, & 4, & -1 \end{pmatrix}$;
A = np.array([6, 2, 4, -1])
print(A)
[ 6 2 4 -1]
(b) $B = \begin{pmatrix} 3 & 5 & -2 \\ -2 & 4 & 3 \\ 7 & 2 & -1 \end{pmatrix}$;
B = np.array([[3, 5, -2], [-2, 4, 3], [7, 2, -1]])
print(B)
[[ 3 5 -2] [-2 4 3] [ 7 2 -1]]
(c) $C = \begin{pmatrix} 2 & 0 & -1 & 4 \\ 7 & -3 & 9 & -5\end{pmatrix}$;
C = np.array([[2, 0, -1, 4], [7, -3, 9, -5]])
print(C)
[[ 2 0 -1 4] [ 7 -3 9 -5]]
(d) $D = \begin{pmatrix} -4 & 4 & 2 \\ 7 & 5 & -3 \\ 5 & 1 & 6 \end{pmatrix}$.
D = np.array([[-4, 4, 2], [7, 5, -3], [5, 1, 6]])
print(D)
[[-4 4 2] [ 7 5 -3] [ 5 1 6]]
In Python the elements of an array are indexed by their position starting at 0 for the first element. The index is written in square brackets following the array name.
A[i]
For two-dimensional arrays, the indices of an element are separated by a comma.
A[i,j]
To index elements at the end of a row or column we can use the index -1
A[-1]
The penultimate element is indexed using -2
and so on.
Define an array for the matrix $A$ below and print the values of the following elements
$$A = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix},$$
(i) $a_{11}$
A = np.array([[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]])
print(A[0,0])
A = np.array([[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]])
print(A[0,0])
1
(ii) $a_{32}$
print(A[2,1])
print(A[1,0])
4
(iii) the element in the second to last row and last column
print(A[-2,-1])
print(A[-2,-1])
6
The number of elements in a one-dimensional array can be determined using
len(A)
The number of rows and columns in an two-dimensional array can be determined using the .shape
property of the array (numpy.shape help page).
A.shape
This returns the tuple (rows, columns)
for the array A
.
The following commands defines arrays correpsonding to the matrices
$$A = \begin{pmatrix} 1 & 2 & 3 & 4 \end{pmatrix}, \qquad B = \begin{pmatrix} 1 & 3 & 5 \\ 7 & 9 & 11 \\ 13 & 15 & 17 \\ 19 & 21 & 23 \end{pmatrix},$$
and prints their sizes. Enter them into the code cell below and execute it.
A = np.array([ 1, 2, 3, 4 ])
B = np.array([[ 1, 3, 5 ],
[ 7, 9, 11 ],
[ 13, 15, 17 ],
[ 19, 21, 23 ]])
print(f"The array A has {len(A)} elements.")
print(f"The array B has {B.shape[0]} rows and {B.shape[1]} columns.")
A = np.array([ 1, 2, 3, 4 ])
B = np.array([[ 1, 3, 5 ],
[ 7, 9, 11 ],
[ 13, 15, 17 ],
[ 19, 21, 23 ]])
print(f"The array A has {len(A)} elements.")
print(f"The array B has {B.shape[0]} rows and {B.shape[1]} columns.")
The array A has 4 elements. The array B has 4 rows and 3 columns.
Array slicing allows us to return multiple elements from a NumPy array
A[start : stop : step]
If any these are unspecified Python uses default values of start=0
, stop=size of dimension
, step=1
.
Define an array for the matrix $A$ below and use array slicing to print the following
$$A = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}. $$(i) the first row of $A$
A = np.array([[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]])
print(A[0,:])
A = np.array([[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]])
print(A[0,:])
[1 2 3]
(ii) the second column of $A$
print(A[:,1])
print(A[:,1])
[2 5 8]
Note that Python returned a $1\times 3$ array instead of a $3\times 1$ array. Python always prints one-dimensional arrays as a row vector.
(iii) the elements from the second column onwards in the second row of $A$
print(A[1,1:])
print(A[1,1:])
[5 6]
(iv) the last row of $A$ in reverse order
print(A[-1,::-1])
print(A[-1,::-1])
[9 8 7]
A = np.array([[6, -2, 4, 0], [-4, 6, -1, 2], [4, -3, -5, 6]])
print(A)
[[ 6 -2 4 0] [-4 6 -1 2] [ 4 -3 -5 6]]
(a) $a_{12}$;
print(A[0,1])
-2
(b) $a_{32}$;
print(A[2,1])
-3
(c) the first row of $A$;
print(A[0,:])
[ 6 -2 4 0]
(d) the middle two elements of the second row of $A$;
print(A[1,1:3])
[ 6 -1]
(e) the even columns of $A$;
print(A[:,1::2])
[[-2 0] [ 6 2] [-3 6]]
(f) The matrix $A$ flipped upside-down (i.e., the rows of $A$ in reverse order).
print(A[-1::-1,:])
[[ 4 -3 -5 6] [-4 6 -1 2] [ 6 -2 4 0]]
The NumPy library has some commands that can be used to generate special matrices. To generate an array containing all zeros, all ones or the identity matrix we can use the following commands
np.zeros((m, n))
np.ones((m, n))
np.eye(n)
Use NumPy commands to generate the following arrays.
(i) $A = \mathbf{0}_{3\times 3}$
A = np.zeros((3, 3))
print(A)
A = np.zeros((3, 3))
print(A)
[[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]]
(ii) $B = \text{a $3 \times 2$ array of 1s}$
B = np.ones((3, 2))
print(B)
B = np.ones((3, 2))
print(B)
[[1. 1.] [1. 1.] [1. 1.]]
(iii) $I_4$ (the $4 \times 4$ identity matrix)
I = np.eye(4)
print(I)
print(np.eye(4))
[[1. 0. 0. 0.] [0. 1. 0. 0.] [0. 0. 1. 0.] [0. 0. 0. 1.]]
A one-dimensional array containing a sequence of numbers can be generated using the np.arange
command (numpy.arange help page).
np.arange(end)
This will generate an array containing integer numbers from 0
to end-1
. For other sequences we can use
np.arange(start, end, step)
This will generate an array of numbers starting at start
and finishing at end-1
where the difference between each number is step
. When step
isn't specified Python will assume a step of 1.
Use the np.arange
command to define arrays which contain the following sequences of numbers.
(i) $0, 1, 2, \ldots, 9$
print(np.arange(10))
print(np.arange(10))
[0 1 2 3 4 5 6 7 8 9]
(ii) $2, 5, 8, \ldots, 38$
print(np.arange(2, 40, 3))
print(np.arange(2, 40, 3))
[ 2 5 8 11 14 17 20 23 26 29 32 35 38]
(iii) $100, 92, 84, \ldots, 4$
print(np.arange(2, 6))
print(np.arange(100, 0, -8))
[100 92 84 76 68 60 52 44 36 28 20 12 4]
print(np.ones((10, 10)))
[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]
print(np.eye(8))
[[1. 0. 0. 0. 0. 0. 0. 0.] [0. 1. 0. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 0. 0. 0. 0.] [0. 0. 0. 1. 0. 0. 0. 0.] [0. 0. 0. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 0. 1. 0. 0.] [0. 0. 0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 0. 0. 0. 1.]]
print(np.arange(3, 33, 3))
[ 3 6 9 12 15 18 21 24 27 30]
NumPy arrays can be concantenated (merged) using the np.concatenate
command (numpy.concatenate help page)
np.concatenate((first matrix, second matrix), axis=0)
This will form a new matrix where the second matrix
is placed below the first matrix
. To form a matrix where the second matrix
is placed to the right of the first matrix
we can use
np.concatenate((first matrix, second matrix), axis=1)
The commands below define array corresponding to the matrices
$$A = \begin{pmatrix} 1 & 2 \\ 3 & 3 \end{pmatrix}, \qquad B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix},$$
and concatenates them. Enter them into the code cell below and execute it.
A = np.array([[ 1, 2 ],
[ 3, 4 ]])
B = np.array([[ 5, 6 ],
[ 7, 8 ]])
print(np.concatenate((A, B), axis=0), end="\n\n") # merge A and B with A on top of B
print(np.concatenate((A, B), axis=1)) # merge A and B side-by-side
A = np.array([[ 1, 2 ],
[ 3, 4 ]])
B = np.array([[ 5, 6 ],
[ 7, 8 ]])
print(np.concatenate((A, B), axis=0), end="\n\n") # merge A and B with A on top of B
print(np.concatenate((A, B), axis=1)) # merge A and B side-by-side
[[1 2] [3 4] [5 6] [7 8]] [[1 2 5 6] [3 4 7 8]]
The Python commands for the common operations on matrices and arrays are summarised in the table below.
Operation | Name | Python code |
---|---|---|
$A + B$ | matrix addition | A + B |
$A - B$ | matrix subtraction | A - B |
$kA$ | scalar multiplication | k * A |
$A \odot B$ | element-wise multiplication | A * B |
$AB$ | matrix multiplication | np.dot(A, B) |
$ABC$ | matrix multiplication of multiple matrices | np.linalg.multi_dot([A, B, C]) |
$A^{\circ k}$ | element-wise power | A ** k |
$A^k$ | matrix power | np.linalg.matrix_power(A, k) |
$A^T$ | matrix transpose | A.T |
$\det(A)$ | determinant of a matrix | np.linalg.det(A) |
$A^{-1}$ | inverse of a matrix | np.linalg.inv(A) |
$\sin(A)$ | $\sin$ of a matrix* | np.sin(A) |
* Other mathematical functions are treated similarly.
Define arrays for the matrices $A$ and $B$ below and print the result of the the following operations:
$$A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \qquad B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix},$$(i) $A + B$
A = np.array([[ 1, 2 ],
[ 3, 4 ]])
B = np.array([[ 5, 6 ],
[ 7, 8 ]])
print(A + B)
A = np.array([[ 1, 2 ],
[ 3, 4 ]])
B = np.array([[ 5, 6 ],
[ 7, 8 ]])
print(A + B)
[[ 6 8] [10 12]]
(ii) $3A$
print(3 * A)
print(3 * A)
[[ 3 6] [ 9 12]]
(iii) $A \odot B$ (element-wise multiplication)
print(A * B)
print(A * B)
[[ 5 12] [21 32]]
(iv) $AB$
print(np.dot(A, B))
print(np.dot(A, B))
[[19 22] [43 50]]
(v) $ABA$
print(np.linalg.multi_dot([A, B, A]))
print(np.linalg.multi_dot([A, B, A]))
[[ 85 126] [193 286]]
(vi) $A^{\circ 3}$ (element-wise power)
print(A ** 3)
print(A ** 3)
[[ 1 8] [27 64]]
(vii) $A^3$ (matrix power)
print(np.linalg.matrix_power(A, 3))
print(np.linalg.matrix_power(A, 3))
[[ 37 54] [ 81 118]]
(viii) $A^\mathrm{T}$ (transpose of a matrix)
print(A.T)
print(A.T)
[[1 3] [2 4]]
(ix) $\det(A)$ (the determinant of $A$)
print(np.linalg.det(A))
print(np.linalg.det(A))
-2.0000000000000004
(x) $A^{-1}$ (inverse of a matrix)
print(np.linalg.inv(A))
print(np.linalg.inv(A))
[[-2. 1. ] [ 1.5 -0.5]]
(xi) $\sin(A)$
print(np.sin(A))
print(np.sin(A))
[[ 0.84147098 0.90929743] [ 0.14112001 -0.7568025 ]]
A = np.array([[1, 4, -2], [0, 5, 7], [4, 1, -9]])
B = np.array([[5, 1, 8], [-4, -2, 0], [5, 11, 3]])
print(A)
print()
print(B)
[[ 1 4 -2] [ 0 5 7] [ 4 1 -9]] [[ 5 1 8] [-4 -2 0] [ 5 11 3]]
(a) $A - 3B$
print(A - 3 * B)
[[-14 1 -26] [ 12 11 7] [-11 -32 -18]]
(b) $A \odot B$
print(A * B)
[[ 5 4 -16] [ 0 -10 0] [ 20 11 -27]]
(c) $AB$
print(np.dot(A, B))
[[-21 -29 2] [ 15 67 21] [-29 -97 5]]
(d) $BA$
print(np.dot(B, A))
[[ 37 33 -75] [ -4 -26 -6] [ 17 78 40]]
(e) $ABA$
print(np.linalg.multi_dot([A, B, A]))
[[ -13 -227 -179] [ 99 416 250] [ -9 -596 -666]]
(f) $B^{\circ 4}$
print(B ** 4)
[[ 625 1 4096] [ 256 16 0] [ 625 14641 81]]
(g) $B^3$
print(np.linalg.matrix_power(B, 3))
[[ 261 583 680] [-220 -364 -192] [ 161 503 115]]
(h) $A^T$
print(A.T)
[[ 1 0 4] [ 4 5 1] [-2 7 -9]]
(i) $\det(A)$
print(np.linalg.det(A))
99.99999999999996
(j) $A^{-1}$
print(np.linalg.inv(A))
[[-0.52 0.34 0.38] [ 0.28 -0.01 -0.07] [-0.2 0.15 0.05]]
(k) $\cos(B)$
print(np.cos(B))
[[ 0.28366219 0.54030231 -0.14550003] [-0.65364362 -0.41614684 1. ] [ 0.28366219 0.0044257 -0.9899925 ]]