Efficient data manipulation in data science depends heavily on mastering array indexing and slicing. NumPy provides powerful capabilities that extend far beyond standard Python lists, enabling you to extract, modify, and filter data with concise syntax.
1D Array Indexing
Accessing elements in a 1D array works similarly to Python lists, with support for positive indices, negative indices, and slicing.
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
print(arr[0]) # 10
print(arr[2]) # 30
print(arr[-1]) # 50
print(arr[-2]) # 40
print(arr[1:4]) # [20 30 40]
print(arr[:3]) # [10 20 30]
print(arr[2:]) # [30 40 50]
print(arr[::2]) # [10 30 50] (every 2nd element)
print(arr[::-1]) # [50 40 30 20 10] (reversed)Output:
10
30
50
40
[20 30 40]
[10 20 30]
[30 40 50]
[10 30 50]
[50 40 30 20 10]Modifying Elements
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
arr[0] = 100
print(arr) # [100 20 30 40 50]
arr[1:3] = [200, 300]
print(arr) # [100 200 300 40 50]Output:
[100 20 30 40 50]
[100 200 300 40 50]2D Array Indexing (Matrices)
A 2D array organizes data in rows and columns. Access elements using the syntax array[row, column].
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(matrix[1, 2]) # 6
print(matrix[1][2]) # 6
print(matrix[-1, -1]) # 9Output:
6
6
9Row and Column Selection
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(matrix[0, :]) # [1 2 3]
print(matrix[0]) # [1 2 3] (shorthand)
print(matrix[:, 1]) # [2 5 8]
print(matrix[0:2, :])
print(matrix[:, 1:3])Output:
[1 2 3]
[1 2 3]
[2 5 8]
[[1 2 3]
[4 5 6]]
[[2 3]
[5 6]
[8 9]]Submatrix Extraction
import numpy as np
matrix = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
])
submatrix = matrix[1:3, 1:3]
print(submatrix)
print(matrix[::2, ::2])Output:
[[ 6 7]
[10 11]]
[[ 1 3]
[ 9 11]]3D Array Indexing (Tensors)
Think of 3D arrays as stacks of 2D matrices-like layers in a cake or frames in a video. Access elements using array[layer, row, column].
import numpy as np
tensor = np.array([
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]],
[[13, 14, 15, 16],
[17, 18, 19, 20],
[21, 22, 23, 24]]
])
print(f"Shape: {tensor.shape}") # (2, 3, 4)
print(tensor[0])
print(tensor[1, 0, 2]) # 15
print(tensor[1, 0, :]) # [13 14 15 16]
print(tensor[:, :, 0])Output:
Shape: (2, 3, 4)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
15
[13 14 15 16]
[[ 1 5 9]
[13 17 21]]Practical 3D Example: RGB Image
import numpy as np
image = np.random.randint(0, 256, size=(4, 4, 3))
print(f"Image shape: {image.shape}")
pixel = image[0, 0]
print(f"Pixel RGB: {pixel}")
red_channel = image[:, :, 0]
print(f"Red channel shape: {red_channel.shape}")
region = image[:2, :2, :]
print(f"Region shape: {region.shape}")Output:
Image shape: (4, 4, 3)
Pixel RGB: [ 35 191 34]
Red channel shape: (4, 4)
Region shape: (2, 2, 3)Boolean Indexing (Filtering)
Boolean indexing is one of NumPy's most powerful features. Apply conditions to select elements that match specific criteria.
import numpy as np
arr = np.array([10, 15, 20, 25, 30])
mask = arr > 20
print(mask) # [False False False True True]
filtered = arr[mask]
print(filtered) # [25 30]
print(arr[arr > 20]) # [25 30]Output:
[False False False True True]
[25 30]
[25 30]Multiple Conditions
import numpy as np
arr = np.array([5, 10, 15, 20, 25, 30])
result = arr[(arr >= 10) & (arr <= 25)]
print(result) # [10 15 20 25]
result = arr[(arr < 10) | (arr > 25)]
print(result) # [ 5 30]
result = arr[~(arr == 15)]
print(result) # [ 5 10 20 25 30]Output:
[10 15 20 25]
[ 5 30]
[ 5 10 20 25 30]Boolean Indexing with 2D Arrays
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(matrix[matrix > 5]) # [6 7 8 9], returns 1D array
matrix[matrix > 5] = 0
print(matrix)Output:
[6 7 8 9]
[[1 2 3]
[4 5 0]
[0 0 0]]Fancy Indexing (Index Arrays)
Use arrays of indices to access multiple specific elements:
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
indices = [0, 2, 4]
print(arr[indices]) # [10 30 50]
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
rows = [0, 1, 2]
cols = [0, 1, 2]
print(matrix[rows, cols]) # [1 5 9]Output:
[10 30 50]
[1 5 9]Views vs Copies
Understanding when NumPy creates views versus copies is crucial for avoiding bugs:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4]
view[0] = 99
print(arr) # [ 1 99 3 4 5] (original changed!)
arr = np.array([1, 2, 3, 4, 5])
copy = arr[[1, 2, 3]]
copy[0] = 99
print(arr) # [1 2 3 4 5] (original unchanged)
arr = np.array([1, 2, 3, 4, 5])
explicit_copy = arr[1:4].copy()
explicit_copy[0] = 99
print(arr) # [1 2 3 4 5] (original unchanged)Output:
[ 1 99 3 4 5]
[1 2 3 4 5]
[1 2 3 4 5]Summary
| Dimension | Syntax | Description |
|---|---|---|
| 1D | arr[i] |
Single element by index |
| 1D slice | arr[start:end:step] |
Range of elements |
| 2D | arr[row, col] |
Single element in matrix |
| 2D row | arr[row, :] |
Entire row |
| 2D column | arr[:, col] |
Entire column |
| 3D | arr[layer, row, col] |
Single element in tensor |
| Boolean | arr[arr > 5] |
Filter by condition |
| Fancy | arr[[0, 2, 4]] |
Multiple specific indices |
Mastering these indexing techniques allows you to efficiently extract, transform, and analyze data without writing explicit loops-a cornerstone of effective NumPy programming.