Max Bartolo

3 minute read

A matrix is a $2$-dimensional (2D) array of numbers. This means that every element in the matrix is identified by two indices, commonly $i$ representing the row-index and $j$ representing the column-index.

Matrices are usually given uppercase variable names in bold, such as $\mathbf{A}$.

$\mathbf{A} = \begin{bmatrix} A_{1,1} & A_{1,2} \\ A_{2,1} & A_{2,2} \end{bmatrix}$

We use a colon “:” to represent all the elements across an axis. So, $\mathbf{A}_{i,:}$ identifies all the elements in the $i$th row and $\mathbf{A}_{:,j}$ identifies all the elements in the $j$th column. If a real-valued matrix (i.e. its elements are all real numbers) has $m$ rows and $n$ columns, we show the matrix dimensionality as $\mathbf{A} \in \mathbb{R}^{m \times n}$.

In Python, we can represent a matrix as a list of lists, however, it is more convenient to represent matrices and higher-order tensors in packages designed to handle them such as NumPy, TensorFlow or PyTorch.

A = np.array([[7, 4, 3, 2], [0, 8, 9, 5]])

# Python is zero-indexed so A[1,:] will give the second row in the matrix
print("First element of matrix A is {}".format(A[0,0]))
print("Second row of matrix A is {}".format(A[1,:]))
print("A is of type {}".format(type(A)))
print("A has shape {}".format(A.shape))  # 2 rows, 4 columns
First element of matrix A is 7
Second row of matrix A is [0 8 9 5]
A is of type <class 'numpy.ndarray'>
A has shape (2, 4)

NumPy allows us to do various matrix operations such as element-wise multiplication, matrix multiplication, dimensionality manipulation, broadcasting and more. Some examples can be found below.

# Create a 2x2 identitity matrix
B = np.identity(2)
print(B)
[[1. 0.]
 [0. 1.]]
# Create a matrix of ones
B = np.ones(shape=(2, 4))
print(B)
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]]
# Element-wise multiplication by a scalar
B = 3*B
print(B)
[[3. 3. 3. 3.]
 [3. 3. 3. 3.]]
# Multiplication of a vector and a matrix through broadcasting
x = np.array([7, 4, 3, 2])
B = x*B
print(B)
[[21. 12.  9.  6.]
 [21. 12.  9.  6.]]
# Element-wise multiplication between two matrices of the same shape
C = A*B
print(C)
[[147.  48.  27.  12.]
 [  0.  96.  81.  30.]]
# Matrix multiplication between two matrices
C = A.dot(B.T)  # We transpose B to get a multiplication between matrices of shapes (2, 4) x (4, 2) = (2, 2)
print(C)
[[234. 234.]
 [207. 207.]]
# Modify a matrix element in-place
C[1,1] -= 5
print(C)
[[234. 234.]
 [207. 202.]]
# We can also invert a square, non-singular matrix
C_inv = np.linalg.inv(C)
print(C_inv)
[[-0.17264957  0.2       ]
 [ 0.17692308 -0.2       ]]
# Another way to do matrix inversion would be through the NumPy matrix object instead of using arrays
D = np.matrix([[234, 234], [207, 202]])
D_inv = D.I
print(D_inv)
[[-0.17264957  0.2       ]
 [ 0.17692308 -0.2       ]]
# The multiplication of a matrix with its inverse should therefore give
# the Identity matrix (subject to floating-point inaccuracies)
E = D.dot(D_inv)
assert np.array_equal(E, np.identity(E.shape[0])), "E is not equal to the identity matrix of the same shape!"
print(E)
[[1. 0.]
 [0. 1.]]
# Expand a matrix dimensions
print("Original matrix shape is", E.shape)
E = np.expand_dims(E, axis=1)
print("Matrix shape after expanding dimensions on axis 1 is", E.shape)
Original matrix shape is (2, 2)
Matrix shape after expanding dimensions on axis 1 is (2, 1, 2)

For more examples, see https://scipy-lectures.org/intro/numpy/operations.html

Source: https://github.com/maxbartolo/ml-index/blob/master/01-basic-math/01-linear-algebra/03-matrices.ipynb

comments powered by Disqus