This release is a pre-release and may not be stable for production use.
drift-or-shift
A reproducible research pipeline for distinguishing label shift from concept drift, and for showing exactly when a training-free logit offset is sufficient — and when it is not.
The experiments reproduce and extend the analysis in SSRN 6052514: start from synthetic two-class Gaussian data, apply the Bayes-optimal offset correction without retraining, then observe which metrics are invariant (ROC AUC) and which are not (PR-AUC, effective sample size). When the class-conditionals themselves move, the offset provably cannot recover the new posterior, and retraining becomes necessary.
Installation
# From source, for development
git clone https://github.com/DiogoRibeiro7/drift-or-shift.git
cd drift-or-shift
make install # editable install + dev extras + pre-commit hooks
Without make:
python -m pip install -e ".[dev]"
python -m pre_commit install
Runtime install only:
python -m pip install .
Requires Python 3.10+. This installs the drift_or_shift package, the
drift_shift_pipeline compatibility alias, the caliblab calibration
benchmarking helpers, and the eleven dos-expN console scripts.
Quickstart
# Fast end-to-end sanity check (seconds)
python scripts/smoke_exp1.py --results-dir results/smoke
# A full experiment
dos-exp1 --pi-train 0.2 --pi-tests 0.05 0.2 0.5 --results-dir results
# Aggregate every run into a reviewable dashboard
python scripts/aggregate_results.py --results-dir results \
--output reports/dashboard.md --figure reports/best_risk.png
Each run writes tables/, figures/, and a _summary.json into
results/<experiment>/<utc-timestamp>/.
Core concepts
Label shift vs. concept drift
| Label shift | Concept drift | |
|---|---|---|
| What moves | Class prior π only |
The class-conditionals p(x|y) |
| Posterior recoverable without retraining? | Yes — additive logit offset | No |
| Demonstrated by | Exp 1, 2, 5, 8–11 | Exp 4, 7 |
Offset correction
Logits are log-odds, so shifting the prior from π_train to π_test is a
constant additive term:
offset = log( π_test · (1 − π_train) / (π_train · (1 − π_test)) )
Apply the offset before comparing against the cost-derived threshold
log(c10 / c01); with default costs (c10 = c01 = 1) that threshold is zero.
Because a constant offset preserves the ranking of scores, ROC AUC is invariant
under label shift — while PR-AUC, which depends on prevalence, is not.
Experiments
| Command | What it demonstrates |
|---|---|
dos-exp1 |
Synthetic label shift: offset correction vs. oracle risk |
dos-exp2 |
ROC AUC invariance and PR-AUC prevalence dependence |
dos-exp3 |
ESS fraction shrinking as class weight α grows |
dos-exp4 |
Concept drift: standard vs. offset vs. retrained |
dos-exp5 |
Breast cancer label shift replication with resampling |
dos-exp6 |
Calibration (temperature, isotonic) vs. offset under label shift |
dos-exp7 |
Drift-type sweep: covariance, feature, and label shift |
dos-exp8 |
Multimodal label shift with mixture components |
dos-exp9 |
Covertype: real, high-dimensional label shift |
dos-exp10 |
Credit-card fraud benchmark with reweighted prevalences |
dos-exp11 |
High-variance medical-style benchmark with nonlinear test shifts |
Sources live in src/drift_or_shift/experiments/.
Every script accepts --help.
Reproducibility
- All generators take an explicit
seed; summaries record seeds, sizes, costs, and prevalence grids. - Run-directory names and summary timestamps are UTC and timezone-aware.
ExperimentConfigindrift_or_shift.experiments._common(or the CLI flags) pins a run's configuration.reproduce/holds a separate calibration-benchmark replication driven by reproduce/config/default.yaml.
Tooling utilities
drift_or_shift.drift_variants— inject covariance/feature shifts, label noise, and density-ratio scoring without rebuilding the generators.scripts/aggregate_results.py— collect everyresults/*/*_summary.jsoninto a markdown dashboard;--fail-on-alertsexits non-zero when drift is detected.scripts/drift_alerts.py— flagfeature_max_*statistics above threshold intoreports/drift_alerts.csv.scripts/watch_results.py— pollresults/and refresh alerts + dashboard as new summaries land (--oncefor a single pass).
See docs/CLI_USAGE.md for full flag documentation.
Development
make check # ruff + black + mypy + pytest, exactly what CI runs
make test-cov # tests with an HTML coverage report
make build # sdist + wheel, validated with twine
make help # all targets
CI runs the suite on Python 3.10–3.13 on Linux, plus Windows and macOS spot checks, and verifies that the built wheel installs and runs in a clean environment.
See CONTRIBUTING.md for branching and PR conventions, and CHANGELOG.md for release history.
Project documents
| File | Contents |
|---|---|
| RESULTS_DIGEST.md | Latest metrics and artifact paths |
| REPORT.md | Reproduction steps and what each figure claims |
| notes/lessons.md | Counter-intuitive findings worth revisiting |
| ROADMAP.md | Milestones |
| FUTURE.md | Candidate extensions |
| RELEASE.md | Release checklist |
Citation
If this code supports academic work, please cite the underlying paper (SSRN 6052514) and reference this repository:
@software{ribeiro_drift_shift_pipeline,
author = {Ribeiro, Diogo},
title = {drift-or-shift: label shift, offset correction and drift diagnostics},
url = {https://github.com/DiogoRibeiro7/drift-or-shift},
version = {0.1.0}
}
License
Released under the MIT License.
Metadata
Release files for drift-or-shift 0.1.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| drift_or_shift-0.1.0a1.tar.gz | 64.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| drift_or_shift-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 111.4 kB
Release files / drift_or_shift-0.1.0a1.tar.gz
| Download URL | drift_or_shift-0.1.0a1.tar.gz |
|---|---|
| Size | 64.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
52c977cc17c8f47da874ad9bc4409a58f586083877cd4502e5a781eef5801600
|
|
BLAKE2b-256 checksum How to use checksums |
4915f84aa66668b066d7ff0b5f3f8d7d140ea572b494fecc26a116294cfd26d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / drift_or_shift-0.1.0a1-py3-none-any.whl
| Download URL | drift_or_shift-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 47.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
88c48ec8ed7c2583b9a2b7fbdd014bad0a03924428b00c44e72b1a5571531a0e
|
|
BLAKE2b-256 checksum How to use checksums |
b7fc071fa825a9feaca5a5af9ce7d638b5ae45e8cae278d230892197b868044c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log