Skip to content

Fancy Indexing

Fancy indexing selects elements using arrays of indices instead of scalars or slices.

Mental Model

Fancy indexing lets you cherry-pick elements by passing an array of positions, like handing a librarian a list of shelf numbers. Unlike slicing, the positions need not be contiguous or in order, and the result is always a copy. The output shape matches the shape of your index array, not the original data.

1D Fancy Indexing

Select multiple elements by passing a list or array of indices.

1. List of Indices

```python import numpy as np

def main(): a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = np.array(a) print(f"{b[[0, 1, 3]] = }")

if name == "main": main() ```

Output:

b[[0, 1, 3]] = array([0, 1, 3])

2. Array of Indices

```python import numpy as np

def main(): a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = np.array(a) print(f"{b[np.array([0, 1, 3])] = }")

if name == "main": main() ```

Output:

b[np.array([0, 1, 3])] = array([0, 1, 3])

2D Fancy Indexing

Select multiple rows from a 2D array.

1. Row Selection

```python import numpy as np

def main(): a = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] b = np.array(a) print(f"{b[[0, 1, 3]] = }")

if name == "main": main() ```

Output:

b[[0, 1, 3]] = array([[0, 1, 2], [1, 2, 3], [3, 4, 5]])

2. With np.array

```python import numpy as np

def main(): a = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] b = np.array(a) print(f"{b[np.array([0, 1, 3])] = }")

if name == "main": main() ```

Output:

b[np.array([0, 1, 3])] = array([[0, 1, 2], [1, 2, 3], [3, 4, 5]])

Multi-Axis Fancy

Index both rows and columns simultaneously.

1. Paired Indices

```python import numpy as np

def main(): a = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] b = np.array(a) print(f"{b[[0, 1, 3], [0, 0, -1]] = }")

if name == "main": main() ```

Output:

b[[0, 1, 3], [0, 0, -1]] = array([0, 1, 6])

2. How It Works

Pairs (row[i], col[i]) are selected: (0,0), (1,0), (3,-1).

3. With np.array

```python import numpy as np

def main(): a = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] b = np.array(a) print(f"{b[np.array([0, 1, 3]), np.array([0, 0, -1])] = }")

if name == "main": main() ```

Output:

b[np.array([0, 1, 3]), np.array([0, 0, -1])] = array([0, 1, 6])

Boolean Masking

Select elements where a condition is True.

1. Create Mask

```python import numpy as np

arr = np.array([10, 20, 30, 40, 50]) mask = arr > 20 print(f"{mask = }") print(f"{arr[mask] = }") ```

Output:

mask = array([False, False, True, True, True]) arr[mask] = array([30, 40, 50])

2. Inline Condition

```python import numpy as np

arr = np.array([10, 20, 30, 40, 50]) print(f"{arr[arr > 20] = }") ```

Output:

arr[arr > 20] = array([30, 40, 50])

Use Cases

Fancy indexing enables expressive data selection.

1. Data Filtering

Select rows matching specific criteria from datasets.

2. Reordering

Rearrange array elements in arbitrary order.

3. Sampling

Select random subsets using random index arrays.

Fancy Indexing vs Slicing

Key Contrast

Slicing selects a contiguous range and returns a view (shared memory, no copy). Fancy indexing selects arbitrary, possibly non-contiguous positions and always returns a copy.

python a = np.arange(10) s = a[2:5] # slice → view, contiguous [2, 3, 4] f = a[[2, 7, 0]] # fancy → copy, arbitrary [2, 7, 0]

Use slicing when positions are contiguous; use fancy indexing when you need arbitrary reordering, sampling, or permutation.

Fancy Indexing vs Boolean Masking

Both return copies, but they serve different purposes:

text Fancy indexing → "give me elements at these exact positions" Boolean masking → "give me elements that satisfy this condition"

Fancy indexing Boolean masking
Input Array of indices Array of True/False
Result shape Shape of index array Number of True values
Use case Reorder, sample, permute Filter by condition
Returns Copy Copy

Exercises

Exercise 1. Given a = np.array([10, 20, 30, 40, 50]), use fancy indexing to select elements at indices [0, 3, 4]. Then use fancy indexing to create a reversed copy: select elements at indices [4, 3, 2, 1, 0].

Solution to Exercise 1
import numpy as np

a = np.array([10, 20, 30, 40, 50])
selected = a[[0, 3, 4]]
print(f"Selected: {selected}")  # [10 40 50]

reversed_a = a[[4, 3, 2, 1, 0]]
print(f"Reversed: {reversed_a}")  # [50 40 30 20 10]

Exercise 2. Create a 5x4 matrix M = np.arange(20).reshape(5, 4). Use multi-axis fancy indexing to extract the elements at positions (0, 0), (2, 1), and (4, 3) in a single operation. The result should be a 1D array of 3 elements.

Solution to Exercise 2
import numpy as np

M = np.arange(20).reshape(5, 4)
rows = np.array([0, 2, 4])
cols = np.array([0, 1, 3])
result = M[rows, cols]
print(f"Extracted: {result}")  # [ 0  9 19]

Exercise 3. Given a = np.random.randn(100), use np.random.choice to generate 10 random indices (without replacement) and use fancy indexing to sample 10 elements from a. Print the sampled values and verify the result shape is (10,).

Solution to Exercise 3
import numpy as np

np.random.seed(42)
a = np.random.randn(100)
indices = np.random.choice(100, size=10, replace=False)
sampled = a[indices]
print(f"Indices: {indices}")
print(f"Sampled: {sampled}")
print(f"Shape: {sampled.shape}")  # (10,)