Topic - 2 - The Basics of NumPy Arrays 1
Topic - 2 - The Basics of NumPy Arrays 1
Each array has attributes ndim (the number of dimensions), shape (the size of each dimension),
and size (the total size of the array):
x3 ndim: 3
x3 shape: (3, 4, 5)
1
x3 size: 60
Another useful attribute is the dtype
[3]: print("dtype:", x3.dtype)
dtype: int64
Other attributes include itemsize, which lists the size (in bytes) of each array element, and nbytes,
which lists the total size (in bytes) of the array:
itemsize: 8 bytes
nbytes: 480 bytes
In general, we expect that nbytes is equal to itemsize times size.
[6]: x1[0]
[6]: 5
[7]: x1[4]
[7]: 7
To index from the end of the array, you can use negative indices:
[8]: x1[-1]
[8]: 9
[9]: x1[-2]
[9]: 7
2
[10]: array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
[11]: x2[0, 0]
[11]: 3
[12]: x2[2, 0]
[12]: 1
[13]: 7
Values can also be modified using any of the above index notation:
[14]: x2
[15]: x2[0, 0] = 12
x2
Keep in mind that, unlike Python lists, NumPy arrays have a fixed type. This means, for example,
that if you attempt to insert a floating-point value to an integer array, the value will be silently
truncated. Don’t be caught unaware by this behavior!
[16]: x1
3
• The NumPy slicing syntax follows that of the standard Python list; to access a slice of an
array x, use this:
x[start:stop:step]
• If any of these are unspecified, they default to the values start=0, stop=size of dimension,
step=1.
[18]: x = np.arange(10)
x
A potentially confusing case is when the step value is negative. In this case, the defaults for start
and stop are swapped. This becomes a convenient way to reverse an array:
[24]: x[::-1] # all elements, reversed
4
1.3.2 Multi-dimensional subarrays
Multi-dimensional slices work in the same way, with multiple slices separated by commas. For
example:
[26]: x2
Accessing array rows and columns One commonly needed routine is accessing of single rows
or columns of an array. This can be done by combining indexing and slicing, using an empty slice
marked by a single colon (:):
[12 7 1]
[12 5 2 4]
In the case of row access, the empty slice can be omitted for a more compact syntax:
[32]: print(x2[0]) # equivalent to x2[0, :]
[12 5 2 4]
5
1.3.3 Subarrays as no-copy views
One important–and extremely useful–thing to know about array slices is that they return views
rather than copies of the array data. This is one area in which NumPy array slicing differs from
Python list slicing: in lists, slices will be copies. Consider our two-dimensional array from before:
[33]: print(x2)
[[12 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
Let’s extract a 2 × 2 subarray from this:
[34]: x2_sub = x2[:2, :2]
print(x2_sub)
[[12 5]
[ 7 6]]
Now if we modify this subarray, we’ll see that the original array is changed! Observe:
[35]: x2_sub[0, 0] = 99
print(x2_sub)
[[99 5]
[ 7 6]]
[36]: print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
This default behavior is actually quite useful: - it means that when we work with large datasets,
we can access and process pieces of these datasets without the need to copy the underlying data
buffer.
[[99 5]
[ 7 6]]
If we now modify this subarray, the original array is not touched:
6
[38]: x2_sub_copy[0, 0] = 42
print(x2_sub_copy)
[[42 5]
[ 7 6]]
[39]: print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
[[1 2 3]
[4 5 6]
[7 8 9]]
Note that for this to work, the size of the initial array must match the size of the reshaped array.
Where possible, the reshape method will use a no-copy view of the initial array, but with non-
contiguous memory buffers this is not always the case.
Another common reshaping pattern is the conversion of a one-dimensional array into a two-
dimensional row or column matrix. This can be done with the reshape method, or more easily
done by making use of the newaxis keyword within a slice operation:
[41]: x = np.array([1, 2, 3])
x
7
[44]: # column vector via reshape
x.reshape((3, 1))
[44]: array([[1],
[2],
[3]])
[45]: array([[1],
[2],
[3]])
We will see this type of transformation often throughout the remainder of the book.
# concatenate x and y
# concatenate x, y and z
8
[50]: # concatenate along the second axis (zero-indexed)
np.concatenate([grid, grid], axis=1)
For working with arrays of mixed dimensions, it can be clearer to use the np.vstack (vertical stack)
and np.hstack (horizontal stack) functions:
[1 2 3] [99 99] [3 2 1]
Notice that N split-points, leads to N + 1 subarrays. The related functions np.hsplit and
np.vsplit are similar:
[54]: grid = np.arange(16).reshape((4, 4))
grid
9
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
[[0 1 2 3]
[4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]
[[ 0 1]
[ 4 5]
[ 8 9]
[12 13]]
[[ 2 3]
[ 6 7]
[10 11]
[14 15]]
10