Top-N Patterns
Get the top or bottom N values and their owners with argsort[::-1][:n], per-row top-N on 2D arrays, and the faster argpartition for large data.
"Top 3 run-scorers." "Bottom 2 economy rates." "Each player's two best innings." Top-N questions appear in every leaderboard, dashboard and recommendation system. They combine everything from this chapter: argsort, reversing, slicing and fancy indexing.
Definition
A top-N query returns the N largest (or smallest) values of an array — usually together with who/what they belong to. The standard NumPy pattern is
idx = np.argsort(a)[::-1][:n](indices of the N largest, biggest first), thenvalues = a[idx]andlabels = names[idx]. For very large arrays,np.argpartitionfinds the top N without fully sorting.
- argsort — indices in ascending order.
[::-1]— reverse to descending.[:n]— keep the first n.- argpartition — only guarantees that the N biggest end up in the last N positions (in no particular order), which is O(n) instead of O(n log n).
Syntax
python
top_idx = np.argsort(a)[::-1][:n] # N largest, largest first
top_idx = np.argsort(a)[-n:][::-1] # same thing
bottom_idx = np.argsort(a)[:n] # N smallest, smallest first
top_vals = np.sort(a)[-n:][::-1] # values only
np.argpartition(a, kth, axis=-1) # indices; element at kth is in final sorted spot
np.partition(a, kth, axis=-1) # same, but returns values| Parameter | Meaning | Default |
|---|---|---|
a | array to search | required |
n | how many items you want | — |
kth | position that must end up correct; use -n for top-n | required |
axis | axis to work along (-1 = each row) | -1 |
Example — top 3 and bottom 2 run-scorers
python
import numpy as np
players = np.array(["Virat", "Rohit", "Hardik", "Shubman", "Shreyas", "Gill", "Pant"])
runs = np.array([779, 710, 580, 643, 695, 512, 655])
n = 3
top = np.argsort(runs)[::-1][:n]
print(top)
print(players[top], runs[top])
bottom = np.argsort(runs)[:2]
print(players[bottom], runs[bottom])text
[0 1 4]
['Virat' 'Rohit' 'Shreyas'] [779 710 695]
['Gill' 'Hardik'] [512 580]How it works:
text
np.argsort(runs) -> [5 2 3 6 4 1 0] (ascending)
[::-1] -> [0 1 4 6 3 2 5] (descending)
[:3] -> [0 1 4] (top 3)If you only need the values: np.sort(runs)[-n:][::-1] → [779 710 695].
Example — argpartition for big data
python
p = np.argpartition(runs, -n)[-n:]
print(p)
print(players[p], runs[p])
p_sorted = p[np.argsort(runs[p])[::-1]]
print(players[p_sorted], runs[p_sorted])text
[4 1 0]
['Shreyas' 'Rohit' 'Virat'] [695 710 779]
['Virat' 'Rohit' 'Shreyas'] [779 710 695]How it works: np.argpartition(runs, -3) rearranges the indices so that the 3 largest values sit in the last 3 positions, but it does not promise any order inside that group (or before it). We take [-3:], then sort just those 3 — cheap, because it's only 3 items. On an array of 10 million scores where you need the top 10, this is much faster than a full argsort.
Note: on small arrays the output of
np.partitionmay look fully sorted — that's a coincidence of the algorithm. The only guarantee is: everything beforekthis ≤ thekthelement, everything after is ≥ it.
Example — top 2 per row (each player's best innings)
python
squad = np.array([[72, 45, 88, 31, 64],
[56, 0, 74, 88, 32],
[45, 32, 0, 67, 18]])
print(np.sort(squad, axis=1)[:, ::-1][:, :2])
idx = np.argsort(squad, axis=1)[:, ::-1][:, :2]
print(idx)
print(np.take_along_axis(squad, idx, axis=1))text
[[88 72]
[88 74]
[67 45]]
[[2 0]
[3 2]
[3 0]]
[[88 72]
[88 74]
[67 45]]How it works: argsort(axis=1) sorts within each row; [:, ::-1] reverses the columns; [:, :2] keeps the first two. idx tells you which matches those were (Virat's best: match index 2 and 0). np.take_along_axis uses those per-row indices to fetch the values.
Ties in top-N
python
s = np.array([10, 50, 30, 50, 20])
print(np.argsort(s)[::-1])
print(np.argsort(-s, kind="stable"))text
[3 1 2 4 0]
[1 3 2 4 0]Both 50s are "top", but reversing puts index 3 before 1. If ties should keep their original order (e.g. the player who reached it first), use np.argsort(-s, kind="stable")[:n].
Choosing a pattern
| Need | Pattern | Cost |
|---|---|---|
| top N values only | np.sort(a)[-n:][::-1] | O(n log n) |
| top N with labels | idx = np.argsort(a)[::-1][:n] | O(n log n) |
| bottom N with labels | idx = np.argsort(a)[:n] | O(n log n) |
| top N, huge array, small N | np.argpartition(a, -n)[-n:] then sort those | ~O(n) |
| top N per row | np.argsort(a, axis=1)[:, ::-1][:, :n] | O(n log n) |
| just the single max | a.argmax() | O(n) |
| top N with ties in original order | np.argsort(-a, kind="stable")[:n] | O(n log n) |
Common mistakes
- ❌
np.argsort(a)[:n]for the top N. ✅ That's the bottom N — reverse first:[::-1][:n]. - ❌
np.argsort(a)[::-1][n:]. ✅ The slice must be[:n](first n), not[n:](everything after n). - ❌ Assuming
argpartition's top-N are already sorted. ✅ Sort the N results yourself. - ❌ Sorting values and labels separately. ✅ Always index both with the same
idx.
Interview tip
Q: Find the 10 highest values in an array of 50 million floats efficiently.
A: idx = np.argpartition(a, -10)[-10:] (linear time), then order those 10 with idx[np.argsort(a[idx])[::-1]]. A full argsort would be O(n log n) for no benefit.
Key points to remember
- Top-N with labels: idx = np.argsort(a)[::-1][:n], then a[idx] and names[idx].
- Bottom-N: np.argsort(a)[:n] — without reversing.
- Values only: np.sort(a)[-n:][::-1].
- np.argpartition(a, -n)[-n:] finds the top n in ~linear time but in no guaranteed order — sort those n afterwards.
- Per-row top-N: np.argsort(a, axis=1)[:, ::-1][:, :n] with np.take_along_axis to fetch the values.
- Reversing an argsort also reverses ties; use np.argsort(-a, kind='stable') to keep tied items in original order.
Try it yourself
Top 3 batters with names
argpartition vs argsort
Each player's two best innings
Practice: top bowlers by economy
Revision Flashcards
Tap a card to flip it and see the answer.
