Slicing
Slicing allows taking a view of subset of an array. Most slicing operations will not allocate extra memory, they will create a new view into the original buffer with a different starting address, a new shape, and a different stride.
Syntax
The API to slice an array revolves around the overloaded indexing operator ([]).
Single Axis
For an array with a single axis, it behaves exactly like a normal python list. We can
- Get a single scalar by specifying its index
arr[2] - Use negative indexing to index from right to left
arr[-1] - Use a slice object to get multiple indices (from start to end with the last index excluded.)
arr[3:7]orslice(3, 7). - Add a step to the slice object to how many indices to skip between two elements.
arr[3:7:2]will get elements at indices3and5.arr[7:3:-1]is the reversed version ofarr[3:7].
Performance Note: When you use a step (e.g., ::2), NumPy simply doubles the stride in the metadata. The memory is untouched.
import numpy as np
arr = np.arange(10)
print(f'{arr[2]=}')
print(f'{arr[-1]=}')
print(f'{arr[3:7]=}')
print(f'{arr[slice(3, 7)]=}')
print(f'{arr[3:7:2]=}')
print(f'{arr[7:3:-1]=}')
stdout
arr[2]=np.int64(2)
arr[-1]=np.int64(9)
arr[3:7]=array([3, 4, 5, 6])
arr[slice(3, 7)]=array([3, 4, 5, 6])
arr[3:7:2]=array([3, 5])
arr[7:3:-1]=array([7, 6, 5, 4])
Out of Bound
- Out of bound access to a scalar is illegal,
np.arange(10)[100]raises anIndexError. - But out of bound slicing is fine
np.arange(10)[100:1]will just return an empty array (shape = (0,)).
Multiple Axes
Arrays with multiple axes can be sliced using the same mechanism:
import numpy as np
arr = np.arange(10).reshape(5, 2)
print(f'{arr=}')
print(f'{arr[2]=}')
print(f'{arr[1:5]=}')
print(f'{arr[1:5:2]=}')
stdout
arr=array([[0, 1],
[2, 3],
[4, 5],
[6, 7],
[8, 9]])
arr[2]=array([4, 5])
arr[1:5]=array([[2, 3],
[4, 5],
[6, 7],
[8, 9]])
arr[1:5:2]=array([[2, 3],
[6, 7]])
We can also slice multiple axes at once by separating them with a coma ,:
import numpy as np
arr = np.arange(12).reshape(4, 3)
print(f'{arr=}')
print(f'{arr[2, 1]=}')
print(f'{arr[2, 1:3]=}')
print(f'{arr[2:4, 1:3]=}')
stdout
arr=array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
arr[2, 1]=np.int64(7)
arr[2, 1:3]=array([7, 8])
arr[2:4, 1:3]=array([[ 7, 8],
[10, 11]])
We can slice a full axis by inserting : in its position. If we provide less indices than we have axes, NumPy will automatically append : to the missing axes as we have seen earlier. If we just want to slice the last indices and take a full view of the first ones, we can use the ... syntax (ellipsis.)
import numpy as np
arr = np.arange(12).reshape(2, 3, 2)
print(f'{arr=}')
print(f'{arr[:, -1, :]=}')
print(f'{arr[:, -1]=}')
print(f'{arr[..., -1]=}')
print(f'{arr[..., -1, -1]=}')
stdout
arr=array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
arr[:, -1, :]=array([[ 4, 5],
[10, 11]])
arr[:, -1]=array([[ 4, 5],
[10, 11]])
arr[..., -1]=array([[ 1, 3, 5],
[ 7, 9, 11]])
arr[..., -1, -1]=array([ 5, 11])
Mutation
Since a slice is just a window into the same memory, modifying the slice modifies the original array.
import numpy as np
original = np.zeros(5)
slice_view = original[0:2]
# Modify the slice
slice_view[:] = 100
print(f'{original=}') # Original is changed!
stdout
original=array([100., 100., 0., 0., 0.])
If you need to modify a slice without affecting the original, you must explicitly call .copy().