shaply 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shaply-1.0.0/PKG-INFO +211 -0
- shaply-1.0.0/README.md +178 -0
- shaply-1.0.0/pyproject.toml +137 -0
- shaply-1.0.0/src/shaply/__init__.py +110 -0
- shaply-1.0.0/src/shaply/colors.py +96 -0
- shaply-1.0.0/src/shaply/config.py +241 -0
- shaply-1.0.0/src/shaply/enums.py +69 -0
- shaply-1.0.0/src/shaply/explanation.py +279 -0
- shaply-1.0.0/src/shaply/interaction.py +98 -0
- shaply-1.0.0/src/shaply/plots/__init__.py +52 -0
- shaply-1.0.0/src/shaply/plots/_common/__init__.py +8 -0
- shaply-1.0.0/src/shaply/plots/_common/cluster.py +122 -0
- shaply-1.0.0/src/shaply/plots/_common/layout.py +42 -0
- shaply-1.0.0/src/shaply/plots/_common/ordering.py +130 -0
- shaply-1.0.0/src/shaply/plots/_common/stats.py +42 -0
- shaply-1.0.0/src/shaply/plots/advanced/__init__.py +32 -0
- shaply-1.0.0/src/shaply/plots/advanced/beeswarm_ranges.py +197 -0
- shaply-1.0.0/src/shaply/plots/advanced/error_analysis.py +151 -0
- shaply-1.0.0/src/shaply/plots/advanced/explanation_archetypes.py +116 -0
- shaply-1.0.0/src/shaply/plots/advanced/feature_clustering.py +115 -0
- shaply-1.0.0/src/shaply/plots/advanced/importance_by_cohort.py +146 -0
- shaply-1.0.0/src/shaply/plots/advanced/importance_ci.py +118 -0
- shaply-1.0.0/src/shaply/plots/advanced/interaction_heatmap.py +93 -0
- shaply-1.0.0/src/shaply/plots/advanced/monotonicity.py +108 -0
- shaply-1.0.0/src/shaply/plots/advanced/response_curve.py +200 -0
- shaply-1.0.0/src/shaply/plots/advanced/shap_surface.py +148 -0
- shaply-1.0.0/src/shaply/plots/usual/__init__.py +21 -0
- shaply-1.0.0/src/shaply/plots/usual/bar.py +111 -0
- shaply-1.0.0/src/shaply/plots/usual/beeswarm.py +214 -0
- shaply-1.0.0/src/shaply/plots/usual/decision.py +140 -0
- shaply-1.0.0/src/shaply/plots/usual/force.py +164 -0
- shaply-1.0.0/src/shaply/plots/usual/heatmap.py +104 -0
- shaply-1.0.0/src/shaply/plots/usual/scatter.py +122 -0
- shaply-1.0.0/src/shaply/plots/usual/waterfall.py +129 -0
- shaply-1.0.0/src/shaply/py.typed +0 -0
shaply-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: shaply
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Usual SHAP explainability figures rendered as interactive Plotly charts.
|
|
5
|
+
Keywords: shap,shapley,plotly,explainability,xai,machine-learning
|
|
6
|
+
Author: antoine126
|
|
7
|
+
Author-email: antoine126 <antoine.pagneux@neuf.fr>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: numpy>=1.26
|
|
17
|
+
Requires-Dist: plotly>=5.20
|
|
18
|
+
Requires-Dist: pydantic>=2.6
|
|
19
|
+
Requires-Dist: pandas>=2.0 ; extra == 'examples'
|
|
20
|
+
Requires-Dist: scikit-learn>=1.4 ; extra == 'examples'
|
|
21
|
+
Requires-Dist: xgboost>=2.0 ; extra == 'examples'
|
|
22
|
+
Requires-Dist: lightgbm>=4.0 ; extra == 'examples'
|
|
23
|
+
Requires-Dist: shap>=0.45 ; extra == 'examples'
|
|
24
|
+
Requires-Dist: ipykernel>=6.29 ; extra == 'examples'
|
|
25
|
+
Requires-Dist: nbformat>=5.9 ; extra == 'examples'
|
|
26
|
+
Requires-Dist: pandas>=2.0 ; extra == 'pandas'
|
|
27
|
+
Requires-Python: >=3.12.7
|
|
28
|
+
Project-URL: Homepage, https://github.com/antoine126/shaply
|
|
29
|
+
Project-URL: Repository, https://github.com/antoine126/shaply
|
|
30
|
+
Provides-Extra: examples
|
|
31
|
+
Provides-Extra: pandas
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# shaply
|
|
35
|
+
|
|
36
|
+
**Usual SHAP explainability figures, rendered as interactive [Plotly](https://plotly.com/python/) charts.**
|
|
37
|
+
|
|
38
|
+
`shaply` reproduces the familiar figures from the [`shap`](https://github.com/shap/shap) library - bar, beeswarm, waterfall, dependence (scatter) and heatmap - but returns `plotly.graph_objects.Figure` objects instead of matplotlib axes, so the plots are interactive and embeddable out of the box.
|
|
39
|
+
|
|
40
|
+
It does **not** depend on `shap`: every plotting function accepts a `shap.Explanation`-like object, a raw NumPy array of SHAP values, or a pandas `DataFrame`.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add shaply
|
|
46
|
+
# or
|
|
47
|
+
pip install shaply
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Optional dependencies (extras)
|
|
51
|
+
|
|
52
|
+
`shaply` itself only needs `numpy`, `plotly` and `pydantic`. Extra features and
|
|
53
|
+
the example notebook pull in heavier packages, grouped as installable extras:
|
|
54
|
+
|
|
55
|
+
| Extra | Installs | Purpose |
|
|
56
|
+
| ------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
57
|
+
| `pandas` | `pandas` | Pass SHAP values as a`DataFrame` (column names become feature names) |
|
|
58
|
+
| `examples` | `scikit-learn`, `xgboost`, `lightgbm`, `shap`, `pandas`, `ipykernel`, `nbformat` | Everything needed to run[`examples/shaply_demo.ipynb`](examples/shaply_demo.ipynb) |
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv add "shaply[pandas]" # DataFrame support
|
|
62
|
+
uv add "shaply[examples]" # run the demo notebook
|
|
63
|
+
# or with pip
|
|
64
|
+
pip install "shaply[examples]"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Building the wheel from source (use the package without PyPI)
|
|
68
|
+
|
|
69
|
+
To build and install `shaply` straight from a clone of this repository, without going through PyPI:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/antoine126/shaply.git
|
|
73
|
+
cd shaply
|
|
74
|
+
uv build --wheel # produces dist/shaply-<version>-py3-none-any.whl
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then install the wheel wherever you need it:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv add /path/to/shaply/dist/shaply-<version>-py3-none-any.whl
|
|
81
|
+
# or with pip, in any environment
|
|
82
|
+
pip install /path/to/shaply/dist/shaply-<version>-py3-none-any.whl
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The wheel is self-contained (it ships `py.typed`, so type annotations reach the installed package) and does not require `uv` or the source tree at runtime.
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import shaply
|
|
91
|
+
|
|
92
|
+
# `explanation` can be a shap.Explanation, an ndarray, or a DataFrame
|
|
93
|
+
fig = shaply.beeswarm(explanation)
|
|
94
|
+
fig.show()
|
|
95
|
+
|
|
96
|
+
fig = shaply.bar(explanation)
|
|
97
|
+
fig = shaply.waterfall(explanation, sample_index=0)
|
|
98
|
+
fig = shaply.scatter(explanation, feature="income", color_feature="age")
|
|
99
|
+
fig = shaply.heatmap(explanation)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Every function takes an optional typed config from `shaply.config`:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from shaply.config import BeeswarmConfig
|
|
106
|
+
from shaply.enums import ColorScale, FeatureOrdering
|
|
107
|
+
|
|
108
|
+
cfg = BeeswarmConfig(
|
|
109
|
+
max_display=15,
|
|
110
|
+
ordering=FeatureOrdering.IMPORTANCE,
|
|
111
|
+
color_scale=ColorScale.RED_BLUE,
|
|
112
|
+
)
|
|
113
|
+
fig = shaply.beeswarm(explanation, config=cfg)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Available plots
|
|
117
|
+
|
|
118
|
+
| Function | SHAP equivalent | Purpose |
|
|
119
|
+
| -------------------- | ------------------------ | ------------------------------------ |
|
|
120
|
+
| `shaply.bar` | `shap.plots.bar` | Global feature importance |
|
|
121
|
+
| `shaply.beeswarm` | `shap.plots.beeswarm` | Summary of per-sample contributions |
|
|
122
|
+
| `shaply.waterfall` | `shap.plots.waterfall` | Single-prediction explanation |
|
|
123
|
+
| `shaply.scatter` | `shap.plots.scatter` | Dependence plot |
|
|
124
|
+
| `shaply.heatmap` | `shap.plots.heatmap` | SHAP values across instances |
|
|
125
|
+
| `shaply.force` | `shap.plots.force` | Additive force layout (one instance) |
|
|
126
|
+
| `shaply.decision` | `shap.decision_plot` | Cumulative decision paths |
|
|
127
|
+
|
|
128
|
+
## Advanced tools - beyond the usual SHAP plots
|
|
129
|
+
|
|
130
|
+
These are `shaply`-only figures aimed at engineers and business-facing data scientists who want to *act* on SHAP, not just explain a model. They cross SHAP values with the real data to surface operating ranges, tipping points, coupled effects and failure drivers.
|
|
131
|
+
|
|
132
|
+
> **Read them as associational, not causal.** SHAP measures a feature's
|
|
133
|
+
> contribution to the *model's* output, not to reality. Wording is deliberately
|
|
134
|
+
> cautious ("associated with", "tipping point of the model") - a strong signal
|
|
135
|
+
> here is a lead to investigate, not a proven cause.
|
|
136
|
+
|
|
137
|
+
| Function | What it shows | Insight |
|
|
138
|
+
| --------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
139
|
+
| `shaply.beeswarm_ranges` | Beeswarm**+** real value distribution (violin + box, true min/max) per feature | Read impact*and* concrete operating range on the same line |
|
|
140
|
+
| `shaply.response_curve` | Smoothed mean SHAP vs a feature's value, with a ±1 std band and auto-detected zero-crossings | The **tipping point** where a feature flips from lowering to raising the output |
|
|
141
|
+
| `shaply.interaction_heatmap` | Matrix of mean\|SHAP interaction\| between feature pairs | Which features**act together** (coupled effects), diagonal hidden by default |
|
|
142
|
+
| `shaply.error_analysis` | Mean SHAP per feature,**correct vs mis-predicted** cohorts, ranked by gap | What the model relies on differently**when it is wrong** |
|
|
143
|
+
| `shaply.shap_surface` | Mean SHAP of a feature over the 2D plane of two features | The**operating regions** where a feature helps or hurts, and how a second one modulates it |
|
|
144
|
+
| `shaply.importance_by_cohort` | `mean(\|SHAP\|)` per feature, split by cohort (explicit or quantile-binned) | A feature can**dominate in one regime** and be negligible in another |
|
|
145
|
+
| `shaply.feature_clustering` | Clustered heatmap of SHAP correlation between features | **Redundant** features (correlated SHAP) that could be dropped |
|
|
146
|
+
| `shaply.explanation_archetypes` | Mean SHAP profile of each k-means cluster of instances | The model's recurring**decision patterns / failure modes** |
|
|
147
|
+
| `shaply.importance_ci` | Global importance bars with**bootstrap confidence intervals** | Whether an importance ranking is**robust** or fragile |
|
|
148
|
+
| `shaply.monotonicity_check` | Spearman correlation between each feature's value and its SHAP | Clean**monotonic** effects vs suspicious non-monotonic ones (interaction/noise) |
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Beeswarm + real value ranges (needs feature values via data=...)
|
|
152
|
+
shaply.beeswarm_ranges(explanation).show()
|
|
153
|
+
|
|
154
|
+
# Response curve of one feature, with tipping-point detection
|
|
155
|
+
shaply.response_curve(explanation, "temperature").show()
|
|
156
|
+
|
|
157
|
+
# Pairwise interaction strength (needs SHAP *interaction* values)
|
|
158
|
+
inter = shap.TreeExplainer(model).shap_interaction_values(X) # (n, f, f) - pick a class if 4D
|
|
159
|
+
shaply.interaction_heatmap(inter, feature_names=list(X.columns)).show()
|
|
160
|
+
|
|
161
|
+
# What drives the model's mistakes
|
|
162
|
+
shaply.error_analysis(explanation, y_true=y_test, y_pred=model.predict(X_test)).show()
|
|
163
|
+
|
|
164
|
+
# 2D SHAP surface over a feature pair
|
|
165
|
+
shaply.shap_surface(explanation, "temperature", "pressure").show()
|
|
166
|
+
|
|
167
|
+
# Importance split by an operating regime (quantiles of another feature)
|
|
168
|
+
shaply.importance_by_cohort(explanation, by_feature="load").show()
|
|
169
|
+
|
|
170
|
+
# Redundant features (correlated SHAP), and typical decision patterns
|
|
171
|
+
shaply.feature_clustering(explanation).show()
|
|
172
|
+
shaply.explanation_archetypes(explanation).show()
|
|
173
|
+
|
|
174
|
+
# Robustness of the ranking, and monotonicity of each effect
|
|
175
|
+
shaply.importance_ci(explanation).show()
|
|
176
|
+
shaply.monotonicity_check(explanation).show()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Each takes a typed config from `shaply.config` (e.g. `ResponseCurveConfig`,
|
|
180
|
+
`ShapSurfaceConfig`, `ImportanceByCohortConfig`, `FeatureClusteringConfig`,
|
|
181
|
+
`ExplanationArchetypesConfig`, `ImportanceCIConfig`, `MonotonicityConfig`).
|
|
182
|
+
|
|
183
|
+
The clustering and statistics behind these tools are implemented in pure NumPy,
|
|
184
|
+
so the advanced tools add **no runtime dependency** beyond `numpy`/`plotly`/`pydantic`.
|
|
185
|
+
|
|
186
|
+
## Example notebook
|
|
187
|
+
|
|
188
|
+
[`examples/shaply_demo.ipynb`](examples/shaply_demo.ipynb) is a full, executed walkthrough. It builds a **synthetic dataset** with `make_classification` (5 informative, 2 redundant and 3 pure-noise features), trains **five very different classifiers** - RandomForest, XGBoost, LightGBM, LogisticRegression and an RBF SVM - computes SHAP values for each (`TreeExplainer`, `LinearExplainer`, `KernelExplainer`) and renders **every `shaply` figure** for all of them, plus the advanced tools (response curve, interaction heatmap, error analysis) and a cross-model importance comparison.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
uv sync --extra examples
|
|
192
|
+
uv run jupyter lab examples/shaply_demo.ipynb
|
|
193
|
+
# regenerate the executed outputs from scratch:
|
|
194
|
+
uv run jupyter nbconvert --to notebook --execute --inplace examples/shaply_demo.ipynb
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
uv sync
|
|
201
|
+
uv run ruff check . --fix
|
|
202
|
+
uv run ruff format .
|
|
203
|
+
uv run mypy
|
|
204
|
+
uv run pytest
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The package ships a `py.typed` marker, so all type annotations are available to downstream users.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
shaply-1.0.0/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# shaply
|
|
2
|
+
|
|
3
|
+
**Usual SHAP explainability figures, rendered as interactive [Plotly](https://plotly.com/python/) charts.**
|
|
4
|
+
|
|
5
|
+
`shaply` reproduces the familiar figures from the [`shap`](https://github.com/shap/shap) library - bar, beeswarm, waterfall, dependence (scatter) and heatmap - but returns `plotly.graph_objects.Figure` objects instead of matplotlib axes, so the plots are interactive and embeddable out of the box.
|
|
6
|
+
|
|
7
|
+
It does **not** depend on `shap`: every plotting function accepts a `shap.Explanation`-like object, a raw NumPy array of SHAP values, or a pandas `DataFrame`.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv add shaply
|
|
13
|
+
# or
|
|
14
|
+
pip install shaply
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Optional dependencies (extras)
|
|
18
|
+
|
|
19
|
+
`shaply` itself only needs `numpy`, `plotly` and `pydantic`. Extra features and
|
|
20
|
+
the example notebook pull in heavier packages, grouped as installable extras:
|
|
21
|
+
|
|
22
|
+
| Extra | Installs | Purpose |
|
|
23
|
+
| ------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
24
|
+
| `pandas` | `pandas` | Pass SHAP values as a`DataFrame` (column names become feature names) |
|
|
25
|
+
| `examples` | `scikit-learn`, `xgboost`, `lightgbm`, `shap`, `pandas`, `ipykernel`, `nbformat` | Everything needed to run[`examples/shaply_demo.ipynb`](examples/shaply_demo.ipynb) |
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv add "shaply[pandas]" # DataFrame support
|
|
29
|
+
uv add "shaply[examples]" # run the demo notebook
|
|
30
|
+
# or with pip
|
|
31
|
+
pip install "shaply[examples]"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Building the wheel from source (use the package without PyPI)
|
|
35
|
+
|
|
36
|
+
To build and install `shaply` straight from a clone of this repository, without going through PyPI:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/antoine126/shaply.git
|
|
40
|
+
cd shaply
|
|
41
|
+
uv build --wheel # produces dist/shaply-<version>-py3-none-any.whl
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then install the wheel wherever you need it:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv add /path/to/shaply/dist/shaply-<version>-py3-none-any.whl
|
|
48
|
+
# or with pip, in any environment
|
|
49
|
+
pip install /path/to/shaply/dist/shaply-<version>-py3-none-any.whl
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The wheel is self-contained (it ships `py.typed`, so type annotations reach the installed package) and does not require `uv` or the source tree at runtime.
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import shaply
|
|
58
|
+
|
|
59
|
+
# `explanation` can be a shap.Explanation, an ndarray, or a DataFrame
|
|
60
|
+
fig = shaply.beeswarm(explanation)
|
|
61
|
+
fig.show()
|
|
62
|
+
|
|
63
|
+
fig = shaply.bar(explanation)
|
|
64
|
+
fig = shaply.waterfall(explanation, sample_index=0)
|
|
65
|
+
fig = shaply.scatter(explanation, feature="income", color_feature="age")
|
|
66
|
+
fig = shaply.heatmap(explanation)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Every function takes an optional typed config from `shaply.config`:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from shaply.config import BeeswarmConfig
|
|
73
|
+
from shaply.enums import ColorScale, FeatureOrdering
|
|
74
|
+
|
|
75
|
+
cfg = BeeswarmConfig(
|
|
76
|
+
max_display=15,
|
|
77
|
+
ordering=FeatureOrdering.IMPORTANCE,
|
|
78
|
+
color_scale=ColorScale.RED_BLUE,
|
|
79
|
+
)
|
|
80
|
+
fig = shaply.beeswarm(explanation, config=cfg)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Available plots
|
|
84
|
+
|
|
85
|
+
| Function | SHAP equivalent | Purpose |
|
|
86
|
+
| -------------------- | ------------------------ | ------------------------------------ |
|
|
87
|
+
| `shaply.bar` | `shap.plots.bar` | Global feature importance |
|
|
88
|
+
| `shaply.beeswarm` | `shap.plots.beeswarm` | Summary of per-sample contributions |
|
|
89
|
+
| `shaply.waterfall` | `shap.plots.waterfall` | Single-prediction explanation |
|
|
90
|
+
| `shaply.scatter` | `shap.plots.scatter` | Dependence plot |
|
|
91
|
+
| `shaply.heatmap` | `shap.plots.heatmap` | SHAP values across instances |
|
|
92
|
+
| `shaply.force` | `shap.plots.force` | Additive force layout (one instance) |
|
|
93
|
+
| `shaply.decision` | `shap.decision_plot` | Cumulative decision paths |
|
|
94
|
+
|
|
95
|
+
## Advanced tools - beyond the usual SHAP plots
|
|
96
|
+
|
|
97
|
+
These are `shaply`-only figures aimed at engineers and business-facing data scientists who want to *act* on SHAP, not just explain a model. They cross SHAP values with the real data to surface operating ranges, tipping points, coupled effects and failure drivers.
|
|
98
|
+
|
|
99
|
+
> **Read them as associational, not causal.** SHAP measures a feature's
|
|
100
|
+
> contribution to the *model's* output, not to reality. Wording is deliberately
|
|
101
|
+
> cautious ("associated with", "tipping point of the model") - a strong signal
|
|
102
|
+
> here is a lead to investigate, not a proven cause.
|
|
103
|
+
|
|
104
|
+
| Function | What it shows | Insight |
|
|
105
|
+
| --------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
106
|
+
| `shaply.beeswarm_ranges` | Beeswarm**+** real value distribution (violin + box, true min/max) per feature | Read impact*and* concrete operating range on the same line |
|
|
107
|
+
| `shaply.response_curve` | Smoothed mean SHAP vs a feature's value, with a ±1 std band and auto-detected zero-crossings | The **tipping point** where a feature flips from lowering to raising the output |
|
|
108
|
+
| `shaply.interaction_heatmap` | Matrix of mean\|SHAP interaction\| between feature pairs | Which features**act together** (coupled effects), diagonal hidden by default |
|
|
109
|
+
| `shaply.error_analysis` | Mean SHAP per feature,**correct vs mis-predicted** cohorts, ranked by gap | What the model relies on differently**when it is wrong** |
|
|
110
|
+
| `shaply.shap_surface` | Mean SHAP of a feature over the 2D plane of two features | The**operating regions** where a feature helps or hurts, and how a second one modulates it |
|
|
111
|
+
| `shaply.importance_by_cohort` | `mean(\|SHAP\|)` per feature, split by cohort (explicit or quantile-binned) | A feature can**dominate in one regime** and be negligible in another |
|
|
112
|
+
| `shaply.feature_clustering` | Clustered heatmap of SHAP correlation between features | **Redundant** features (correlated SHAP) that could be dropped |
|
|
113
|
+
| `shaply.explanation_archetypes` | Mean SHAP profile of each k-means cluster of instances | The model's recurring**decision patterns / failure modes** |
|
|
114
|
+
| `shaply.importance_ci` | Global importance bars with**bootstrap confidence intervals** | Whether an importance ranking is**robust** or fragile |
|
|
115
|
+
| `shaply.monotonicity_check` | Spearman correlation between each feature's value and its SHAP | Clean**monotonic** effects vs suspicious non-monotonic ones (interaction/noise) |
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Beeswarm + real value ranges (needs feature values via data=...)
|
|
119
|
+
shaply.beeswarm_ranges(explanation).show()
|
|
120
|
+
|
|
121
|
+
# Response curve of one feature, with tipping-point detection
|
|
122
|
+
shaply.response_curve(explanation, "temperature").show()
|
|
123
|
+
|
|
124
|
+
# Pairwise interaction strength (needs SHAP *interaction* values)
|
|
125
|
+
inter = shap.TreeExplainer(model).shap_interaction_values(X) # (n, f, f) - pick a class if 4D
|
|
126
|
+
shaply.interaction_heatmap(inter, feature_names=list(X.columns)).show()
|
|
127
|
+
|
|
128
|
+
# What drives the model's mistakes
|
|
129
|
+
shaply.error_analysis(explanation, y_true=y_test, y_pred=model.predict(X_test)).show()
|
|
130
|
+
|
|
131
|
+
# 2D SHAP surface over a feature pair
|
|
132
|
+
shaply.shap_surface(explanation, "temperature", "pressure").show()
|
|
133
|
+
|
|
134
|
+
# Importance split by an operating regime (quantiles of another feature)
|
|
135
|
+
shaply.importance_by_cohort(explanation, by_feature="load").show()
|
|
136
|
+
|
|
137
|
+
# Redundant features (correlated SHAP), and typical decision patterns
|
|
138
|
+
shaply.feature_clustering(explanation).show()
|
|
139
|
+
shaply.explanation_archetypes(explanation).show()
|
|
140
|
+
|
|
141
|
+
# Robustness of the ranking, and monotonicity of each effect
|
|
142
|
+
shaply.importance_ci(explanation).show()
|
|
143
|
+
shaply.monotonicity_check(explanation).show()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Each takes a typed config from `shaply.config` (e.g. `ResponseCurveConfig`,
|
|
147
|
+
`ShapSurfaceConfig`, `ImportanceByCohortConfig`, `FeatureClusteringConfig`,
|
|
148
|
+
`ExplanationArchetypesConfig`, `ImportanceCIConfig`, `MonotonicityConfig`).
|
|
149
|
+
|
|
150
|
+
The clustering and statistics behind these tools are implemented in pure NumPy,
|
|
151
|
+
so the advanced tools add **no runtime dependency** beyond `numpy`/`plotly`/`pydantic`.
|
|
152
|
+
|
|
153
|
+
## Example notebook
|
|
154
|
+
|
|
155
|
+
[`examples/shaply_demo.ipynb`](examples/shaply_demo.ipynb) is a full, executed walkthrough. It builds a **synthetic dataset** with `make_classification` (5 informative, 2 redundant and 3 pure-noise features), trains **five very different classifiers** - RandomForest, XGBoost, LightGBM, LogisticRegression and an RBF SVM - computes SHAP values for each (`TreeExplainer`, `LinearExplainer`, `KernelExplainer`) and renders **every `shaply` figure** for all of them, plus the advanced tools (response curve, interaction heatmap, error analysis) and a cross-model importance comparison.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync --extra examples
|
|
159
|
+
uv run jupyter lab examples/shaply_demo.ipynb
|
|
160
|
+
# regenerate the executed outputs from scratch:
|
|
161
|
+
uv run jupyter nbconvert --to notebook --execute --inplace examples/shaply_demo.ipynb
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
uv sync
|
|
168
|
+
uv run ruff check . --fix
|
|
169
|
+
uv run ruff format .
|
|
170
|
+
uv run mypy
|
|
171
|
+
uv run pytest
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The package ships a `py.typed` marker, so all type annotations are available to downstream users.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "shaply"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Usual SHAP explainability figures rendered as interactive Plotly charts."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "antoine126", email = "antoine.pagneux@neuf.fr" },
|
|
9
|
+
]
|
|
10
|
+
keywords = ["shap", "shapley", "plotly", "explainability", "xai", "machine-learning"]
|
|
11
|
+
requires-python = ">=3.12.7"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
19
|
+
"Typing :: Typed",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"numpy>=1.26",
|
|
23
|
+
"plotly>=5.20",
|
|
24
|
+
"pydantic>=2.6",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
pandas = ["pandas>=2.0"]
|
|
29
|
+
|
|
30
|
+
# Everything needed to run the example notebook in `examples/`.
|
|
31
|
+
examples = [
|
|
32
|
+
"pandas>=2.0",
|
|
33
|
+
"scikit-learn>=1.4",
|
|
34
|
+
"xgboost>=2.0",
|
|
35
|
+
"lightgbm>=4.0",
|
|
36
|
+
"shap>=0.45",
|
|
37
|
+
"ipykernel>=6.29",
|
|
38
|
+
"nbformat>=5.9",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/antoine126/shaply"
|
|
43
|
+
Repository = "https://github.com/antoine126/shaply"
|
|
44
|
+
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"mypy>=1.10",
|
|
48
|
+
"nbconvert>=7.16",
|
|
49
|
+
"pandas>=2.0",
|
|
50
|
+
"pandas-stubs>=2.0",
|
|
51
|
+
"pytest>=8.2",
|
|
52
|
+
"pytest-cov>=5.0",
|
|
53
|
+
"ruff>=0.5",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[build-system]
|
|
57
|
+
requires = ["uv_build>=0.9.8,<0.10.0"]
|
|
58
|
+
build-backend = "uv_build"
|
|
59
|
+
|
|
60
|
+
[tool.uv.build-backend]
|
|
61
|
+
module-name = "shaply"
|
|
62
|
+
module-root = "src"
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------------------------------------
|
|
65
|
+
# Ruff
|
|
66
|
+
# ------------------------------------------------------------------------------
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
line-length = 100
|
|
69
|
+
target-version = "py312"
|
|
70
|
+
src = ["src", "tests"]
|
|
71
|
+
extend-exclude = ["examples"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
select = [
|
|
75
|
+
"E", # pycodestyle errors
|
|
76
|
+
"W", # pycodestyle warnings
|
|
77
|
+
"F", # pyflakes
|
|
78
|
+
"I", # isort
|
|
79
|
+
"N", # pep8-naming
|
|
80
|
+
"UP", # pyupgrade
|
|
81
|
+
"B", # flake8-bugbear
|
|
82
|
+
"C4", # flake8-comprehensions
|
|
83
|
+
"SIM", # flake8-simplify
|
|
84
|
+
"TC", # flake8-type-checking
|
|
85
|
+
"PTH", # flake8-use-pathlib
|
|
86
|
+
"RUF", # ruff-specific rules
|
|
87
|
+
"ANN", # flake8-annotations
|
|
88
|
+
"D", # pydocstyle
|
|
89
|
+
]
|
|
90
|
+
ignore = [
|
|
91
|
+
"D105", # missing docstring in magic method
|
|
92
|
+
"D107", # missing docstring in __init__
|
|
93
|
+
"ANN401", # dynamically typed *args/**kwargs allowed where needed
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[tool.ruff.lint.per-file-ignores]
|
|
97
|
+
"tests/*" = ["D100", "D101", "D102", "D103", "D104", "ANN", "TC001", "TC002", "TC003"]
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint.pydocstyle]
|
|
100
|
+
convention = "numpy"
|
|
101
|
+
|
|
102
|
+
[tool.ruff.lint.isort]
|
|
103
|
+
known-first-party = ["shaply"]
|
|
104
|
+
|
|
105
|
+
# ------------------------------------------------------------------------------
|
|
106
|
+
# mypy
|
|
107
|
+
# ------------------------------------------------------------------------------
|
|
108
|
+
[tool.mypy]
|
|
109
|
+
python_version = "3.12"
|
|
110
|
+
strict = true
|
|
111
|
+
warn_unreachable = true
|
|
112
|
+
warn_redundant_casts = true
|
|
113
|
+
plugins = ["pydantic.mypy"]
|
|
114
|
+
files = ["src", "tests"]
|
|
115
|
+
|
|
116
|
+
[[tool.mypy.overrides]]
|
|
117
|
+
module = ["plotly.*"]
|
|
118
|
+
ignore_missing_imports = true
|
|
119
|
+
|
|
120
|
+
# ------------------------------------------------------------------------------
|
|
121
|
+
# pytest
|
|
122
|
+
# ------------------------------------------------------------------------------
|
|
123
|
+
[tool.pytest.ini_options]
|
|
124
|
+
minversion = "8.0"
|
|
125
|
+
addopts = "-ra --strict-markers --cov=shaply --cov-report=term-missing"
|
|
126
|
+
testpaths = ["tests"]
|
|
127
|
+
|
|
128
|
+
# ------------------------------------------------------------------------------
|
|
129
|
+
# coverage
|
|
130
|
+
# ------------------------------------------------------------------------------
|
|
131
|
+
[tool.coverage.report]
|
|
132
|
+
exclude_lines = [
|
|
133
|
+
"pragma: no cover",
|
|
134
|
+
"if TYPE_CHECKING:",
|
|
135
|
+
"raise NotImplementedError",
|
|
136
|
+
"@overload",
|
|
137
|
+
]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""shaply - usual SHAP explainability figures rendered as Plotly charts.
|
|
2
|
+
|
|
3
|
+
The public API mirrors the familiar ``shap.plots`` entry points but returns
|
|
4
|
+
:class:`plotly.graph_objects.Figure` objects instead of matplotlib axes::
|
|
5
|
+
|
|
6
|
+
import shaply
|
|
7
|
+
|
|
8
|
+
fig = shaply.beeswarm(shap_values) # a shap.Explanation, ndarray or DataFrame
|
|
9
|
+
fig.show()
|
|
10
|
+
|
|
11
|
+
Every plotting function accepts a ``shap.Explanation``-like object, a numpy
|
|
12
|
+
array of SHAP values, or a :class:`pandas.DataFrame`, and an optional typed
|
|
13
|
+
config object from :mod:`shaply.config`.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
19
|
+
|
|
20
|
+
from shaply.config import (
|
|
21
|
+
BarConfig,
|
|
22
|
+
BeeswarmConfig,
|
|
23
|
+
BeeswarmRangesConfig,
|
|
24
|
+
DecisionConfig,
|
|
25
|
+
ErrorAnalysisConfig,
|
|
26
|
+
ExplanationArchetypesConfig,
|
|
27
|
+
FeatureClusteringConfig,
|
|
28
|
+
ForceConfig,
|
|
29
|
+
HeatmapConfig,
|
|
30
|
+
ImportanceByCohortConfig,
|
|
31
|
+
ImportanceCIConfig,
|
|
32
|
+
InteractionHeatmapConfig,
|
|
33
|
+
MonotonicityConfig,
|
|
34
|
+
ResponseCurveConfig,
|
|
35
|
+
ScatterConfig,
|
|
36
|
+
ShapSurfaceConfig,
|
|
37
|
+
WaterfallConfig,
|
|
38
|
+
)
|
|
39
|
+
from shaply.enums import ColorScale, FeatureOrdering, PlotType
|
|
40
|
+
from shaply.explanation import Explanation, to_explanation
|
|
41
|
+
from shaply.interaction import InteractionValues, to_interaction_values
|
|
42
|
+
from shaply.plots import (
|
|
43
|
+
bar,
|
|
44
|
+
beeswarm,
|
|
45
|
+
beeswarm_ranges,
|
|
46
|
+
decision,
|
|
47
|
+
error_analysis,
|
|
48
|
+
explanation_archetypes,
|
|
49
|
+
feature_clustering,
|
|
50
|
+
force,
|
|
51
|
+
heatmap,
|
|
52
|
+
importance_by_cohort,
|
|
53
|
+
importance_ci,
|
|
54
|
+
interaction_heatmap,
|
|
55
|
+
monotonicity_check,
|
|
56
|
+
response_curve,
|
|
57
|
+
scatter,
|
|
58
|
+
shap_surface,
|
|
59
|
+
waterfall,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
__version__ = version("shaply")
|
|
64
|
+
except PackageNotFoundError: # pragma: no cover - only during local dev without install
|
|
65
|
+
__version__ = "0.0.0"
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"BarConfig",
|
|
69
|
+
"BeeswarmConfig",
|
|
70
|
+
"BeeswarmRangesConfig",
|
|
71
|
+
"ColorScale",
|
|
72
|
+
"DecisionConfig",
|
|
73
|
+
"ErrorAnalysisConfig",
|
|
74
|
+
"Explanation",
|
|
75
|
+
"ExplanationArchetypesConfig",
|
|
76
|
+
"FeatureClusteringConfig",
|
|
77
|
+
"FeatureOrdering",
|
|
78
|
+
"ForceConfig",
|
|
79
|
+
"HeatmapConfig",
|
|
80
|
+
"ImportanceByCohortConfig",
|
|
81
|
+
"ImportanceCIConfig",
|
|
82
|
+
"InteractionHeatmapConfig",
|
|
83
|
+
"InteractionValues",
|
|
84
|
+
"MonotonicityConfig",
|
|
85
|
+
"PlotType",
|
|
86
|
+
"ResponseCurveConfig",
|
|
87
|
+
"ScatterConfig",
|
|
88
|
+
"ShapSurfaceConfig",
|
|
89
|
+
"WaterfallConfig",
|
|
90
|
+
"__version__",
|
|
91
|
+
"bar",
|
|
92
|
+
"beeswarm",
|
|
93
|
+
"beeswarm_ranges",
|
|
94
|
+
"decision",
|
|
95
|
+
"error_analysis",
|
|
96
|
+
"explanation_archetypes",
|
|
97
|
+
"feature_clustering",
|
|
98
|
+
"force",
|
|
99
|
+
"heatmap",
|
|
100
|
+
"importance_by_cohort",
|
|
101
|
+
"importance_ci",
|
|
102
|
+
"interaction_heatmap",
|
|
103
|
+
"monotonicity_check",
|
|
104
|
+
"response_curve",
|
|
105
|
+
"scatter",
|
|
106
|
+
"shap_surface",
|
|
107
|
+
"to_explanation",
|
|
108
|
+
"to_interaction_values",
|
|
109
|
+
"waterfall",
|
|
110
|
+
]
|