NumPy
2 min read
Updated 4 Aug 2026
5. Indexing & Slicing
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
Basic, negative, and slicing#
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
a[0], a[-1] # 0, 9
a[2:5] # [2 3 4]
a[::2] # [0 2 4 6 8]
a[::-1] # reversed
Multi-dimensional indexing#
m = np.arange(12).reshape(3, 4)
m[1, 2] # 6 (row 1, col 2)
m[1] # [4 5 6 7] (whole row -> a view)
m[:, 2] # [2 6 10] (whole column)
m[0:2, 1:3] # [[1 2],[5 6]]
📌 Remember: Use
m[i, j]notm[i][j]. The comma form is one indexing op; the chained form creates an intermediate array.
Fancy indexing (integer arrays)#
Selecting arbitrary elements with an array of indices → always returns a copy.
a = np.array([10, 20, 30, 40, 50])
a[[0, 2, 4]] # [10 30 50]
a[[0, 2, 4]] = 0 # assign via fancy index
Boolean indexing (masking)#
a = np.array([1, -2, 3, -4, 5])
a[a > 0] # [1 3 5]
a[a < 0] = 0 # clamp negatives to 0
np.sum(a > 2) # count elements > 2 -> 2
🚀 Best Practice: Boolean masking is the vectorized replacement for
if-loops.df[df.col > x]in Pandas is the same idea.
Views vs Copies — the critical distinction#
| Operation | Returns |
|---|---|
Basic slicing a[2:5] |
View (shares memory) |
Fancy indexing a[[1,3]] |
Copy |
Boolean indexing a[a>0] |
Copy |
reshape, .T, ravel |
Usually view |
flatten, .copy() |
Always copy |
a = np.arange(5)
s = a[1:4] # view
s[0] = 99
print(a) # [ 0 99 2 3 4] <- parent changed!
flowchart LR
P["parent a = [0,1,2,3,4]"] --> BUF["shared data buffer"]
V["view: a[1:4] (basic slice)"] --> BUF
F["copy: a[[1,3]] (fancy) / .copy()"] --> BUF2["independent buffer"]
BUF -.->|"mutating the view edits the parent"| P
⚠️ Common Mistake: Mutating a slice mutates the original. If you need independence, call
.copy().
⭐ Interview Question: Does slicing copy data? Basic slicing returns a view (no copy); fancy/boolean indexing returns a copy.