Typorithm · medium

Stable sorting, tie rules and aligned identifiers

pythonnumpynumerical-computingindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Stable sorting, tie rules and aligned identifiers

0%

Concept and contract

Sort scores through one shared ordering vector and make tie-breaking a deliberate policy.

An ordering vector contains positions, not sorted business values. argsort lets you apply the same permutation to identifiers, scores and any other aligned field. Sorting each column independently destroys row relationships even when every output column appears individually sorted. This lesson ranks unique technical-role identifiers by decreasing finite score and preserves the input order of equal scores.

Stability answers a specific tie question: equal sort keys retain their relative order. It does not introduce a secondary business key automatically. A ranking can therefore be deterministic for one input order yet change when the upstream order changes. If a different tie rule is required, encode it explicitly.

Mechanism and worked example

The worked function validates equal-length vectors, unique identifiers and finite scores.

The worked function validates equal-length vectors, unique identifiers and finite scores. It sorts the negated scores with kind="stable", then applies the resulting positions to both arrays. A fixture with tied scores and deliberately unordered identifiers makes the stable input-order policy visible. The return boundary converts NumPy scalars to ordinary JSON-compatible lists.

Trace one position from the original arrays into the result. Its identifier and score must travel together. This technique extends to several numerical columns, but it still depends on an established canonical row identity before sorting. A score sort is not a deduplication algorithm.

Complete worked example

python

import numpy as np

def inspect_example(identifiers, scores):
    ids = np.asarray(identifiers, dtype=np.int64)
    values = np.asarray(scores, dtype=np.float64)
    if ids.ndim != 1 or ids.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if np.unique(ids).size != ids.size or not np.isfinite(values).all():
        raise ValueError("expected unique identifiers and finite scores")
    order = np.argsort(-values, kind="stable")
    return {"ids": ids[order].tolist(), "scores": values[order].tolist()}
Reasoning
The worked function validates equal-length vectors, unique identifiers and finite scores. It sorts the negated scores with kind="stable", then applies the resulting positions to both arrays. A fixture with tied scores and deliberately unordered identifiers makes the stable input-order policy visible. The return boundary converts NumPy scalars to ordinary JSON-compatible lists. Trace one position from the original arrays into the result. Its identifier and score must travel together. This technique extends to several numerical columns, but it still depends on an established canonical row identity before sorting. A score sort is not a deduplication algorithm.
Open in local Python playground ↗

Focused decision: context is in the complete example

python

order = np.argsort(-values, kind="stable")
ranked_ids = ids[order]
ranked_scores = values[order]
Focused decision
Type this short numerical decision after reading its complete context. The complete imports, array construction, validation, and result checks remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

The controlled mistake sorts increasing scores instead of decreasing scores.

The controlled mistake sorts increasing scores instead of decreasing scores. Other plausible mistakes include reversing an ascending stable order, which also reverses ties, and sorting identifiers independently after scores have been ordered. Tests should distinguish direction, tie policy and alignment rather than check only that the first score is large.

Non-finite scores need a stated policy before ranking. This lesson rejects them instead of relying on library placement conventions for NaN. Equal identifiers are also rejected because the caller promises one canonical role per row. Those input boundaries keep the ordering problem separate from source reconciliation.

Controlled mistake: inspect the failing numerical contract

python

import numpy as np

def inspect_example(identifiers, scores):
    ids = np.asarray(identifiers, dtype=np.int64)
    values = np.asarray(scores, dtype=np.float64)
    if ids.ndim != 1 or ids.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if np.unique(ids).size != ids.size or not np.isfinite(values).all():
        raise ValueError("expected unique identifiers and finite scores")
    order = np.argsort(values, kind="stable")
    return {"ids": ids[order].tolist(), "scores": values[order].tolist()}
Reasoning
The controlled mistake sorts increasing scores instead of decreasing scores. Other plausible mistakes include reversing an ascending stable order, which also reverses ties, and sorting identifiers independently after scores have been ordered. Tests should distinguish direction, tie policy and alignment rather than check only that the first score is large. Non-finite scores need a stated policy before ranking. This lesson rejects them instead of relying on library placement conventions for NaN. Equal identifiers are also rejected because the caller promises one canonical role per row. Those input boundaries keep the ordering problem separate from source reconciliation.
Open in local Python playground ↗

