Indexing and Slicing
Selecting data without loops — and without accidents
Fancy indexing, slices and boolean masks select exactly the rows, columns and elements you want — and choosing view vs copy determines whether you silently mutate your data.
What you will learn
- Slice with step and negative indices
- Use boolean masks and integer arrays for selection
- Explain view-vs-copy and avoid mutation traps
What is this?
numpy selection comes in four forms: integer positions (arr[2]), slices (arr[1:5:2]), boolean masks (arr[arr > 0]), and fancy integer arrays (arr[[0, 2, 5]]). The catch: slices of basic indexing return views sharing memory; advanced indexing returns copies. Misreading that distinction is a classic production bug.
Why do you need it?
- Data extraction and subsetting happen constantly; loops are the slow way to do it.
- Boolean masks are how outliers, missing values and category subsets are filtered.
- View vs copy confusion silently corrupts data when you mutate a selection.
- Previewing shapes of selections (arr[:, 0], arr[::2]) reads code faster.
Mental model
Highlighting a page in a book vs taking a photo of a page
View (basic) vs copy (fancy indexing)
Highlighting the book marks the same shared paper — edit the highlight, edit the book (view). A photo is independent — change the photo, the book is untouched (copy).
How it works
- arr[i], arr[i, j] pick elements; arr[1:4] and arr[::-1] slice sequences with steps.
- Boolean mask: arr[mask] where mask has shape == arr shape, keeping True positions.
- Fancy indexing with an array of positions returns a copy of the selected elements.
- Basic indexing (slices, single ints) returns a view — logically the same buffer.
- Transposing and ravel also produce views; reshape may too.
What each selection returns.
Examples
Selection styles side by side
import numpy as npa = np.arange(20.0)mask = (a > 8) & (a < 13)print(a[mask]) # boolean selection → [9,10,11,12]print(a[a % 4 == 0]) # every 4th valueprint(a[::3]) # step slice → viewrows = np.arange(20).reshape(5, 4)print(rows[2]) # 3rd row (view)print(rows[:, 1]) # 2nd column (view)Mask and step selections pull exactly the intended values in one line. The 1D step slice is a view — mutate it and `a` changes — so know which kind of array you're manipulating before assignment.
In production
- A monitoring job grabs `latency[latency > p99]` to study the slowest 1% only.
- An A/B analysis slices control/treatment via a boolean assignment mask on a cohort column.
- A feature pipeline samples `arr[::50]` for quick previews before full processing.
Best practices
- Prefer boolean masks over loops for filtering.
- If you will mutate a selection, force a copy: selected.copy().
- Use .copy() wherever the semantics could confuse the next maintainer.
Common mistakes
- Assigning to a slice and discovering the parent changed too (view).
- Believing fancy indexing returns a view — it returns a copy, breaking in-place edits.
- Forgetting mask shape equality → IndexError or silent misalignment.
Debugging tips
- Identify view-vs-copy: assign arr2 = arr[:3]; arr2[0]=99; check arr[0].
- For wrong selection counts, print mask.sum() to confirm how many elements matched.
- When mutation 'doesn't stick', you likely have a fancy-indexed copy, not a view.
Interview checkpoint
Hands-on lab
- Practice slice, mask and fancy selection
- Find the p99+1% values with masks
- Prove view vs copy behavior
Indexing Playground
25 min · intermediate
Quick quiz
arr[arr > 3] is which kind of selection?
Mutating the result of arr[1:5] also changes arr.