ch8s3_IndexingAndSlicing
Indexing and slicing are essential tools for **accessing, selecting, and modifying** specific elements in NumPy arrays.
Chapter 8: Introduction to NumPy
Sub-Chapter: Indexing and Slicing โ Accessing and Manipulating Array Data
Indexing and slicing are essential tools for accessing, selecting, and modifying specific elements in NumPy arrays.
They enable efficient subsetting, filtering, and reshaping of data โ all while taking advantage of NumPyโs vectorized and memory-efficient design.
๐งฉ 1. Indexing in NumPy vs. Python Lists
While Python lists require nested loops for multi-dimensional access, NumPy arrays support multi-axis indexing directly and return views instead of copies (in most cases).
import numpy as np
matrix = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
print(matrix[0, 1]) # Access row 0, column 1 โ 20
print(matrix[2]) # Access entire third row
๐ง In NumPy, slicing often returns a view, not a copy โ meaning modifying the slice affects the original array.
๐ข 2. Basic Indexing โ Single and Multi-Dimensional
1D Arrays
arr = np.array([10, 20, 30, 40, 50])
print(arr[0]) # 10
print(arr[-1]) # 50 (negative index)
2D Arrays
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(matrix[1, 2]) # Element at row 1, column 2 โ 6
print(matrix[2]) # Entire 3rd row โ [7 8 9]
print(matrix[:, 0]) # Entire first column โ [1 4 7]
3D Arrays
tensor = np.arange(27).reshape(3, 3, 3)
print(tensor[1, 2, 0]) # Access single element
โ๏ธ 3. Slicing โ Extracting Subsets
Slicing syntax: [start : stop : step]
arr = np.array([10, 20, 30, 40, 50, 60])
print(arr[1:4]) # [20 30 40]
print(arr[:3]) # [10 20 30]
print(arr[::2]) # [10 30 50]
print(arr[::-1]) # Reverse โ [60 50 40 30 20 10]
2D Slicing
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
sub_matrix = matrix[:2, 1:]
print(sub_matrix)
# [[2 3]
# [5 6]]
Visual Diagram
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
โโโ
|โโ Columns 1 to 2
โโโ Rows 0 to 1
โก Slicing creates a view, not a copy. To get a copy, use
.copy()explicitly.
๐ญ 4. Conditional (Boolean) Indexing
You can use logical expressions to create boolean masks that filter elements.
data = np.array([10, 20, 30, 40, 50])
mask = data > 25
filtered = data[mask]
print(filtered) # [30 40 50]
Combining Conditions
arr = np.array([5, 15, 25, 35, 45, 55])
subset = arr[(arr > 20) & (arr < 50)]
print(subset) # [25 35 45]
Use
&for AND,|for OR, and parentheses around each condition.
Conditional Modification
arr[arr > 40] = 999
print(arr) # [ 5 15 25 35 999 999]
๐ฏ 5. Fancy Indexing โ Using Arrays of Indices
Fancy indexing lets you use integer arrays or lists to select arbitrary elements or rows.
arr = np.array([10, 20, 30, 40, 50])
indices = [0, 2, 4]
print(arr[indices]) # [10 30 50]
2D Fancy Indexing
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
rows = np.array([0, 2])
cols = np.array([1, 2])
print(matrix[rows, cols]) # [2 9]
Selecting Rows and Columns Together
selected_rows = matrix[[0, 2]] # Select 1st and 3rd rows
selected_cols = matrix[:, [0, 2]] # Select 1st and 3rd columns
๐ง 6. Mixing Fancy Indexing and Slicing
You can combine fancy indexing with slicing โ though it creates a copy, not a view.
matrix = np.arange(12).reshape(3, 4)
print(matrix[[0, 2], 1:3])
# [[1 2]
# [9 10]]
๐ 7. Using Helper Functions for Indexing
NumPy provides several powerful utilities for complex indexing.
np.where() โ Conditional Extraction
arr = np.array([10, 20, 30, 40, 50])
indices = np.where(arr > 25)
print(indices) # (array([2, 3, 4]),)
print(arr[indices]) # [30 40 50]
np.take() โ Extract by Index Along Axis
matrix = np.arange(9).reshape(3, 3)
print(np.take(matrix, [0, 4, 8])) # [0 4 8]
np.ix_() โ Cartesian Indexing
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
rows = np.array([0, 2])
cols = np.array([1, 2])
print(A[np.ix_(rows, cols)])
# [[2 3]
# [8 9]]
๐งฎ 8. Practical Examples
Extracting Odd Numbers
arr = np.arange(1, 11)
odds = arr[arr % 2 == 1]
print(odds) # [1 3 5 7 9]
Normalizing Selected Columns
data = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
cols = [0, 2]
data[:, cols] = (data[:, cols] - data[:, cols].mean()) / data[:, cols].std()
Modifying Elements Based on Condition
arr = np.array([10, 20, 30, 40, 50])
arr[arr < 30] = 0
print(arr) # [ 0 0 30 40 50]
โ๏ธ 9. Views vs Copies โ Important Concept
| Operation | Returns | Modifies Original? |
|---|---|---|
Basic slice (arr[1:3]) | View | โ Yes |
Fancy indexing (arr[[1,3]]) | Copy | โ No |
Boolean mask (arr[arr > 5]) | Copy | โ No |
Always use
.copy()when you need to preserve the original data.
๐งพ 10. Quick Reference Table
| Type | Description | Example |
|---|---|---|
| Basic Indexing | Access element(s) | arr[1, 2] |
| Slicing | Extract range | arr[1:4], arr[:, 1:] |
| Negative Indexing | From end | arr[-1] |
| Boolean Masking | Filter by condition | arr[arr > 10] |
| Fancy Indexing | Use index arrays | arr[[0,2,4]] |
| Helper Functions | Complex indexing | np.where, np.take, np.ix_ |
๐งญ 11. Best Practices
โ
Prefer slicing for large subsets (efficient, returns view).
โ
Use .copy() when modifying slices independently.
โ
Combine boolean masks with broadcasting for expressive filters.
โ
Avoid mixing fancy and boolean indexing in the same call (hard to debug).
โ
Use np.where() for conditional selection or replacement.
๐ง Summary
| Concept | Description | Example |
|---|---|---|
| Indexing | Accessing single/multiple elements | matrix[1,2] |
| Slicing | Extracting views | matrix[:2, 1:] |
| Boolean Masking | Conditional filtering | arr[arr>5] |
| Fancy Indexing | Arbitrary selection | matrix[[0,2], [1,2]] |
| np.where | Conditional positions | np.where(arr>10) |
Mastering indexing and slicing unlocks true NumPy power โ enabling expressive, fast, and memory-efficient data operations.