Skip to main content

SWIFT: SHAP-Weighted Impact Feature Testing

PyPI Python License

Model-aware distribution monitoring for production ML systems.

SWIFT detects feature drift by comparing SHAP-transformed distributions between reference and monitoring data. Unlike traditional drift detection that treats features independently of the model, SWIFT weights distribution changes by their impact on model predictions — flagging only the shifts that affect model behavior.

Installation

pip install swift-monitoring

Or with uv:

uv pip install swift-monitoring

For development (from source):

git clone https://github.com/wlazlod/swift.git
cd swift
uv pip install -e ".[dev]"

Requires Python >= 3.11.

Quick Start

from swift import SWIFTMonitor

# Create monitor with a trained tree-ensemble model
monitor = SWIFTMonitor(model=lgb_model, n_permutations=200)

# Fit on reference data (stages 1-3)
monitor.fit(X_ref)

# Test monitoring data for drift (stages 4-5)
result = monitor.test(X_mon)
print(result.drifted_features)

The SWIFT Pipeline

SWIFT executes a 5-stage pipeline:

Stage Operation Description
1 Extraction Extract decision points (split thresholds) from the trained model
2 Bucketing Partition each feature's domain into buckets based on decision points
3 SHAP Normalization Compute bucket-level mean SHAP values on reference data
4 Wasserstein Distance Measure distance between SHAP-transformed distributions
5 Permutation Test + MTC Estimate p-values and apply multiple testing correction

Stages 1-3 run during fit(). Stages 4-5 run during test().

API Reference

SWIFTMonitor

SWIFTMonitor(
    model,                          # Trained tree-ensemble (LightGBM/XGBoost)
    order=1,                        # Wasserstein order (1 or 2)
    n_permutations=1000,            # Permutations for p-value estimation
    alpha=0.05,                     # Significance level
    correction="benjamini-hochberg",# MTC method ("bonferroni", "bh", "fdr")
    n_synthetic=10,                 # Synthetic observations for empty buckets
    max_samples=None,               # Max pool size for permutation test
    random_state=42,                # RNG seed
)

Core Methods

Method Input Output Description
fit(X) DataFrame self Learn reference distribution (stages 1-3)
transform(X) DataFrame DataFrame Map values to bucket-level mean SHAP
fit_transform(X) DataFrame DataFrame Fit and transform in one call
score(X) DataFrame dict[str, float] Per-feature Wasserstein distances vs. reference (stage 4)
score(X, X_compare=Y) DataFrame, DataFrame dict[str, float] Per-feature distances: X vs. Y (SHAP transform from reference)
test(X) DataFrame SWIFTResult Full drift test vs. reference with p-values and MTC
test(X, X_compare=Y) DataFrame, DataFrame SWIFTResult Full drift test: X vs. Y (SHAP transform from reference)

Visualization Methods

Method Description
plot_buckets(feature_name) SHAP response curve + reference density per bucket
plot_buckets(feature_name, X=...) Custom density source (e.g. monitoring sample instead of reference)
plot_buckets(feature_name, X_compare=...) Side-by-side density comparison
plot_buckets(feature_name, x_axis="natural") Feature-value scale on x-axis (instead of bucket indices)
plot_swift_scores(result) Bar chart of SWIFT scores, colored by drift status
plot_swift_scores(result, result_compare=...) Grouped bars comparing two test results

Both return (Figure, Axes) tuples for further customization.

Result Objects

SWIFTResult — returned by test():

Attribute Type Description
feature_results tuple[FeatureSWIFTResult, ...] Per-feature results
swift_max float Maximum SWIFT score across features
swift_mean float Mean SWIFT score across features
num_drifted int Number of drifted features
drifted_features tuple[str, ...] Names of drifted features

FeatureSWIFTResult — per-feature detail:

Attribute Type Description
feature_name str Feature name
swift_score float SWIFT score (Wasserstein distance)
p_value float Permutation test p-value
is_drifted bool Drift flag after MTC
num_buckets int Number of buckets used

Visualization

Bucketing Profile

# Single sample — reference density only
fig, ax = monitor.plot_buckets("feature_0")

# Comparison — reference vs. drifted density side-by-side
fig, ax = monitor.plot_buckets(
    "feature_0",
    X_compare=X_drifted,
    labels=("Reference", "Drifted"),
)

# Natural x-axis — bucket midpoints on the feature-value scale
fig, ax = monitor.plot_buckets("feature_0", x_axis="natural")

# Custom density source — show monitoring density instead of reference
fig, ax = monitor.plot_buckets("feature_0", X=X_mon)

The bucketing profile shows:

  • Left y-axis: Mean SHAP per bucket (line + markers) with a shaded 95% band (mean +/- 2 std)
  • Right y-axis: Observation density (line + fill, fraction of samples in each bucket)
  • X-axis: Bucket intervals (default), compact labels B0, B1, ... for >20 buckets, or feature-value scale with x_axis="natural"
  • SHAP=0 reference: Dashed grey horizontal line

