Multi-Dimensional Indexing
In the previous module, we touched on basic slicing. However, NumPy’s true power comes from its ability to manipulate multi-dimensional arrays with surgical precision.
Comma-Separated Indexing
Unlike Python lists, where you access nested elements using list[i][j], NumPy allows you to use a single set of brackets with comma-separated indices: arr[i, j]. This is not just syntactic sugar; it is more efficient and allows for complex expressions.
Interactive Lab
import numpy as np
arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
print("Element at [1, 2]:", arr[1, 2]) # Row 1, Col 2
print("All rows, first column:", arr[:, 0])
print("First two rows, last two columns:\n", arr[:2, 1:])
Expected output
Element at [1, 2]: 60
All rows, first column: [10 40 70]
First two rows, last two columns:
[[20 30]
[50 60]]
1
2import numpy as np
3
4arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
5
6print("Element at [1, 2]:", arr[1, 2])
7print("All rows, first column:", arr[:, 0])
8print("First two rows, last two columns:\n", arr[:2, 1:])
9
Integer Array Indexing (Fancy Indexing)
Fancy indexing is a term for passing arrays of indices to access multiple array elements at once.
Interactive Lab
import numpy as np
a = np.arange(12).reshape((3, 4))
print("Array:\n", a)
# Select elements at [0,0], [1,1], [2,0]
rows = np.array([0, 1, 2])
cols = np.array([0, 1, 0])
print("\nSelected elements:", a[rows, cols])
Expected output
Array:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Selected elements: [0 5 8]
1
2import numpy as np
3
4a = np.arange(12).reshape((3, 4))
5print("Array:\n", a)
6
7
8rows = np.array([0, 1, 2])
9cols = np.array([0, 1, 0])
10print("\nSelected elements:", a[rows, cols])
11
Boolean Indexing (Masking)
This is perhaps the most useful feature for data processing. You can index an array using another array of booleans of the same shape.
Interactive Lab
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# Create a mask where elements are greater than 3
mask = arr > 3
print("Mask:", mask)
# Apply the mask
print("Filtered Array:", arr[mask])
# Complex conditions
mask_complex = (arr > 2) & (arr < 6)
print("Filtered (2 < x < 6):", arr[mask_complex])
Expected output
Mask: [False False False True True True]
Filtered Array: [4 5 6]
Filtered (2 < x < 6): [3 4 5]
1
2import numpy as np
3
4arr = np.array([1, 2, 3, 4, 5, 6])
5
6
7mask = arr > 3
8print("Mask:", mask)
9
10
11print("Filtered Array:", arr[mask])
12
13
14mask_complex = (arr > 2) & (arr < 6)
15print("Filtered (2 < x < 6):", arr[mask_complex])
16
Shape Manipulation
Frequently, data arrives in a shape that doesn’t match the input requirements of your algorithm (e.g., deep learning models often require a batch dimension).
Reshape
reshape() gives a new shape to an array without changing its data.
Interactive Lab
import numpy as np
# 1D array of 0-11
a = np.arange(12)
print("1D array:", a)
# Reshape to 3x4
a_2d = a.reshape((3, 4))
print("\n3x4 array:\n", a_2d)
# Flatten back to 1D
print("\nFlattened:", a_2d.flatten())
Expected output
1D array: [ 0 1 2 3 4 5 6 7 8 9 10 11]
3x4 array:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Flattened: [ 0 1 2 3 4 5 6 7 8 9 10 11]
1
2import numpy as np
3
4
5a = np.arange(12)
6print("1D array:", a)
7
8
9a_2d = a.reshape((3, 4))
10print("\n3x4 array:\n", a_2d)
11
12
13print("\nFlattened:", a_2d.flatten())
14
Stacking and Splitting
Combining multiple arrays into one or splitting one into many.
Interactive Lab
import numpy as np
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
# Vertical Stack
print("V-Stack:\n", np.vstack((x, y)))
# Horizontal Stack
print("H-Stack:", np.hstack((x, y)))
Expected output
V-Stack:
[[1 2 3]
[4 5 6]]
H-Stack: [1 2 3 4 5 6]
1
2import numpy as np
3
4x = np.array([1, 2, 3])
5y = np.array([4, 5, 6])
6
7
8print("V-Stack:\n", np.vstack((x, y)))
9
10
11print("H-Stack:", np.hstack((x, y)))
12
Broadcasting: A Preview
If you try to add a scalar to an array, NumPy implicitly expands the scalar to match the array’s shape. This is called broadcasting.
Interactive Lab
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("Array + 10:\n", arr + 10)
Expected output
Array + 10:
[[11 12 13]
[14 15 16]]
1
2import numpy as np
3
4arr = np.array([[1, 2, 3], [4, 5, 6]])
5print("Array + 10:\n", arr + 10)
6
In the next module, we will explore the formal rules of broadcasting and how it allows for high-performance operations without unnecessary memory replication.