NumPy: Indexing and Slicing
Last updated: 2026-08-26
1. What You'll Learn
- ❶ 1D indexing and slicing: subscript access and range extraction from 1D arrays
- ❷ 2D indexing: extracting rows, columns, and sub-matrices
- ❸ 3D indexing intuition: thinking in layers for higher-dimensional arrays
- ❹ Step slicing: positive steps, negative steps, and negative indices
- ❺ Slices are views: understanding the essential difference between views and copies
2. Story
Charlie is analyzing a 10K-row sensor data matrix. He uses data[0:3] to grab the first 3 rows for outlier cleaning, and sets missing values to -1:
subset = data[0:3]
subset[subset == -999] = -1
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
After cleaning, subset looks great. But when he checks the original data, the first 3 rows have been modified too!
"Slices return views, not copies!" Alice explains.
She shows Charlie how to use subset = data[0:3].copy() for safe operations that don't affect the original. Charlie learns the hard way: NumPy slices share memory by default — this is the source of its efficiency and the root of its traps.
3. Key Concepts
(1) Basic Indexing
1D array indexing works like Python lists: starts at 0, negative indices count from -1.
import numpy as np
a = np.array([10, 20, 30, 40, 50])
a[0] # 10
a[-1] # 50
a[2] # 30
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(2) Slice Syntax
Slice format: start:stop:step. All three parts are optional:
| Short form | Equivalent | Meaning |
|---|---|---|
a[:] |
a[0:len(a):1] |
All elements |
a[2:] |
a[2:len(a):1] |
From index 2 to end |
a[:3] |
a[0:3:1] |
First 3 elements |
a[::2] |
a[0:len(a):2] |
Every other element |
a[::-1] |
a[len(a)-1::-1] |
Reverse the array |
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
a[2:7] # [2 3 4 5 6]
a[::3] # [0 3 6 9]
a[::-1] # [9 8 7 6 5 4 3 2 1 0]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(3) 2D Indexing
import numpy as np
a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Row and column
print(a[1]) # [4 5 6 7] — row 1
print(a[1, 2]) # 6 — row 1, col 2
# Sub-matrix
print(a[:2, 1:3])
# [[1 2]
# [5 6]]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(4) 3D Indexing Intuition
Think of a 3D array as "layers of 2D matrices":
import numpy as np
a = np.arange(24).reshape(2, 3, 4)
print(a.shape) # (2, 3, 4)
# Layer 0: first 3x4 matrix
print(a[0])
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Layer 1: second 3x4 matrix
print(a[1])
# [[12 13 14 15]
# [16 17 18 19]
# [20 21 22 23]]
# Single element: a[layer, row, col]
print(a[1, 2, 3]) # 23
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(5) Views vs Copies
Slices always return views. Modifying a slice modifies the original array.
import numpy as np
a = np.arange(12).reshape(3, 4)
view = a[0:2, 1:3]
view[0, 0] = 999
print(a)
# [[ 0 999 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# The original changed!
# Use .copy() for a safe copy
copy = a[0:2, 1:3].copy()
copy[0, 0] = 0
print(a[0, 1]) # 999 — original unchanged by copy
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: 1D slicing with step (Difficulty ⭐)
import numpy as np
a = np.arange(10)
print("Original:", a)
# [0 1 2 3 4 5 6 7 8 9]
print("a[2:8:2]:", a[2:8:2]) # [2 4 6]
print("a[::-1]:", a[::-1]) # [9 8 7 6 5 4 3 2 1 0]
print("a[5:1:-1]:", a[5:1:-1]) # [5 4 3 2]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: 2D sub-matrix extraction (Difficulty ⭐⭐)
import numpy as np
a = np.arange(20).reshape(4, 5)
print("Original:\n", a)
# Extract rows 1-2, cols 2-4
sub = a[1:3, 2:5]
print("a[1:3, 2:5]:\n", sub)
# [[ 7 8 9]
# [12 13 14]]
# Every other row, every other column
print("a[::2, ::2]:\n", a[::2, ::2])
# [[ 0 2 4]
# [10 12 14]]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
4. Comparison Tables
(1) Slice Variants
| Slice | Meaning | Result for [0,1,2,3,4,5] |
|---|---|---|
[:] |
All | [0 1 2 3 4 5] |
[2:] |
From index 2 | [2 3 4 5] |
[:3] |
First 3 | [0 1 2] |
[1:4] |
Index 1 to 3 | [1 2 3] |
[::2] |
Every other | [0 2 4] |
[::-1] |
Reversed | [5 4 3 2 1 0] |
(2) View vs Copy
| Feature | Slice (View) | Fancy Index (Copy) |
|---|---|---|
| Syntax | a[1:3] |
a[[1, 2, 3]] |
| Returns | View | Copy |
| Memory | Shared | New allocation |
| Modify affects original | Yes | No |
| Speed | O(1) | O(n) |
▶ Example: Using ellipsis for multi-dimensional slicing (Difficulty ⭐⭐)
import numpy as np
arr = np.arange(24).reshape(2, 3, 4)
print("Shape:", arr.shape)
# Ellipsis (...) means "all remaining dimensions"
print("arr[0, ...]:\n", arr[0, ...])
print("arr[..., 0]:\n", arr[..., 0])
# Select last column for all layers and rows
print("arr[..., -1]:\n", arr[..., -1])
Output:
TEXT 📖 Display onlyShape: (2, 3, 4) arr[0, ...]: [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] arr[..., 0]: [[ 0 4 8] [12 16 20]] arr[..., -1]: [[ 3 7 11] [15 19 23]]
❓ FAQ
.copy() explicitly: a[1:3].copy(). Or use fancy indexing like a[[0, 1, 2]] which always returns a copy.... (Ellipsis) stands for "all remaining dimensions." a[..., 0] means "select index 0 from the last dimension for all other dimensions." Useful for high-dimensional arrays.a[1:3] = [99, 100] replaces elements 1 and 2. This is a powerful feature — you can modify parts of an array in-place.📖 Summary
- Basic indexing:
a[i]accesses element i; negative indices count from the end - Slice syntax:
start:stop:step— all parts optional, negative step reverses direction - 2D indexing:
a[row, col]— mix slices and integers freely - 3D+ indexing: think in layers;
a[layer, row, col]for 3D - Slices return views — modifying them changes the original array
- Use
.copy()to get a safe, independent copy
📝 Exercises
-
Beginner (Difficulty ⭐): Create
a = np.arange(15). Extract: (a) first 5 elements, (b) last 3 elements, (c) elements at even indices, (d) the array in reverse order. -
Intermediate (Difficulty ⭐⭐): Create a 5x5 array of 0..24. Extract: (a) the 3x3 center sub-matrix, (b) the first and last rows, (c) all even rows and odd columns. Verify that modifying the sub-matrix changes the original.
-
Advanced (Difficulty ⭐⭐⭐): Create a 3x4x5 array. Use
...to select the last column of all layers and rows. Then usea[0, ...]to select the first layer. Explain what...does in each case.