Photo by Vlado Paunovic
NumPy is a powerful Python library that contains a large collection of mathematical functions, and supports the creation of matrices and multi-dimensional arrays to which these mathematical functions can be applied.
In this short tutorial, you will learn how to perform several of the most basic matrix operations with NumPy.
Matrices and Arrays in NumPy
In NumPy, a matrix is defined as a specialised array that is strictly 2-dimensional, and which retains its 2-dimensionality following the application of mathematical operations. A matrix of this type can be implemented using the np.matrix
class, however NumPy no longer recommends use of this class as it may be removed in the future. The alternative option that is recommended by NumPy is to use the N-dimensional array type, ndarray
.
The key difference between an ndarray and a matrix in NumPy is that the former can be of any dimensionality, and its use is not restricted to 2-dimensional operations.
Hence, in this tutorial we shall be focusing on implementing several basic matrix operations on 2-dimensional arrays, created using np.ndarray
Creating NumPy Arrays
Let’s first import the NumPy package and then proceed to create two, 2-dimensional arrays that are composed of two rows and three columns each. These arrays will be used in the ensuing examples of this tutorial:
# Import NumPy package
import numpy as np
# Create arrays
a1 = np.array([[0, 1, 0], [2, 3, 2]])
a2 = np.array([[3, 4, 3], [5, 6, 5]])
The shape
attribute lets us confirm the array’s dimensions:
# Print one of the arrays
print('Array 1:', '\n', a1, '\n Shape: \n’, a1.shape)
Output:
Array 1:
[[0 1 0]
[2 3 2]]
Shape: (2, 3)
Basic Array Operations
NumPy provides its own functions to perform element-wise addition, subtraction, division and multiplication of arrays. In addition, Numpy also leverages Python’s arithmetic operators by extending their functionality to handle element-wise array operations.
Let’s start with element-wise addition between the arrays a1
and a2
as an example.
Element-wise addition of two arrays can be achieved by making use of the np.add
function or the overloaded +
operator:
# Using np.add
func_add = np.add(a1, a2)
# Using the + operator
op_add = a1 + a2
By printing out the results, it may be confirmed that they both produce the same output:
# Print results
print('Function: \n', func_add, '\n\n', 'Operator: \n', op_add)
Output:
Function:
[[3 5 3]
[7 9 7]]
Operator:
[[3 5 3]
[7 9 7]]
However, if we had to time them, we can notice a small difference:
import numpy as np
import timeit
def func():
a1 = np.array([[0, 1, 0], [2, 3, 2]])
a2 = np.array([[3, 4, 3], [5, 6, 5]])
np.add(a1, a2)
def op():
a1 = np.array([[0, 1, 0], [2, 3, 2]])
a2 = np.array([[3, 4, 3], [5, 6, 5]])
a1 + a2
# Timing the functions over 100000 iterations
func_time = timeit.timeit(func, number=100000)
op_time = timeit.timeit(op, number=100000)
# Print timing results
print('Function:', func_time, '\n', 'Operator:', op_time)
Output:
Function: 0.2588757239282131
Operator: 0.24321464297827333
Here it may be seen that the NumPy np.add
function performs slightly slower than the +
operator. This is mainly because the add function introduces type-checking to convert any array_like inputs (such as lists) into arrays before performing the addition operation. This, in turn, introduces an extra computational overhead over the +
operator.
However, such measure also makes the np.add
function less prone to error. For instance, applying np.add
to inputs of type list
still works (e.g. np.add([1, 1], [2, 2])
), whereas applying the +
operator results in list concatenation.
Similarly for element-wise subtraction (using np.subtract
or -
), division (using np.divide
or /
) and multiplication (using np.multiply
or *
), the NumPy functions perform type-checking, introducing a small computational overhead.
Several other operations that may come in handy include transposing and multiplying arrays.
Matrix transposition results in an orthogonal rotation of the matrix, and can be achieved using the np.transpose
function (which includes type-checking) or the .T
attribute:
# Using np.transpose
func_a1_T = np.transpose(a1)
# Using the .T attribute
att_a1_T = a1.T
Matrix multiplication can be performed using the np.dot
function or the @
operator (the latter implements the np.matmul
function from Python 3.5 onwards):
# Using np.dot
func_dot = np.dot(func_a1_T, a2)
# Using the @ operator
op_dot = func_a1_T @ a2
When working with 2-dimensional arrays, np.dot
and np.matmul
perform identically and both include type-checking.
Additional Resources
Stefania Cristina, PhD, is a Senior Lecturer with the Department of Systems and Control Engineering at the University of Malta. Her research interests lie within the domains of computer vision and machine learning.