The numpy
package¶
What is the numpy
package ?¶
numpy
is a python package designed for numerical computations.
It provdes the main object called ndarray
, which stands for $n$-dimensional arrays.
This ndarray
s play the role of matrices in linear algebra.
Importing the package¶
To use numpy
, we must import it into the kernel.
It is common to give it a nicknake np
.
# Import numpy into the current kernel and nickname it as np.
import numpy as np
Creating an array¶
In python
, if you want to use a function func
from a package pack
, we call it by pack.func
.
Similarly, to call the function array
from numpy
(now nicknamed np
), we call it with np.array
.
This function np.array
changes a list
with appropriate structure into an ndarray
.
l = [1, 2, 3, 4]
print(l)
print(type(l))
[1, 2, 3, 4] <class 'list'>
a = np.array(l)
print(a)
print(type(a))
[1 2 3 4] <class 'numpy.ndarray'>
A = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(A)
print(type(A))
[[1 2 3 4] [5 6 7 8]] <class 'numpy.ndarray'>
Array shape¶
The shape of a ndarray
is a tuple in which each element is the length of the corresponding dimension.
To check the shape of an array a
, we can use either np.shape(a)
or an array's method a.shape
.
In the next blocks, we check the dimension of the arrays a
and A
.
The first np.shape(a)
should return (4,)
, which means that a
is a one-dimensional array (only one number output) and there are 4 items in this dimension.
The second np.shape(A)
should return (2,4)
.
This means that A
is a two-dimensional array (two numbers are returned), where there are 2 rows and 4 columns.
np.shape(a)
(4,)
A.shape
(2, 4)
One may also create a higher-dimensional array, but this would be out of our scopes.
A caution with the shapes (n,)
, (1,n)
and (n,1)
¶
In mathematics, we do not distinguish between vectors in $\mathbb{R}^{n}$ and a matrices in $\mathbb{R}^{n \times 1}$.
However, in python
's context, an array of shapes (n,)
, (1,n)
and (n,1)
are considered different due to the memory usage.
In particular, a (n,)
array is the cheapest in terms of memory.
A (1,n)
array is considered as a matrix of 1 row and n columns, and a (n,1)
array is considered as a matrix of n rows and 1 column.
The differences of these arrays will become clearer when we later do computation with them.
a = np.array([1, 2, 3, 4])
a_row = np.array([[1, 2, 3, 4]])
a_col = np.array([[1], [2], [3], [4]])
print(a)
np.shape(a)
[1 2 3 4]
(4,)
print(a_row)
np.shape(a_row)
[[1 2 3 4]]
(1, 4)
print(a_col)
np.shape(a_col)
[[1] [2] [3] [4]]
(4, 1)
Accessing entries¶
We access entries of a ndarray
in a similar way we would do with a list
.
A = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(A)
[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]
A[1]
array([5, 6, 7, 8])
We can use all the slicing methods we learned with a list
with a ndarray
.
A[0:2] # This calls A[0] and A[1].
array([[1, 2, 3, 4], [5, 6, 7, 8]])
We access entries of a higher-dimensional array using a tuple of indices. Slicing in each dimension is also possible.
A[0, 3]
np.int64(4)
# Take rows 2--3 and columns 1--2.
A[1:3, :2]
array([[ 5, 6], [ 9, 10]])
# Take all rows of columns 2--3.
A[:, 1:3]
array([[ 2, 3], [ 6, 7], [10, 11]])
One selective method that works with a ndarray
but not a list
is to explicitly pick the items along a list of indices.
# Take every columns of rows 1 and 3.
A[[0, 2], :]
array([[ 1, 2, 3, 4], [ 9, 10, 11, 12]])
Transposition¶
We may use the method T
of an array A
to transpose itself, that is A.T
.
If one prefers a clearer procedure, np.transpose
can also be used.
A = np.array([[1, 2, 3], [4, 5, 6]])
A_t = A.T
print(f"A =\n{A}")
print(f"A_t =\n{A_t}")
A = [[1 2 3] [4 5 6]] A_t = [[1 4] [2 5] [3 6]]
B = np.array([[1, 1], [2, 2], [3, 3]])
B_t = np.transpose(B)
print(f"B =\n{B}")
print(f"B_t =\n{B_t}")
B = [[1 1] [2 2] [3 3]] B_t = [[1 2 3] [1 2 3]]
Addition and subtraction of arrays¶
Unlike a list
, the objects of type ndarray
allow us to do the real computation.
The operators +
and -
are used for adding and subtracting two arrays.
Arrays with the same shapes¶
As one have always do in mathematics, addition and subtraction requires both matrices to have the same dimension.
A = np.array([[1, 2, 1], [3, 1, 4]])
B = np.array([[0, 1, 2], [-1, 3, 1]])
A + B
array([[1, 3, 3], [2, 4, 5]])
A - B
array([[ 1, 1, -1], [ 4, -2, 3]])
Broadcasting of arrays with different shapes¶
Broadcasting is a coding mechanism that allows operations to be performed on arrays with different shapes. The broadcasting is usually not mathematically correct, but it is a programming trick designed to simplify the code and is actually a key to achieve a clean and fast code.
Requirements¶
To add or subtract two arrays of different shapes, a + A
or a - A
, the two arrays must satisfy one of the following cases:
- The shapes of
a
is(m,)
and ofA
is(n,m)
:a+A
addsa
to every rows ofA
. - The shapes of
a
is(1,m)
and ofA
is(n,m)
:a+A
addsa
to every rows ofA
. - The shapes of
a
is(n,1)
and ofA
is(n,m)
:a+A
addsa
to every columns ofA
.
A = np.array([[1, 2, 1], [3, 1, 4]])
a = np.array([2, 1, 1])
a_row = np.array([[2, 1, 1]])
a_col = np.array([[3], [1]])
a + A
array([[3, 3, 2], [5, 2, 5]])
a_row + A
array([[3, 3, 2], [5, 2, 5]])
a_col + A
array([[4, 5, 4], [4, 2, 5]])
Componentwise multiplication¶
The operator *
is used when one wants to multiply a scalar (a number) to a ndarray
.
Note that this is not matrix multiplication.
c = 3
A = np.array([[1, 0, -2], [1, 4, 3]])
c * A
array([[ 3, 0, -6], [ 3, 12, 9]])
Broadcasting with *
¶
The operator *
could also be used on arrays of different shapes in the same way with +
and -
.
Let us demonstrate below.
a * A
array([[ 2, 0, -2], [ 2, 4, 3]])
a_row * A
array([[ 2, 0, -2], [ 2, 4, 3]])
a_col * A
array([[ 3, 0, -6], [ 1, 4, 3]])
Matrix multiplication¶
Here, we are talking about the conventional matrix multiplication.
The operator to use is @
.
A = np.array([[2, 2, 3, 0], [1, 0, 1, 2]])
B = np.array([[1, 1], [2, 2], [3, 0], [-2, 1]])
A @ B
array([[15, 6], [ 0, 3]])
There are also functions like np.matmul
and np.dot
that also do the job of matrix multiplication.
Nevertheless, it is not recommended to use np.dot
to do matrix multiplication.
On the other hand, np.matmul
implements the semantic of @
and also does complicate broadcasting.
These aspects are, however, outside of the scopes of this introductory course.