Section 3: The numpy
Library (part 2)
Table of Contents >
Chapter 7 > Section 3
In this section we consider multi-dimensional numpy
arrays. While arrays with dimension greater than 3 are possible, we
will restrict our attention to arrays of no more than 3 dimensions.
That said, the concepts that we will learn extend naturally to higher
dimensional arrays.
Two-dimensional Arrays
A 2-dimensional array is an array of arrays. It can be used to
represent information that is organized into a table of rows and columns:
We can use a Python list-of-lists to initialize a numpy
2D
array. We can then provide a row index to access the array that
represents a particular row of information or we can provide a row and
column index to access the element in a particular row and column:
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6]])
>>> z[0] # the first element of z is a 1D
array
array([1, 2, 3])
>>> z[1] # the second element of z is
also a 1D array
array([4, 5, 6])
>>> z[0, 0] # the element at row 0, column 0
1
>>> z[1, 2] # the element at row 1, column 2
6
To retrieve a particular column or some other sub-array, we can use slicing
operations. Recall that such operations provide a view onto
the array:
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> z[:,0] # slice of
elements in all rows of column 0
array([1, 4, 7])
>>> z[0:2,1:] = 0 # slice of elements lying in rows 0 up to
# but not including 2 and from column 1 to
# the last column
array([[1, 0, 0],
[4, 0, 0],
[7, 8, 9]])
Fancy indexing operations can also be applied to 2D arrays. To specify
the row and column index of entries in the view, we create a tuple with the
row indexes as the first component and corresponding column indexes as the
second:
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> zi = (np.array([0, 1, 2]), np.array([2, 1, 0]))
>>> z[zi]
array([3, 5, 7])
>>> z[zi] = 0
>>> z
array([[1, 2, 0],
[4, 0, 6],
[0, 8, 9]])
In the example above, zi
is a tuple that represents the
following indexes into the array: [0, 2], [1, 1] and [2, 0] - these are the
entries lying on the diagonal from top-right to bottom-left. Note that
the statement z[zi] = 0
assigns the value 0 to each of these
entries.
You can also use a 2D array of Booleans as a fancy index into a 2D
array. As for a 1D array, we obtain a view onto an array that consists
of only those values that correspond to True
in the Boolean
array:
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6], [7,8,9]])
>>> zb = np.array([[False, False, True],
...
[False, True, False],
...
[True, False, False]])
>>> z[zb] = 0
>>> z
array([[1, 2, 0],
[4, 0, 6],
[0, 8, 9]])
The ndarray
class
The ndarray class has the following attributes that allow us to determine
the number of dimensions, size and shape of an array
ndim
- the number of
dimensions
shape
- the shape of the array (the size of each dimension)
as a tuple
size
- the total number of elements in the array
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6]])
>>> z.ndim
2
# z is a 2D array
>>> z.shape
(2, 3) # z has 2 rows and
3 columns
>>> z.size
6
# z has a total of 6 elements
The class also has a large number of methods for performing operations on an
ndarray
. We mention just a few of them here:
reshape
- produces a view
whose shape is as specified by the parameters (reshaped array must have
same size as original)
ravel
- produces a view as a 1D array
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6]])
>>> z.reshape(3, 2) # a
reshaped view onto z
array([[1, 2],
[3, 4],
[5, 6]])
>>>
z.ravel()
# a 1D view onto z
array([1, 2, 3, 4, 5, 6])
>>>
z
# the shape of z is still (2, 3)
array([[1, 2, 3],
[4, 5, 6]])
Operations along axes
An n-dimensional array has n-axes labelled 0 through n-1 – one for
each of the dimensions of the array.
Many of the ndarray
methods and numpy
functions
that operate on arrays consume an optional axis argument. If
this argument is not provided, the operation is performed across all
elements of the array, regardless of the shape of the array. If the axis
argument is provided, the calculation is performed over each 1-dimensional
sub-array that lies parallel to the given axis.
Axis 0 is the axis corresponding to the first index into the array. As
we increase the first index, leaving the other indexes fixed, we move
through the array in the direction of axis 0. Similarly, as we
increase the second index, leaving the other indexes fixed, we move through
the array in the direction of axis 1, etc.
The following figure depicts the alignment of axes for the array:
np.array([[1, 2, 3], [4, 5, 6]])
:
We can now perform computations along 1D arrays that lie parallel to a
specified axis (reference the diagram above to verify the values produced by
each of the following function calls):
>>> import numpy as np
>>> z = np.array([[1, 2, 3], [4, 5, 6]]) # array pictured
above
>>> np.max(z, axis=0)
array([4, 5, 6]) # maximum along
each 1D array
# parallel to axis 0
>>> np.max(z, axis=1)
array([3, 6])
# maximum along each 1D array
# parallel to axis 1
Similarly, the following figure depicts the alignment of axes for the array:
np.array([[[ 0, 1,
2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]],
[[12, 13, 14,
15], [16, 17, 18, 19], [20, 21, 22, 23]]])
Just as for 2D arrays, we can perform computations along 1D arrays aligned
with a specified axis (again, reference the diagram above to verify the
value produced by each of the following function calls):
>>> z = np.arange(24).reshape(2,3,4)
# array pictured above
>>> np.max(z, axis=0)
array([[12, 13, 14,
15],
# maximum along each
[16, 17, 18,
19],
# 1D array parallel
[20, 21, 22,
23]])
# to axis 0
>>> np.max(z,
axis=1)
# maximum along each
array([[ 8, 9, 10,
11],
# 1D array parallel
[20, 21, 22,
23]])
# to axis 1
>>> np.max(z,
axis=2)
# maximum along each
array([[ 3, 7,
11],
# 1D array parallel
[15, 19,
23]])
# to axis 2