Basic Indexing and Slicing Concepts
Indexing selects values at specific positions, while slicing selects a range along one or more axes. A Python list slice creates a new list. Basic slicing of an ndarray creates a view sharing the original data, while reading through integer-array or boolean advanced indexing creates a copy.
Views avoid copying data, but a write through a view also changes the corresponding values in the original array.
One Dimensional Array Operations
One-dimensional arrays use familiar Python indexing syntax. A basic slice such as a[1:4] returns a view, so changing that view changes the same elements in a.
import numpy as np
# Create 1D array
a = np.linspace(0, 7, 8)
print("Original array:", a) # Output: Original array: [0. 1. 2. 3. 4. 5. 6. 7.]
# Single element indexing
print("Element at index 3:", a[3]) # Output: Element at index 3: 3.0
print("Last element:", a[-1]) # Output: Last element: 7.0
# Basic slicing
print("Slice [2:6]:", a[2:6]) # Output: Slice [2:6]: [2. 3. 4. 5.]
print("Slice [3:-2]:", a[3:-2]) # Output: Slice [3:-2]: [3. 4. 5.]
# Modification through slicing (changes original array)
a[:3] = 0
print("After a[:3] = 0:", a) # Output: After a[:3] = 0: [0. 0. 0. 3. 4. 5. 6. 7.]The next example changes a view and a copy separately. The view shares storage with the original array, while .copy() allocates independent data.
import numpy as np
# Original array
original = np.array([1, 2, 3, 4, 5])
print("Original:", original) # Output: Original: [1 2 3 4 5]
# Create view through slicing
view = original[1:4]
print("View:", view) # Output: View: [2 3 4]
# Modify view (affects original)
view[0] = 999
print("After modifying view:")
print("Original:", original) # Output: Original: [ 1 999 3 4 5]
print("View:", view) # Output: View: [999 3 4]
# Create explicit copy
copy_array = original[1:4].copy()
copy_array[0] = 777
print("After modifying copy:")
print("Original:", original) # Output: Original: [ 1 999 3 4 5]
print("Copy:", copy_array) # Output: Copy: [777 3 4]Multidimensional Array Operations
In a multidimensional selection, each comma-separated entry addresses one array dimension, starting with the first dimension. An integer index removes that dimension from the result array, while a slice such as : keeps it. For example, on a two-dimensional array a, a[1, :] produces a one-dimensional array holding the second row, while a[1:2, :] still produces a two-dimensional array holding one row.
import numpy as np
# Create 2D array
a = np.array([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.],
[10., 11., 12.]])
print("2D Array:")
print(a)
# Output:
# [[ 1. 2. 3.]
# [ 4. 5. 6.]
# [ 7. 8. 9.]
# [10. 11. 12.]]
# Specific element indexing
print("a[2,1]:", a[2,1]) # Output: a[2,1]: 8.0
# Row slicing
print("a[1,:]:", a[1,:]) # Output: a[1,:]: [4. 5. 6.]
# Column slicing
print("a[:,2]:", a[:,2]) # Output: a[:,2]: [ 3. 6. 9. 12.]Combining slices across axes selects rectangular or strided subarrays without writing a loop:
import numpy as np
# 2D array for demonstration
a = np.array([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.],
[10., 11., 12.]])
# Subarray slicing
print("a[1:3, 0:2]:")
print(a[1:3, 0:2])
# Output:
# [[4. 5.]
# [7. 8.]]
# Slicing with step
print("a[::2, :]:")
print(a[::2, :])
# Output:
# [[1. 2. 3.]
# [7. 8. 9.]]
# Column slicing with step
print("a[:, ::2]:")
print(a[:, ::2])
# Output:
# [[ 1. 3.]
# [ 4. 6.]
# [ 7. 9.]
# [10. 12.]]Two Dimensional Operations Summary
The table collects the indexing and slicing forms for a two-dimensional array in one place, together with the shape of each result.
| Operation | Description | Example Result |
|---|---|---|
a[2,1] | Element at row , column | Single value: 8.0 |
a[1,:] | Entire row | One-dimensional array: [4. 5. 6.] |
a[:,2] | Entire column | One-dimensional array: [3. 6. 9. 12.] |
a[1:3, 0:2] | Subarray rows , columns | Two-dimensional array: [[4. 5.], [7. 8.]] |
a[::2, :] | Every second row | Two-dimensional array with rows |
a[:, ::2] | Every second column | Two-dimensional array with columns |
Advanced Indexing
Advanced indexing uses integer or boolean arrays to select arbitrary elements and always returns a copy. For a one-dimensional source, an integer index array gives the result its shape. With multidimensional sources, NumPy broadcasts the index arrays and combines that shape with any dimensions left by basic slices.
import numpy as np
# Array for demonstration
a = np.array([0, 1, 2, 3, 4])
print("Original array:", a) # Output: Original array: [0 1 2 3 4]
# Indexing with list of indices
i = [1, 3, 2, 1, 4]
print("Index array:", i) # Output: Index array: [1, 3, 2, 1, 4]
print("a[i]:", a[i]) # Output: a[i]: [1 3 2 1 4]
# Indexing and reshaping with 2D index array
i = np.array([[1, 2], [3, 4]])
print("2D index array:")
print(i)
# Output:
# [[1 2]
# [3 4]]
print("a[i]:")
print(a[i])
# Output:
# [[1 2]
# [3 4]]A boolean mask selects the positions whose mask value is True. When the mask has the same shape as the source array, the selected values are returned as a one-dimensional copy.
import numpy as np
# Array for demonstration
a = np.linspace(0, 5, 6)
print("Array:", a) # Output: Array: [0. 1. 2. 3. 4. 5.]
# Create boolean mask
mask = np.array([True, False, True, False, True, False])
print("Boolean mask:", mask) # Output: Boolean mask: [ True False True False True False]
print("a[mask]:", a[mask]) # Output: a[mask]: [0. 2. 4.]
# Boolean mask from condition
condition_mask = a % 2 == 0
print("Condition mask (a % 2 == 0):", condition_mask) # Output: Condition mask (a % 2 == 0): [ True False True False True False]
print("a[condition_mask]:", a[condition_mask]) # Output: a[condition_mask]: [0. 2. 4.]
# Boolean mask with complex conditions
complex_mask = (a > 1) & (a < 4)
print("Complex mask (a > 1) & (a < 4):", complex_mask) # Output: Complex mask (a > 1) & (a < 4): [False False True True False False]
print("a[complex_mask]:", a[complex_mask]) # Output: a[complex_mask]: [2. 3.]Advanced indexes and basic slices can appear in the same selection. The advanced part still makes the result a copy:
import numpy as np
# 2D array for demonstration
a = np.arange(24).reshape(4, 6)
print("2D Array:")
print(a)
# Output:
# [[ 0 1 2 3 4 5]
# [ 6 7 8 9 10 11]
# [12 13 14 15 16 17]
# [18 19 20 21 22 23]]
# Combination of slicing and indexing
print("a[1:3, [0, 2, 5]]:")
print(a[1:3, [0, 2, 5]])
# Output:
# [[ 6 8 11]
# [12 14 17]]
# Boolean indexing on rows, slicing on columns
row_mask = np.array([True, False, True, False])
print("a[row_mask, 2:5]:")
print(a[row_mask, 2:5])
# Output:
# [[ 2 3 4]
# [14 15 16]]Advanced Techniques
The third slice field is the step. It selects regular intervals, supports negative values for reverse traversal, and works independently on each axis.
import numpy as np
# Array for demonstration
a = np.arange(20)
print("Array:", a) # Output: Array: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
# Slicing with step
print("a[::2] (every second element):", a[::2]) # Output: a[::2] (every second element): [ 0 2 4 6 8 10 12 14 16 18]
print("a[1::3] (start index 1, every third):", a[1::3]) # Output: a[1::3] (start index 1, every third): [ 1 4 7 10 13 16 19]
print("a[::-1] (reverse array):", a[::-1]) # Output: a[::-1] (reverse array): [19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0]
# Step on 2D array
b = np.arange(12).reshape(3, 4)
print("2D Array:")
print(b)
# Output:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print("b[::2, ::2] (every second row and column):")
print(b[::2, ::2])
# Output:
# [[ 0 2]
# [ 8 10]]An ellipsis (...) fills in as many full slices as needed for the unspecified dimensions. It keeps high-dimensional selections readable.
import numpy as np
# 3D array for demonstration
a = np.arange(24).reshape(2, 3, 4)
print("3D Array shape:", a.shape) # Output: 3D Array shape: (2, 3, 4)
# Using ellipsis
print("a[0, ...] (same as a[0, :, :]):")
print(a[0, ...])
# Output:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print("a[..., 2] (same as a[:, :, 2]):")
print(a[..., 2])
# Output:
# [[ 2 6 10]
# [14 18 22]]
print("a[1, ..., ::2] (same as a[1, :, ::2]):")
print(a[1, ..., ::2])
# Output:
# [[12 14]
# [16 18]
# [20 22]]Boolean Indexing
Boolean indexing evaluates a mask at each relevant position. True selects the value and False leaves it out.
import numpy as np
# Array for demonstration
a = np.linspace(0, 5, 6)
print("Array:", a) # Output: Array: [0. 1. 2. 3. 4. 5.]
# Manual boolean mask
mask = np.array([True, False, True, False, True, False], dtype=bool)
print("Manual mask:", mask) # Output: Manual mask: [ True False True False True False]
print("a[mask]:", a[mask]) # Output: a[mask]: [0. 2. 4.]
# Boolean mask from comparison
greater_than_2 = a > 2
print("a > 2:", greater_than_2) # Output: a > 2: [False False False True True True]
print("a[a > 2]:", a[a > 2]) # Output: a[a > 2]: [3. 4. 5.]
# Boolean mask with compound conditions
even_and_greater_than_1 = (a % 2 == 0) & (a > 1)
print("(a % 2 == 0) & (a > 1):", even_and_greater_than_1) # Output: (a % 2 == 0) & (a > 1): [False False True False True False]
print("a[even_and_greater_than_1]:", a[even_and_greater_than_1]) # Output: a[even_and_greater_than_1]: [2. 4.]A same-shaped boolean mask selects matching elements from a two-dimensional array and returns them in a one-dimensional result:
import numpy as np
# 2D array for demonstration
a = np.arange(12).reshape(3, 4)
print("2D Array:")
print(a)
# Output:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Boolean mask for specific elements
mask = a > 5
print("Mask a > 5:")
print(mask)
# Output:
# [[False False False False]
# [False False True True]
# [ True True True True]]
print("a[mask]:", a[mask]) # Output: a[mask]: [ 6 7 8 9 10 11]
# Boolean indexing with assignment
a[a < 5] = 0
print("After a[a < 5] = 0:")
print(a)
# Output:
# [[ 0 0 0 0]
# [ 0 5 6 7]
# [ 8 9 10 11]]Distinguish reading a selection from assignment. selected = a[mask] creates a copy, but a[a < 5] = 0 writes directly to the selected positions in a. The value 5 remains because the condition is < 5, not <= 5.
Fancy Indexing
Fancy indexing is a common name for advanced integer indexing. It can reorder positions, repeat them, or arrange the result according to an index array.
import numpy as np
# Array for demonstration
a = np.array([10, 20, 30, 40, 50, 60])
print("Array:", a) # Output: Array: [10 20 30 40 50 60]
# Fancy indexing with list of indices
indices = [0, 2, 4, 1]
print("Indices:", indices) # Output: Indices: [0, 2, 4, 1]
print("a[indices]:", a[indices]) # Output: a[indices]: [10 30 50 20]
# Fancy indexing with NumPy array
np_indices = np.array([5, 1, 3, 1, 0])
print("NumPy indices:", np_indices) # Output: NumPy indices: [5 1 3 1 0]
print("a[np_indices]:", a[np_indices]) # Output: a[np_indices]: [60 20 40 20 10]
# Fancy indexing with 2D index array
indices_2d = np.array([[0, 1], [2, 3]])
print("2D Indices:")
print(indices_2d)
# Output:
# [[0 1]
# [2 3]]
print("a[indices_2d]:")
print(a[indices_2d])
# Output:
# [[10 20]
# [30 40]]On a two-dimensional array, integer indexes can select whole rows or pair row and column coordinates element by element:
import numpy as np
# 2D array for demonstration
a = np.arange(24).reshape(4, 6)
print("2D Array:")
print(a)
# Output:
# [[ 0 1 2 3 4 5]
# [ 6 7 8 9 10 11]
# [12 13 14 15 16 17]
# [18 19 20 21 22 23]]
# Fancy indexing for specific rows
row_indices = [0, 2, 3]
print("a[row_indices, :]:")
print(a[row_indices, :])
# Output:
# [[ 0 1 2 3 4 5]
# [12 13 14 15 16 17]
# [18 19 20 21 22 23]]
# Fancy indexing for specific elements
row_idx = [0, 1, 2, 3]
col_idx = [1, 2, 3, 4]
print("a[row_idx, col_idx]:", a[row_idx, col_idx]) # Output: a[row_idx, col_idx]: [ 1 8 15 22]
# Combination of fancy indexing with slicing
print("a[[0, 2], 1:4]:")
print(a[[0, 2], 1:4])
# Output:
# [[ 1 2 3]
# [13 14 15]]Selecting and Rearranging Array Data
Use the same selection rules to filter rows, draw samples, and rearrange data without writing loops.
This example demonstrates masks and assignment. In a real analysis, the measurement context must determine the temperature thresholds and replacement rule:
import numpy as np
# Temperature sensor data simulation
temperatures = np.array([22.5, 25.1, 19.8, 30.2, 18.5, 27.3, 31.1, 24.8])
print("Temperature data:", temperatures) # Output: Temperature data: [22.5 25.1 19.8 30.2 18.5 27.3 31.1 24.8]
# Filter normal temperatures (20-28 degrees)
normal_temp_mask = (temperatures >= 20) & (temperatures <= 28)
normal_temps = temperatures[normal_temp_mask]
print("Normal temperatures:", normal_temps) # Output: Normal temperatures: [22.5 25.1 27.3 24.8]
# Filter extreme temperatures
extreme_temp_mask = (temperatures < 20) | (temperatures > 30)
extreme_temps = temperatures[extreme_temp_mask]
print("Extreme temperatures:", extreme_temps) # Output: Extreme temperatures: [19.8 30.2 18.5 31.1]
# Replace extreme values with average
mean_temp = temperatures[normal_temp_mask].mean()
temperatures_cleaned = temperatures.copy()
temperatures_cleaned[extreme_temp_mask] = mean_temp
print("Data after cleaning:", temperatures_cleaned) # Output: Data after cleaning: [22.5 25.1 24.925 24.925 24.925 27.3 24.925 24.8 ]To repeat a sample with the same generator and NumPy version, pass a fixed seed to a local generator:
import numpy as np
# Simulation dataset
data = np.arange(100).reshape(10, 10)
print("Dataset shape:", data.shape) # Output: Dataset shape: (10, 10)
# Random row sampling with a local generator
rng = np.random.default_rng(42)
sample_indices = rng.choice(10, size=5, replace=False)
sample_indices.sort()
print("Sample indices:", sample_indices) # Output: Sample indices: [0 3 4 5 7]
sampled_data = data[sample_indices, :]
print("Sampled data shape:", sampled_data.shape) # Output: Sampled data shape: (5, 10)
print("First 3 columns of sampled data:")
print(sampled_data[:, :3])
# Output:
# [[ 0 1 2]
# [30 31 32]
# [40 41 42]
# [50 51 52]
# [70 71 72]]
# Column reorganization
column_order = [9, 0, 5, 2, 7, 1, 8, 3, 6, 4]
reorganized_data = data[:, column_order]
print("Reorganized first row:", reorganized_data[0, :]) # Output: Reorganized first row: [9 0 5 2 7 1 8 3 6 4]