Test kit
Seven checks that audit a numeric catalogue for the defects that survive review.
sixteen entries in the corrections register, written as code so they can be
run against your data rather than mine. Pure Python and NumPy, no dependencies. Each check
returns a small dictionary; each is under twenty lines, so it can be read and argued with.
The checks
| Check | Question it answers | Fails when |
|---|---|---|
threshold_audit |
Does a threshold comparison silently drop boundary values? | boundary values exist |
quantisation_audit |
Do reported values carry a rounding convention that biases a mean? | χ² is large |
derived_column_audit |
Is one column a deterministic transform of another? | residual σ ≤ 0.10 dex |
conditioning_audit |
Can this many unknowns be separated by this many measurements? | rank deficient or condition > 10³ |
count_bound_audit |
Is the model identified at all, before any question of precision? | points < parameters |
estimator_audit |
How much do standard estimators of one quantity disagree? | spread > 1.05 |
per_scale_audit |
Is a pooled statistic hiding a mixture of populations? | per-group spread > 1.5 |
Worked example, on real data
Run against 24,897 USGS earthquake records (2024, M ≥ 2.5). Every number below came out of the
toolkit, not out of a document.
1. Threshold
The completeness threshold was 4.4. A comparison written as m >= 4.35 + 0.05 evaluates
its right side to 4.400000000000001, and drops the events sitting exactly on the boundary.
| Boundary events present | Kept by naive comparison | Kept by corrected comparison | Dropped |
|---|---|---|---|
| 2,060 | 6,351 | 8,411 | 2,060 |
Effect on the b-value: 1.252 → 0.980, a 28 % error, from a comparison operator.
2. Quantisation
Last-digit counts over the same 24,897 magnitudes give χ² = 3,150 against a uniform expectation,
with a 37 % excess at digit 5. Reported magnitudes carry a rounding convention that a mean will inherit.
3. Estimators
Aki–Utsu maximum likelihood 1.252 against cumulative least squares 1.142 — a spread of 1.10 on the
same 8,411 events. Any single reported b hides this choice.
4. Per-scale mixing
| Magnitude scale | Events | b |
|---|---|---|
| Md | 2,179 | 0.728 |
| Mww | 1,163 | 0.995 |
| ML | 8,168 | 1.047 |
| Mwr | 705 | 1.562 |
| mb | 12,478 | 1.742 |
| Spread | — | 2.39× |
The pooled value for the same file is 1.252 — inside the range but belonging to no single scale.
5. Conditioning
Fitting two similar exponential sensitivities on five temperatures gives a condition number of
26.9. Adding a third, nearly collinear sensitivity and letting it carry its own
parameter raises that to 2,001.8 — a 74-fold penalty. The same third measurement added
as a redundant observation instead of a new parameter lowers it. The penalty belongs to the
parameter count, not the measurement count. This is the correction recorded as #15.
Known limitations, including the one found while building this
- The derived-column check produces false positives on monotone inverse pairs. Run on
a sorted column against its own reverse, it flags “derived” when the two are merely anti-correlated. The
0.10 dex rule was calibrated on forward transforms. Use it to triage, not to conclude. This defect was
discovered during the build of this page and is entry 16 in the corrections register. - Completeness uses maximum curvature, which is crude and can underestimate the threshold.
- Seven checks do not cover everything. Kriging weights, interpolation leverage, closed-sum
and selection effects are absent. - Each check reports a diagnostic, not a verdict. A large χ² means “look here”, not “the data are wrong”.
Use
import auditkit as ak ak.threshold_audit(values, mc=4.4) # -> dropped boundary events ak.quantisation_audit(values) # -> last-digit chi-square ak.derived_column_audit(x, y) # -> residual spread in decades ak.conditioning_audit(kernel_matrix) # -> rank, condition number ak.count_bound_audit(params=3, points=3) # -> identifiable at all? ak.estimator_audit(magnitudes, mc=4.4) # -> estimator spread ak.per_scale_audit(mags, types) # -> pooled-mixture detection
The point of a test kit is not that it finds faults. It is that a fault found once can be
checked for everywhere else in minutes, which is the only way an inspection becomes routine.
auditkit.py is 70 lines and ships with a demo script and its JSON
output. 4 November 2026.