Independent practice

Change the tie rule so equal scores are ordered by increasing identifier.

Change the tie rule so equal scores are ordered by increasing identifier. Preserve decreasing primary score order and keep both output lists aligned. The small task replaces the ordering expression; validation and the application of the resulting positions should remain unchanged.

Predict the result for identifiers [30, 10, 20] with scores [5, 5, 2]. The changed ranking puts 10 before 30, while the baseline keeps 30 before 10. A case without ties would not show whether the requested change was implemented.

Step 1: Break equal scores by increasing identifier instead of input order. Step 2: Retain the working imports, input validation and array shapes. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

import numpy as np

def inspect_example(identifiers, scores):
    ids = np.asarray(identifiers, dtype=np.int64)
    values = np.asarray(scores, dtype=np.float64)
    if ids.ndim != 1 or ids.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if np.unique(ids).size != ids.size or not np.isfinite(values).all():
        raise ValueError("expected unique identifiers and finite scores")
    order = np.argsort(-values, kind="stable")
    return {"ids": ids[order].tolist(), "scores": values[order].tolist()}

# Independent task: Break equal scores by increasing identifier instead of input order.
Reasoning
Break equal scores by increasing identifier instead of input order.
Open in local Python playground ↗
  • Hint 1: Keep decreasing scores as the primary sort key.
  • Hint 2: For np.lexsort, the last supplied key is primary: use (ids, -values).

Solution, tests and trade-offs

The solution uses lexsort with identifiers as the secondary key and negated scores as the primary key.

The solution uses lexsort with identifiers as the secondary key and negated scores as the primary key. lexsort interprets the final key as primary, which makes key order part of the exercise. The practice cases include tied and untied scores, an empty pair of vectors and duplicate identifiers that must still be rejected.

Sorting produces an ordering, not a justified prediction or a probability. The scores in this lab are small synthetic measurements with an explicit contract. Later statistics and ML courses will discuss how a score is estimated and validated; a stable presentation order cannot establish that model quality.

Practice solution and acceptance checks

python

import numpy as np

def inspect_example(identifiers, scores):
    ids = np.asarray(identifiers, dtype=np.int64)
    values = np.asarray(scores, dtype=np.float64)
    if ids.ndim != 1 or ids.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if np.unique(ids).size != ids.size or not np.isfinite(values).all():
        raise ValueError("expected unique identifiers and finite scores")
    order = np.lexsort((ids, -values))
    return {"ids": ids[order].tolist(), "scores": values[order].tolist()}
Reasoning
The solution uses lexsort with identifiers as the secondary key and negated scores as the primary key. lexsort interprets the final key as primary, which makes key order part of the exercise. The practice cases include tied and untied scores, an empty pair of vectors and duplicate identifiers that must still be rejected. Sorting produces an ordering, not a justified prediction or a probability. The scores in this lab are small synthetic measurements with an explicit contract. Later statistics and ML courses will discuss how a score is estimated and validated; a stable presentation order cannot establish that model quality.
Open in local Python playground ↗

Interview reasoning and next step

Explain why an index permutation should be reused across aligned fields and how a stable descending sort differs from reversing an ascending result.

Explain why an index permutation should be reused across aligned fields and how a stable descending sort differs from reversing an ascending result. Describe which tie policy survives changes in upstream order. If asked for top-k, explain why a cutoff through tied values also requires a stated policy.

Type the shared ordering and aligned indexing on the PC, then recall the lexsort key order for the independent task. In the app, inspect the concrete tie examples. Next, group repeated labels and keep inverse indexes, counts and weighted values aligned.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/numpy-lab-20261106.zip. Install pinned dependencies in an isolated Python environment. Run verify.py, then change the working exercise baseline and run --practice np-sorting. The real NumPy arrays, numerical contracts, random state checks and temporary numeric files use owned fixtures only. No production database, feed, account or paid service is used. This lab executes Python; the PC typing trainer and native reading progress do not execute it.

Sort scores through one shared ordering vector and make tie-breaking a deliberate policy.