SWIFT Scores

# Single result — bars colored by drift status
fig, ax = monitor.plot_swift_scores(result, threshold=0.01)

# Comparison — grouped bars from two test results
fig, ax = monitor.plot_swift_scores(
    result_clean,
    result_compare=result_drifted,
    labels=("Clean", "Drifted"),
)

The score plot shows:

  • Single mode: Bars colored red (drifted) or blue (not drifted), with horizontal lines for SWIFT max, SWIFT mean, and an optional threshold
  • Comparison mode: Grouped side-by-side bars with neutral coloring

scikit-learn Integration

SWIFTMonitor inherits from BaseEstimator and TransformerMixin:

# Parameter inspection and modification
monitor.get_params()
monitor.set_params(alpha=0.01, correction="bonferroni")

# fit_transform shorthand
X_transformed = monitor.fit_transform(X_ref)

# Clone with same parameters
from sklearn.base import clone
monitor2 = clone(monitor)

Sample vs. Sample Comparison

By default score() and test() compare monitoring data against the fitted reference. Pass X_compare to compare two arbitrary samples instead — the SHAP transformation (buckets + mean SHAP) is always the one learned from the reference:

# Score: monitoring vs. drifted
scores = monitor.score(X_mon, X_compare=X_drifted)

# Full test: monitoring vs. drifted
result = monitor.test(X_mon, X_compare=X_drifted)
print(result.drifted_features)

Configuration

Parameter Default Description
model (required) Trained LightGBM or XGBoost model
order 1 Wasserstein order: 1 (W1) or 2 (W2, more sensitive to variance)
n_permutations 1000 Number of permutations for p-value estimation
alpha 0.05 Significance level for MTC
correction "benjamini-hochberg" MTC method. Aliases: "bh", "fdr", "bonf"
n_synthetic 10 Synthetic observations for empty buckets during fit
max_samples None Max pool size for permutation test (subsamples if exceeded)
random_state 42 RNG seed for reproducibility

Development

# Install dev dependencies
uv pip install -e ".[dev]"

# Run tests
pytest tests/

# Run tests with coverage
pytest tests/ --cov=swift --cov-report=term-missing

# Lint
ruff check src/ tests/

Project Structure

swift/
├── src/swift/
│   ├── __init__.py          # Public API exports
│   ├── pipeline.py          # SWIFTMonitor (main entry point)
│   ├── plotting.py          # Visualization functions
│   ├── extraction.py        # Stage 1: Decision point extraction
│   ├── bucketing.py         # Stage 2: Bucket construction
│   ├── normalization.py     # Stage 3: SHAP normalization
│   ├── distance.py          # Stage 4: Wasserstein distance
│   ├── threshold.py         # Stage 5: Permutation test + MTC
│   ├── aggregation.py       # Model-level score aggregation
│   └── types.py             # Data types (BucketSet, SWIFTResult, etc.)
├── tests/                   # Test suite
├── experiments/             # Experiment runners (ablations, power analysis, etc.)
├── docs/
│   └── notebooks/getting_started.ipynb   # Quickstart tutorial (rendered in the docs)
└── pyproject.toml

Citation

@article{swift2025,
  title={SWIFT: SHAP-Weighted Impact Feature Testing for Model-Aware Distribution Monitoring},
  year={2025}
}

Metadata

Release files for swift-monitoring 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for swift-monitoring 0.2.0
File Size Uploaded Provenance
swift_monitoring-0.2.0.tar.gz 2.1 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for swift-monitoring 0.2.0
File Interpreter ABI Platform
swift_monitoring-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.2 MB

Release files / swift_monitoring-0.2.0.tar.gz

Download URL swift_monitoring-0.2.0.tar.gz
Size 2.1 MB
Tags Source
SHA-256 checksum
How to use checksums
d3f807e258c7b49f908189fa44f1d5bfc3ced9f962bb9406167f604ca0dac82b
BLAKE2b-256 checksum
How to use checksums
3342b09b61fcd8ff8225a38f91b0978846b0f81f013e4e6daf71579be6718abf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 20, 2026.

Transparency log

Release files / swift_monitoring-0.2.0-py3-none-any.whl

Download URL swift_monitoring-0.2.0-py3-none-any.whl
Size 36.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0246dc7405ba709afe9ec0b9a0a1460cc2dd67a72f66a98c855d12944197fe25
BLAKE2b-256 checksum
How to use checksums
e00ea14fb322089f588460a0f2e56a67c37e41c935debeafe3106aecfd4f0d21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 20, 2026.

Transparency log

Security for swift-monitoring 0.2.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for swift_monitoring-0.2.0.tar.gz

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 Jul 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page