Searching: searchsorted, nonzero, argwhere
Find WHERE values are: insertion points in sorted data with searchsorted, positions of True/non-zero elements with nonzero, np.where and argwhere.
Searching means finding positions, not values. Two different questions come up in analysis:
- "In which band does a score of 64 fall (0–29, 30–49, 50–99, 100+)?" — searching inside sorted data →
np.searchsorted. - "In which matches did Virat score a duck? In which (player, match) cells was the score above 70?" — finding where a condition is True →
np.nonzero,np.where,np.argwhere.
Definition
np.searchsorted(a, v)finds the indices where valuesvshould be inserted into the sorted arrayaso thatastays sorted. It uses binary search, so it is very fast.np.nonzero(a)returns the indices of all non-zero (True) elements, as a tuple of arrays, one per dimension.np.argwhere(a)returns the same positions grouped per element, as an array of shape(N, ndim).
- Sorted —
searchsortedassumesais sorted ascending. On unsorted data it returns meaningless numbers — no error. - Insertion index — e.g. inserting 45 into
[0, 30, 50, 100]keeps order at index 2 (between 30 and 50). - Non-zero / True — for a boolean mask like
runs == 0, True counts as non-zero, sononzerogives the positions where the condition holds. - Tuple vs rows —
nonzerogives(row_indices, col_indices)(good for indexing);argwheregives[[row, col], ...](good for reading/looping).
Syntax
python
np.searchsorted(a, v, side="left", sorter=None)
np.nonzero(a) # same as a.nonzero()
np.where(condition) # one-argument form == np.nonzero(condition)
np.argwhere(a)
np.flatnonzero(a) # 1D indices into the flattened array
np.count_nonzero(a) # how many non-zero / True| Parameter | Meaning | Default |
|---|---|---|
a (searchsorted) | 1D array sorted in ascending order | required |
v | value(s) to find insertion points for | required |
side | "left": index of the first equal element; "right": index after the last equal element | "left" |
sorter | optional argsort indices if a itself is not sorted | None |
a (nonzero / argwhere) | array or boolean condition to search | required |
Example — searchsorted: which band?
python
import numpy as np
thresholds = np.array([0, 30, 50, 100])
print(np.searchsorted(thresholds, 45))
print(np.searchsorted(thresholds, [10, 30, 64, 103]))
print(np.searchsorted(thresholds, [10, 30, 64, 103], side="right"))text
2
[1 1 3 4]
[1 2 3 4]How it works: 45 belongs between 30 (index 1) and 50 (index 2), so the insertion index is 2. For 30, which equals an element: side="left" gives 1 (before the existing 30), side="right" gives 2 (after it). That difference matters when a value sits exactly on a boundary.
Turning that into labels — the classic binning trick:
python
labels = np.array(["out cheaply", "start", "fifty", "century"])
runs2 = np.array([12, 45, 64, 103, 30])
print(labels[np.searchsorted(thresholds, runs2, side="right") - 1])text
['out cheaply' 'start' 'fifty' 'century' 'start']Example — keep a sorted list sorted
python
ladder = np.array([120, 145, 160, 180, 200]) # team totals, sorted
pos = np.searchsorted(ladder, 170)
print(pos)
print(np.insert(ladder, pos, 170))text
3
[120 145 160 170 180 200]Example — nonzero on 1D: which matches?
python
runs = np.array([72, 0, 88, 31, 0, 103, 52])
print(np.nonzero(runs))
print(np.nonzero(runs >= 50))
print(np.flatnonzero(runs >= 50))
print(np.where(runs >= 50))
print(np.count_nonzero(runs == 0))text
(array([0, 2, 3, 5, 6]),)
(array([0, 2, 5, 6]),)
[0 2 5 6]
(array([0, 2, 5, 6]),)
2How it works: np.nonzero(runs) lists the matches where he actually scored (not 0). np.nonzero(runs >= 50) lists the matches with 50+. Note the result is a tuple with one array — take [0] or use np.flatnonzero to get the plain array. np.where(cond) with one argument is identical to np.nonzero(cond). count_nonzero(runs == 0) counts the ducks: 2.
Example — 2D: nonzero vs argwhere
python
squad = np.array([[72, 45, 88],
[56, 0, 74],
[45, 32, 0]])
print(np.nonzero(squad == 0))
print(np.argwhere(squad == 0))
rows, cols = np.nonzero(squad > 70)
print(rows, cols, squad[rows, cols])
print(np.argwhere(squad > 70))text
(array([1, 2]), array([1, 2]))
[[1 1]
[2 2]]
[0 0 1] [0 2 2] [72 88 74]
[[0 0]
[0 2]
[1 2]]How it works: the ducks are at (row 1, col 1) and (row 2, col 2).
nonzero→(rows, cols)— perfect for fancy indexing:squad[rows, cols]pulls out the values.argwhere→ one[row, col]pair per line — perfect for reading or looping over coordinates. (np.argwhere(x)isnp.transpose(np.nonzero(x)).)
Comparison
| Function | Input | Output | Use it for |
|---|---|---|---|
np.searchsorted | sorted array + values | insertion indices | bins, sorted lookups |
np.nonzero / np.where(cond) | any array / mask | tuple of index arrays (one per axis) | indexing back into the array |
np.flatnonzero | any array / mask | 1D flat indices | quick 1D positions |
np.argwhere | any array / mask | (N, ndim) array of coordinates | listing coordinates |
np.argmax / np.argmin | any array | single index | position of the extreme |
arr[mask] | mask | the values | when you need values, not positions |
Common mistakes
- ❌
np.searchsortedon an unsorted array. ✅ Sort first, or passsorter=np.argsort(a). - ❌ Using
np.nonzero(runs >= 50)as if it were an array (len(...)gives 1!). ✅np.nonzero(...)[0]ornp.flatnonzero(...). - ❌ Confusing one-argument
np.where(cond)(positions) with three-argumentnp.where(cond, x, y)(values). ✅ Remember: 1 argument → indices; 3 arguments → if/else. - ❌ Expecting
np.nonzeroto treatFalseor0as found. ✅ Only non-zero / True positions are returned.
Interview tip
Q: How do you get the (row, col) of the maximum value in a 2D array?
A: np.unravel_index(np.argmax(a), a.shape). For the squad above that is row 0, col 2 (Virat's 88). Or np.argwhere(a == a.max()) to get all positions in case of ties.
Key points to remember
- np.searchsorted finds insertion indices in a SORTED array using binary search — unsorted input gives wrong answers silently.
- side='left' returns the index before equal elements, side='right' the index after them.
- searchsorted is great for binning values into bands (e.g. score categories).
- np.nonzero returns a tuple of index arrays (one per dimension) of non-zero / True elements; np.where(cond) is the same.
- np.argwhere returns an (N, ndim) array — one row of coordinates per match.
- np.flatnonzero gives flat 1D indices; np.count_nonzero counts True values.
- Use np.unravel_index(np.argmax(a), a.shape) to get the (row, col) of a 2D maximum.
Try it yourself
Score bands with searchsorted
Where did it happen? nonzero vs argwhere
Practice: find the big matches
Revision Flashcards
Tap a card to flip it and see the answer.
