SWIFT score¶
Stage 4 produces the number the package is named for. Both samples — reference and monitoring — are pushed through the transformation \(\sigma_j\) fitted in Stage 3, and drift is measured as the Wasserstein distance between the two SHAP-transformed distributions. That per-feature distance is the SWIFT score.
from swift.distance import compute_swift_scores, wasserstein_1d
scores = compute_swift_scores(X_ref, X_mon, bucket_sets, order=1)
# Returns: dict[str, float] — feature name → SWIFT score
w = wasserstein_1d(u, v, order=1)
# Returns: non-negative float W_p between two 1-D sample arrays
compute_swift_scores transforms both samples internally with the same \(\sigma_j\)
stored in bucket_sets — fitted on the reference in Stage 3 and not recomputed here.
SWIFTMonitor.score(X_mon) is a thin wrapper that compares the monitoring sample
against the fitted reference; pass X_compare to compare any two windows instead
(scikit-learn integration).
The distance¶
For two empirical distributions \(P\) (transformed reference) and \(Q\) (transformed monitoring), the order-\(p\) Wasserstein distance is computed via the sorted-quantile formula
Two implementation paths:
- Fast path — for
order=1and equal sample sizes, \(W_1\) reduces to the mean absolute difference of the two sorted arrays. - General path — for unequal sizes or
order=2, the empirical quantile functions of both samples are evaluated on the merged grid of their CDF levels, and the differences are integrated with the grid spacings as weights. This handles unequal sample sizes correctly without resampling.
Order must be 1 or 2, and both arrays must be non-empty — anything else raises
ValueError.
Choosing the order¶
- W1 (
order=1) is the earth mover's distance — the \(L_1\) area between CDFs, robust and interpretable as the average shift in SHAP space. - W2 (
order=2) is the root of the integral of squared CDF differences — more sensitive to variance changes and large deviations.
For most use cases W1 is recommended; see Configuration.
Reading a score¶
Because the comparison happens after the SHAP transformation, a large shift in a region where the model's SHAP response is flat produces a small distance, while a small shift across a bucket boundary with a big SHAP jump produces a large one. That is the "model-aware" part of SWIFT in one sentence.
The score's units are SHAP units — for a LightGBM binary model, log-odds contribution of that feature. A score of 0.05 means the average observation's SHAP contribution for this feature has shifted by 0.05 in transport distance. Two consequences:
- Scores are comparable across features of the same model (they share the model's output scale), which is what makes the aggregates meaningful.
- A raw score still has no universal "significant" threshold — that calibration is Stage 5's job.
A feature the model never splits on transforms to a constant, so its score is exactly 0 (Buckets — the catch-all bucket).
Next stage: Testing turns scores into p-values and drift decisions. Full signatures: API reference — Distance.