NumPy - Array Strides
NumPy Array Strides
In NumPy, strides are tuples of integers representing the number of bytes to step in each dimension when traversing an array. It provide the ability to access elements in the array without explicitly copying data.
Strides are calculated based on the shape and data type of the array −
- For a 1D array with a data type of 4 bytes (e.g., int32), the stride is simply the data type size.
- For multi-dimensional arrays, strides are calculated by multiplying the size of the inner dimension by the stride of the previous dimension.
Accessing Strides in NumPy
You can access the strides of a NumPy array using the strides attribute. This attribute returns a tuple where each value represents the number of bytes to move in memory to access the next element along each dimension.
Example
In the following example, we are calculating the stride of an array using the NumPy “stride” attribute −
import numpy as np
# Creating a 2D array
array = np.array([[1, 2, 3], [4, 5, 6]])
# Accessing the strides
print("Array strides:", array.strides)
The strides (24, 8) indicate that to move from one row to the next, 24 bytes are skipped, and to move from one column to the next, 8 bytes are skipped −
Array strides: (24, 8)
How NumPy Strides Work
Strides are calculated based on the shape and data type of the array. For a given dimension, the stride is the product of the element size (in bytes) and the number of elements in the subsequent dimensions.
For a 2D array with shape (m, n) and data type dtype −
- Stride for the first dimension: stride[0] = n * size_of(dtype)
- Stride for the second dimension: stride[1] = size_of(dtype)
Example: Basic Strides
In the example below, we are accessing the strides of a basic 1D NumPy array −
import numpy as np
# Creating a 1D array
array_1d = np.array([1, 2, 3, 4, 5])
# Accessing strides
print("1D Array strides:", array_1d.strides)
The stride (8,) indicates that each element is 8 bytes apart in memory, which is typical for an array of integers −
1D Array strides: (8,)
Example: Changing Strides
Transposing the array changes the strides, reflecting the new memory layout as shown in the example below −
import numpy as np
# Creating a 2D array
array_2d = np.array([[1, 2, 3], [4, 5, 6]])
# Transposing the array
array_2d_T = array_2d.T
# Accessing strides
print("Original array strides:", array_2d.strides)
print("Transposed array strides:", array_2d_T.strides)
The stride (8,) indicates that each element is 8 bytes apart in memory, which is typical for an array of integers −
Original array strides: (24, 8)
Transposed array strides: (8, 24)
Example: Memory Optimization with Strides
Using strides can help optimize memory usage by allowing efficient access patterns −
import numpy as np
# Creating a large array
large_array = np.zeros((1000, 1000))
# Accessing every 10th row
strided_array = large_array[::10, :]
print("Strided array shape:", strided_array.shape)
print("Strided array strides:", strided_array.strides)
The strides indicate that we are skipping 80,000 bytes (10 rows) to access the next row, optimizing memory access −
Strided array shape: (100, 1000)
Strided array strides: (80000, 8)
Strides in Multi-Dimensional Arrays
Strides in multi-dimensional arrays work similarly, with each stride value indicating the step size in bytes for the corresponding dimension.
For a multi-dimensional array, the stride for each dimension is the product of the size of elements and the cumulative product of the sizes of subsequent dimensions.
This means the stride for the last dimension is simply the size of the data type, the stride for the second-to-last dimension is the size of the last dimension multiplied by the size of the data type, and so on.
Example
In the example below, we are calculating the strides of a 3 dimensional array −
import numpy as np
# Creating a 3D array
array_3d = np.zeros((2, 3, 4))
# Accessing strides
print("3D Array strides:", array_3d.strides)
The strides obtained shows the byte steps for each dimension −
3D Array strides: (96, 32, 8)
Strides for Slicing Operations
Strides are useful when it comes to performing slicing operations in NumPy arrays. When you slice a NumPy array, you often create a view rather than a copy of the array. This view shares the same underlying data but may have a different shape or memory layout.
Strides determine how many bytes to step in memory to move from one element to the next along each dimension. By adjusting strides, you can access specific patterns of data efficiently.
Slicing operations in NumPy allow you to extract subsets of an array, ranging from individual elements to specific sections, without copying the underlying data.
Example
In this example, we are creating a large 2D array and access every 10th row without copying the data using the slicing operation with strides −
import numpy as np
# Creating a large 2D array
large_array = np.arange(10000).reshape((100, 100))
# Accessing every 10th row
strided_array = large_array[::10, :]
print("Original array shape:", large_array.shape)
print("Strided array shape:", strided_array.shape)
print("Strided array strides:", strided_array.strides)
Following is the output obtained −
Original array shape: (100, 100)
Strided array shape: (10, 100)
Strided array strides: (4000, 40)