Stable feature importance explanations under collinearity — works with any model and attribution method
Project description
DASH: Diversified Aggregation for Stable Hypotheses
Stable feature importance when your features are correlated. SHAP rankings from a single model are partially random under collinearity — DASH fixes this by averaging explanations across independent models so the noise cancels and the signal remains. Works with any attribution method.
Install
pip install dash-shap
Quick Check — Is Your Model Affected?
from dash_shap import check
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
result = check(data.data, data.target, task="binary",
feature_names=list(data.feature_names))
print(result.report())
DASH Stability Check
========================================
Models trained: 25
Features: 30
Unstable pairs: 68
Correlated groups: 5
UNSTABLE PAIRS (rankings flip across retrains):
worst radius vs worst perimeter: flip rate 48%, predicted 47%
mean radius vs mean perimeter: flip rate 44%, predicted 43%
...
RECOMMENDATION: Use M=150 models for 5% flip rate target
TOP FEATURES (DASH consensus):
1. worst concave points: 0.0831 (stable)
2. worst perimeter: 0.0534 (UNSTABLE)
3. worst radius: 0.0521 (UNSTABLE)
...
result.plot() shows the Importance-Stability plot. result.dash_importance() returns stable consensus rankings. result.to_dataframe() gives a full summary with SNR predictions.
Full Pipeline
For production use with diversity selection and full diagnostics:
from dash_shap import DASHPipeline
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
X, y = load_breast_cancer(return_X_y=True)
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.15, random_state=42)
X_temp, X_ref, y_temp, _ = train_test_split(X_temp, y_temp, test_size=0.12, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.18, random_state=42)
pipe = DASHPipeline(M=100, K=20, epsilon=0.05, epsilon_mode="relative",
task="binary", seed=42)
pipe.fit(X_train, y_train, X_val, y_val, X_ref=X_ref)
# Stable consensus importance
print(pipe.global_importance_)
# Importance-Stability plot: which features are stable vs. collinear?
pipe.plot_importance_stability()
# Feature Stability Index: per-feature stability scores
pipe.get_fsi().summary(top_k=10)
The IS plot places features in four quadrants:
| Quadrant | Importance | Stability | Action |
|---|---|---|---|
| I: Robust Drivers | High | Stable | Report individually |
| II: Collinear Cluster | High | Unstable | Report as a group |
| III: Unimportant | Low | Stable | Safe to omit |
| IV: Fragile | Low | Unstable | Investigate or drop |
Using with Any Attribution Method
The default pipeline uses XGBoost + TreeSHAP, but DASH works with any attribution source — LIME, Integrated Gradients, attention maps, KernelSHAP, or custom methods:
# You have M attribution vectors from any source, shape (M, P)
# e.g., LIME importance from M independently trained neural networks
pipe = DASHPipeline(M=100, K=20, epsilon=0.05)
pipe.fit_from_attributions(attribution_matrices, val_scores)
print(pipe.global_importance_)
# Or use the lightweight stability workflow directly
from dash_shap import validate_from_attributions, consensus_from_attributions
results = validate_from_attributions(attribution_matrix) # Z-tests + flip rates
stable = consensus_from_attributions(attribution_matrix) # averaged importance
The underlying impossibility theorem — mechanically verified in Lean 4 (248 theorems, 0 unproved gaps) — proves that no single-model ranking can be simultaneously faithful and stable under collinearity (the bilemma). DASH's consensus averaging is the unique structural resolution: it enriches the binary output space with a neutral element (ties), which the resolution taxonomy proves is the only escape from the impossibility. This holds for all iterative optimizers (gradient boosting, Lasso, neural networks).
Extensions
After fitting, pipe.result_ is a DASHResult that supports additional analyses:
from dash_shap.extensions import (
confidence_intervals, robust_certification, theory_bridge,
causal_flags, audit_report, DriftMonitor, federated_consensus,
)
# Bootstrap confidence intervals on importance and FSI
ci = confidence_intervals(pipe.result_)
print(ci.summary())
# Certify which features are top-k across ALL models (worst-case guarantee)
cert = robust_certification(pipe.result_)
print(cert.certified[5]) # features certified top-5
# Theory bridge: predict flip rates from impossibility theorem formulas
tb = theory_bridge(pipe.result_)
print(tb.summary()) # SNR per pair, predicted flip rates, M recommendation
# Label each feature: robust / collinear / fragile / unimportant
flags = causal_flags(pipe.result_, X_ref=X_ref)
print(flags.summary())
# Structured audit report with warnings for stakeholders
report = audit_report(pipe.result_, X_ref=X_ref, causal=flags)
print(report.summary())
# Monitor explanation drift between model versions
monitor = DriftMonitor(pipe.result_, threshold=0.1)
alert = monitor.check(new_result, label="v2")
if alert.drifted:
print(f"Drift: {alert.distance:.3f}, changed: {alert.changed_features}")
# Combine results from multiple sites without sharing data
fed = federated_consensus([result_site1, result_site2])
print(f"Cross-site agreement: {fed.cross_site_agreement:.3f}")
All 12 extensions: confidence_intervals, partial_order, feature_groups, stable_feature_selection, local_uncertainty, robust_certification, theory_bridge, causal_flags, audit_report, DriftMonitor, ParetoSelector, federated_consensus.
How It Works
DASH is a five-stage pipeline:
- Population — Train M independent models with random hyperparameters and restricted feature access, so each model explores different features
- Filtering — Keep models within ε of the best validation score
- Diversity Selection — Greedy MaxMin selection maximizing pairwise distance among importance vectors
- Consensus — Compute attributions for each selected model, then average
- Diagnostics — Feature Stability Index (FSI), IS plots, and coverage conflict (sign-stability diagnostic) for auditing without ground truth
The key insight: model independence — not model size — is what cancels the arbitrary noise. A colsample ablation confirms that forced low colsample_bytree (0.1–0.5) is the operative diversity mechanism: ensembles with unrestricted feature access achieve only single-best-level stability. See the paper for the full mechanism analysis (TMLR submission under review).
Documentation
| Resource | Description |
|---|---|
| API Reference | Complete parameter and method documentation |
| Extensions Guide | All 12 extensions with worked examples, organized by use case |
| FAQ | Usage, troubleshooting, parameter guidance |
| Diagnostics Guide | IS plots, FSI thresholds, disagreement maps |
| Getting Started | Concept-first introduction with worked examples |
| Research & Benchmarks | Full results, method comparisons, reproduction guide |
| Tutorials | Step-by-step notebooks from basic to advanced |
Citation
@misc{caraker2026firstmover,
title={First-Mover Bias in Gradient Boosting Explanations: Mechanism, Detection, and Resolution},
author={Caraker, Drake and Arnold, Bryan and Rhoads, David},
year={2026},
eprint={2603.22346},
archiveprefix={arXiv},
primaryclass={cs.LG},
doi={10.5281/zenodo.19060132},
url={https://arxiv.org/abs/2603.22346}
}
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dash_shap-0.2.1.tar.gz.
File metadata
- Download URL: dash_shap-0.2.1.tar.gz
- Upload date:
- Size: 116.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6dfc94972cf8e345734630035091ccbce80a6abd640dce60748d0e788df38855
|
|
| MD5 |
0c7040d8927805c0950bc41ec843dfed
|
|
| BLAKE2b-256 |
852f862e6df82e5ec6a44dec258547338f4fd4ca0cb7e02cd510760a1b504e6b
|
File details
Details for the file dash_shap-0.2.1-py3-none-any.whl.
File metadata
- Download URL: dash_shap-0.2.1-py3-none-any.whl
- Upload date:
- Size: 110.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d8aff1282c0c92b231d711f4e31b1c352dcff0eb0cbc87d14c42ba15cdaa7667
|
|
| MD5 |
ff7ea2373818bf2ef124768a03d0084c
|
|
| BLAKE2b-256 |
4d6cabf19778c52efaa57aa1e26899da85d6a81e8a0614ba0c89c7cbcdaf721d
|