Sorting and Unique
Argsort
Produce the index ordering that would sort a 6-element list. The naive version
builds (value, index) pairs, sorts them, then extracts the indices — keeping
the sort structure visible without a nested loop. The trace shows pairs
growing, then reordering in one step, then order filling with the extracted
indices.
By hand
Build a list of [value, index] pairs. Sort the pairs (Python sorts lists
lexicographically, so they sort by value then by index). Extract the second
element of each sorted pair into order.
naive.py
Replay: real traced execution (multi-file project)
values = [3, 1, 4, 2, 5, 0]
pairs = []
for i in range(len(values)):
pairs.append([values[i], i])
pairs.sort()
order = []
for p in pairs:
order.append(p[1])
print('RESULT:', order)
values ← [3, 1, 4, 2, 5, 0]
1values = [3, 1, 4, 2, 5, 0]2pairs = []values this step[3, 1, 4, 2, 5, 0]valuespairs ← []
1values = [3, 1, 4, 2, 5, 0]2pairs = []3for i in range(len(values)):values this step[]pairsi ← 0, pairs ← [[3, 0]]
pass 1 of 62pairs = []3for i in range(len(values)):4 pairs.append([values[i], i])5pairs.sort()values this step0i[] → [[3, 0]]pairsAll 6 passes — pass 1 is the card above pass ipairs1 0 [] → [[3, 0]] 2 0 → 1 [[3, 0]] → [[3, 0], [1, 1]] 3 1 → 2 [[3, 0], [1, 1]] → [[3, 0], [1, 1], [4, 2]] 4 2 → 3 [[3, 0], [1, 1], [4, 2]] → [[3, 0], [1, 1], [4, 2], [2, 3]] 5 3 → 4 [[3, 0], [1, 1], [4, 2], [2, 3]] → [[3, 0], [1, 1], [4, 2], [2, 3], [5, 4]] 6 4 → 5 [[3, 0], [1, 1], [4, 2], [2, 3], [5, 4]] → [[3, 0], [1, 1], [4, 2], [2, 3], [5, 4], [0, 5]] for i in range(len(values)):
2pairs = []3for i in range(len(values)):4 pairs.append([values[i], i])pairs ← [[0, 5], [1, 1], [2, 3], [3, 0], [4, 2], [5, 4]]
4 pairs.append([values[i], i])5pairs.sort()6order = []values this step[[3, 0], [1, 1], [4, 2], [2, 3], [5, 4], [0, 5]] → [[0, 5], [1, 1], [2, 3], [3, 0], [4, 2], [5, 4]]pairsorder ← []
5pairs.sort()6order = []7for p in pairs:values this step[]orderp ← [0, 5], order ← [5]
pass 1 of 66order = []7for p in pairs:8 order.append(p[1])9print('RESULT:', order)values this step[0, 5]p[] → [5]orderAll 6 passes — pass 1 is the card above pass porder1 [0, 5] [] → [5] 2 [0, 5] → [1, 1] [5] → [5, 1] 3 [1, 1] → [2, 3] [5, 1] → [5, 1, 3] 4 [2, 3] → [3, 0] [5, 1, 3] → [5, 1, 3, 0] 5 [3, 0] → [4, 2] [5, 1, 3, 0] → [5, 1, 3, 0, 2] 6 [4, 2] → [5, 4] [5, 1, 3, 0, 2] → [5, 1, 3, 0, 2, 4] for p in pairs:
6order = []7for p in pairs:8 order.append(p[1])stdout ← RESULT: [5, 1, 3, 0, 2, 4]
8 order.append(p[1])9print('RESULT:', order)values this stepRESULT: [5, 1, 3, 0, 2, 4]stdout
With NumPy
np.argsort(a) returns an array of integer indices such that a[indices]
gives a in ascending order.
library.py
import numpy as np
values = [3, 1, 4, 2, 5, 0]
a = np.array(values)
result = np.argsort(a)
print('a: shape:', a.shape, 'dtype:', a.dtype, 'values:', a.tolist())
print('result: shape:', result.shape, 'dtype:', result.dtype, 'values:', result.tolist())
print('RESULT:', result.tolist())
a: shape: (6,) dtype: int64 values: [3, 1, 4, 2, 5, 0]
result: shape: (6,) dtype: int64 values: [5, 1, 3, 0, 2, 4]
RESULT: [5, 1, 3, 0, 2, 4]
Implementation notes
- Verify:
a[[5, 1, 3, 0, 2, 4]]=[0, 1, 2, 3, 4, 5]— applying the argsort indices toayields the sorted values (seefancy-indexfor index-array selection). - Argsort is useful to reorder a parallel array: if
namesandscoresshare the same positional index,argsort(scores)gives the index order to sortnamesby score without losing alignment. - For the rank-assignment pattern (each element gets its sorted rank) see
rank-assignin the python-data-basics book. - Shape, dtype, and values are shown explicitly here because
ndarray.__repr__output varies with NumPy version and print options.