import numpy as np
temps = np.array([[21, 19, 26],
[27, 23, 28],
[27, 18, 30],
[29, 20, 32]])Picking and ordering with integer arrays
Before this lesson: P02.3.2 Boolean masks
What you will be able to do
- Pick rows or columns in any order, with repeats, using a list or array of positions.
- Explain what np.argsort returns and use it to order the rows of a table by one column.
- Build argsort in plain Python with sorted(range(n), key=…).
- Shuffle features and targets with one seeded permutation so that every pair stays together.
- Tell the entries picked by temps[[0, 1], [2, 0]] from the block picked by np.ix_.
The question
Sort the four days from the coolest hill reading to the warmest, keeping each day’s three readings together. Shuffle a dataset’s rows before splitting it into training and test sets, without separating any input from its target. Both jobs put rows into a new order. A slice can only walk forwards or backwards in steps, and a mask can only keep or drop. What kind of index names an arbitrary order?
From the last lesson
1. What does temps[temps > 27] return?
Show answer
The 1-D array [28, 30, 29, 32]: the readings above 27, in row-by-row order.
2. Why does warm and cool fail for two masks, and what is written instead?
Show answer
and needs one truth value from each side, and an array of four has none, so NumPy raises ValueError. Write warm & cool.
Intuition
The running array once more: noon temperatures (invented) with days as rows and the coast, hill and valley stations as columns.
Put a list of positions inside the brackets and NumPy reads it as a shopping list: fetch the row at the first position, then the row at the second, and so on, and stack them in that order. Nothing says the positions must be in order or different from each other. This is called integer array indexing (some books say fancy indexing).
A list of positions picks rows in the order it names. The result has one row per entry of the list, so [3, 0] gives two rows, Thursday first, and [0, 0, 2] gives three, with Monday twice.
Build it up
Step 1. Rows in any order. temps[[3, 0]] has shape (2, 3): Thursday’s readings, then Monday’s. Note the two pairs of brackets: the outer ones index, the inner ones make the list. In plain Python the same rows come from a comprehension over the positions, [rows[i] for i in [3, 0]].
Step 2. Repeats. temps[[0, 0, 2]] has three rows, Monday twice and then Wednesday. Drawing the same example several times is exactly what a bootstrap sample does (S02.8).
Step 3. Columns too. A list in the column place reorders columns: temps[:, [2, 0]] gives the valley and then the coast reading for every day, shape (4, 2).
Step 4. The positions that would sort. np.argsort(a) does not return the sorted values. It returns the positions of the values in sorted order. For the hill column [19, 23, 18, 20] the smallest value, 18, is at position 2, then 19 at 0, 20 at 3 and 23 at 1, so np.argsort(hill) is [2, 0, 3, 1]. Feed those positions back in as an index and the rows come out ordered by hill temperature, each day’s readings travelling together.
np.argsort answers “where is the smallest, where is the next?” Its answer, used as an index, reorders whole rows so that the hill column rises.
For the warmest first, reverse the positions: np.argsort(hill)[::-1] is [1, 3, 0, 2], Tuesday first. And the first k entries of the reversed order are the positions of the k largest values: np.argsort(temps[:, 2])[::-1][:2] is [3, 2], the two warmest valley days, Thursday and Wednesday.
Step 5. Shuffling. A permutation of 0 to \({n - 1}\) is those numbers in some order, each once. rng.permutation(4) returns a random one, drawn from a generator made with a seed so that it can be repeated (seeds are lesson P02.9.2). Use the same permutation on the inputs and on the targets, and every input keeps its own target. This is how ML01.2.2 shuffles before it splits.
One permutation, applied to x and to y, moves each pair as a unit. Cutting the shuffled arrays then gives a random training set and a random test set.
The formal statement
Why temps[np.argsort(c)] orders the rows by c. Let o = np.argsort(c). By definition, the values c[o[0]], c[o[1]], ... are in increasing order. Now look at row \(k\) of temps[o]: it is row \({o_k}\) of temps, and its entry in the column c came from is \({c[o_k]}\). So, reading down the result, that column runs through \({c[o_0], c[o_1], \dots}\), which is increasing, and each row arrived whole, so no reading has moved to another day.
Ties. The coast column [21, 27, 27, 29] holds 27 twice. Either of the two 27s may come first in a sorted order. NumPy’s default sort does not promise which; np.argsort(c, kind="stable") promises that equal values keep their original order, here Tuesday before Wednesday.
Go deeper: undoing a shuffle
A permutation can be undone by another permutation, its inverse, and np.argsort finds it. Let order be a permutation of 0 to \({n - 1}\) and inv = np.argsort(order). Sorting the numbers 0 to \({n - 1}\) gives exactly \({0, 1, \dots, n - 1}\), so order[inv] is [0, 1, ..., n - 1]: position \(k\) of it says \({\text{order}[\text{inv}[k]] = k}\). Now take s = x[order] and index it with inv. Entry \(k\) of s[inv] is \({s[\text{inv}[k]] = x[\text{order}[\text{inv}[k]]] = x[k]}\). So s[inv] is x again: the shuffle is undone.
Worked example
Order the days by hill reading, coolest first: build the order in plain Python, check it against np.argsort, and apply it.
Python’s sorted takes a key: a function that gives the value to sort by. Sort the positions 0, 1, 2, 3, and let the key of position i be the hill reading hill[i]:
- Pair each position with its key: (0, 19), (1, 23), (2, 18), (3, 20).
- Sort the pairs by the key: (2, 18), (0, 19), (3, 20), (1, 23).
- Keep the positions:
[2, 0, 3, 1].
The worked example as a trace. Sorting positions by their keys and keeping the positions is what np.argsort does in one call.
That is np.argsort(hill). Then temps[[2, 0, 3, 1]] stacks Wednesday, Monday, Thursday and Tuesday:
\[ \begin{aligned} &[[27, 18, 30], \\ &\;\;[21, 19, 26], \\ &\;\;[29, 20, 32], \\ &\;\;[27, 23, 28]] \end{aligned} \]
The middle column now reads 18, 19, 20, 23, increasing, and every row is a day’s real readings.
Code
Picking rows from scratch with a comprehension, then with an index:
rows = temps.tolist()
print([rows[i] for i in [3, 0]])
print(temps[[3, 0]])
print(temps[[0, 0, 2]].shape, temps[:, [2, 0]].shape)[[29, 20, 32], [21, 19, 26]]
[[29 20 32]
[21 19 26]]
(3, 3) (4, 2)
Argsort from scratch, then NumPy’s, then the sorted table:
hill = temps[:, 1]
order = sorted(range(4), key=lambda i: hill[i])
print(order, np.argsort(hill))
print(temps[np.argsort(hill)])
print(np.argsort(hill)[::-1], np.argsort(temps[:, 2])[::-1][:2])
print(np.argsort(temps[:, 0], kind="stable"))[2, 0, 3, 1] [2 0 3 1]
[[27 18 30]
[21 19 26]
[29 20 32]
[27 23 28]]
[1 3 0 2] [3 2]
[0 1 2 3]
Shuffling the regression points of ML01 with one seeded permutation, then cutting it into a training half and a test half:
x = np.array([1, 2, 3, 4])
y = np.array([2, 3, 5, 6])
rng = np.random.default_rng(0)
order = rng.permutation(4)
print(order, x[order], y[order])
train, test = order[:2], order[2:]
print("train:", list(zip(x[train].tolist(), y[train].tolist())),
" test:", list(zip(x[test].tolist(), y[test].tolist())))[2 0 1 3] [3 1 2 4] [5 2 3 6]
train: [(3, 5), (1, 2)] test: [(2, 3), (4, 6)]
With the NumPy version this course pins, order comes out as [2 0 1 3], as in the figure. Another seed gives another order; whatever the order, every shuffled pair is one of the four true pairs, and the training and test positions never overlap. That is what the hidden checks test.
Explore
This module’s interactive, the index explorer, lives in lesson P02.3.4. Its list mode builds a list of row positions one press at a time: try [3, 0], then a list with repeats, and watch the result grow one row per entry.
Pitfalls
Wrong beside right. Two position lists are read in pairs, one entry per pair. To cross every chosen row with every chosen column, use np.ix_, or index the rows first and the columns second.
“temps[[0, 1], [2, 0]] gives rows 0 and 1 at columns 2 and 0.”
It gives two entries, temps[0, 2] and temps[1, 0], that is [26, 27]. The lists are zipped together, not crossed. For the 2 by 2 block, write temps[np.ix_([0, 1], [2, 0])] or temps[[0, 1]][:, [2, 0]].
“Shuffle the inputs and the targets, each with rng.permutation.”
Two calls draw two independent orders, which on a real dataset almost never agree, so input \(i\) ends up beside some other example’s target and the model learns from noise. Draw one order and index both arrays with it.
“np.argsort(hill) gives the hill readings in order.”
It gives their positions in order, [2, 0, 3, 1]. The sorted values are hill[np.argsort(hill)], or np.sort(hill), [18, 19, 20, 23]. Positions are what you need to move whole rows.
“Sorting puts tied values in their original order.”
Not unless you ask. With the default sort, the two coast readings of 27 may come out either way round. Pass kind="stable" when the order of ties matters, for example when a second sort must not undo a first.
Check yourself
Recall
1. What does temps[[3, 0]] return, and what is its shape?
Show answer
Thursday’s readings, then Monday’s: [[29, 20, 32], [21, 19, 26]], shape (2, 3).
2. What is np.argsort([19, 23, 18, 20])?
Show answer
[2, 0, 3, 1]: the smallest value is at position 2, then position 0, then 3, then 1.
3. How do you order the days by hill reading, warmest first, and which day comes first?
Show answer
temps[np.argsort(temps[:, 1])[::-1]]. Tuesday comes first, with 23.
4. What is temps[[0, 1], [2, 0]]?
Show answer
[26, 27]: the two entries temps[0, 2] and temps[1, 0], not a block.
5. Why must the inputs and the targets be shuffled with the same permutation?
Show answer
So that every input stays beside its own target. Two separate shuffles break the pairs.
Exercises
1. ★ List the days from the warmest valley reading to the coolest, and give the positions of the two warmest valley days.
Show solution
The valley column is [26, 28, 30, 32], already increasing, so np.argsort(temps[:, 2]) is [0, 1, 2, 3] and reversed it is [3, 2, 1, 0]: Thursday, Wednesday, Tuesday, Monday. The first two of the reversed order, [3, 2], are the positions of the two warmest valley days.
2. ★★ Predict temps[[1, 1, 3], [0, 2, 2]] and temps[[1, 1, 3]][:, [0, 2]], with their shapes.
Show solution
The first pairs the lists: temps[1, 0], temps[1, 2], temps[3, 2], that is [27, 28, 32], shape (3,). The second takes rows 1, 1 and 3 first, then columns 0 and 2 of each: [[27, 28], [27, 28], [29, 32]], shape (3, 2).
3. ★★★ After s = x[order] with order = [2, 0, 1, 3] and x = [1, 2, 3, 4], find a position list inv with s[inv] equal to x. Show that np.argsort(order) is such a list.
Show solution
s is [3, 1, 2, 4]. Value 1 sits at position 1 of s, value 2 at position 2, value 3 at position 0 and value 4 at position 3, so inv = [1, 2, 0, 3] and s[inv] is [1, 2, 3, 4].
np.argsort([2, 0, 1, 3]) lists where the smallest entry of order is (position 1, holding 0), then the next (position 2, holding 1), then position 0 (holding 2), then position 3 (holding 3): again [1, 2, 0, 3]. In general order[inv] is [0, 1, 2, 3], so entry \(k\) of s[inv] is \({x[\text{order}[\text{inv}[k]]] = x[k]}\), as the deeper block shows.
Summary card
A list of positions picks rows in the order it names.
Any order, repeats allowed.
a[[i, j, ...]]: rows \({i, j, \dots}\) in that order; always a copy.np.argsort(c): positions that sortc;a[np.argsort(c)]orders rows byc.- One
order = rng.permutation(n)forXandykeeps pairs together. a[p, q]pairs the lists;a[np.ix_(p, q)]gives the block.
Sources and next steps
Sources. Each was opened and checked against this lesson on 15 November 2026.
- NumPy developers, Indexing on ndarrays, NumPy user guide, section Advanced indexing, subsection Integer array indexing: index arrays are broadcast and iterated as one, so
x[[0, 1, 2], [0, 1, 0]]picks three entries; the example withnp.ix_, which without the call would select only the diagonal; advanced indexing always returns a copy. - NumPy developers, numpy.argsort, NumPy reference: returns the indices that would sort an array; the default kind is quicksort, and
kind="stable"(orstable=True) keeps equal values in their relative order. - NumPy developers, numpy.random.Generator.permutation, NumPy reference: for an integer
xit permutesnp.arange(x); for an array it shuffles a copy along axis 0. - Jake VanderPlas, Python Data Science Handbook, Fancy Indexing: the shape of the result reflects the shape of the index arrays;
X[row, col]pairs its index arrays; fancy indexing combined with slices; selecting a random subset of rows.
Next. P02.3.4 Views and copies asks what each kind of index gives back: a slice shares memory with the array, while a mask or a list of positions makes a copy.
This lesson unlocks