shap-svg 0.2.3 → 0.3.0

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.
package/README.md CHANGED
@@ -1,12 +1,39 @@
1
1
  # shap-svg
2
2
 
3
- Interactive SVG charts for [SHAP](https://github.com/shap/shap) explanations — **bar, beeswarm,
4
- heatmap and waterfall** — drawn in the browser from SHAP values instead of shipped as rendered images.
3
+ `shap-svg` is a browser rendering layer for the [`shap.plots`](https://shap.readthedocs.io/en/latest/api.html#plots)
4
+ API. It reimplements eight SHAP plots as interactive SVG charts, exposed as React components through
5
+ `shap-svg/react`, so web applications can present SHAP explanations without server-side matplotlib
6
+ rendering.
7
+
8
+ It is a rendering library only. SHAP values must still be computed with the
9
+ [`shap`](https://github.com/shap/shap) Python package and delivered to the client as JSON.
5
10
 
6
11
  ```bash
7
12
  npm install shap-svg
8
13
  ```
9
14
 
15
+ ## In action
16
+
17
+ Recorded in the
18
+ [Explainable AI Microbiome Platform](https://github.com/princepongsakorn/explainable-ai-microbiome-platform),
19
+ which draws its SHAP charts with `shap-svg`.
20
+
21
+ ### `Plots.bar`
22
+
23
+ ![Bar chart of mean absolute SHAP value per taxon](https://raw.githubusercontent.com/princepongsakorn/shap-svg/main/docs/assets/bar.gif)
24
+
25
+ ### `Plots.beeswarm`
26
+
27
+ ![Beeswarm chart with one dot per sample per taxon, coloured by relative abundance](https://raw.githubusercontent.com/princepongsakorn/shap-svg/main/docs/assets/beeswarm.gif)
28
+
29
+ ### `Plots.heatmap`
30
+
31
+ ![Heatmap of samples by taxa coloured by SHAP value, with the model output line above](https://raw.githubusercontent.com/princepongsakorn/shap-svg/main/docs/assets/heatmap.gif)
32
+
33
+ ### `Plots.waterfall`
34
+
35
+ ![Waterfall chart building one sample's prediction from the average prediction](https://raw.githubusercontent.com/princepongsakorn/shap-svg/main/docs/assets/waterfall.gif)
36
+
10
37
  ## Why this exists, and why the name
11
38
 
12
39
  SHAP draws its plots with matplotlib, on the machine that computed the values. A web application
@@ -26,10 +53,14 @@ affiliated with the SHAP authors.
26
53
 
27
54
  | Component | SHAP counterpart | Shows |
28
55
  | --- | --- | --- |
29
- | `Plots.bar` | `shap.plots.bar` | mean(\|SHAP value\|) per feature across samples |
30
- | `Plots.beeswarm` | `shap.plots.beeswarm` | one dot per sample per feature, coloured by feature value |
31
- | `Plots.heatmap` | `shap.plots.heatmap` | samples × features coloured by SHAP value, with the f(x) line above |
32
- | `Plots.waterfall` | `shap.plots.waterfall` | how one sample's prediction is built from E[f(X)] to f(x) |
56
+ | [`Plots.bar`](https://github.com/princepongsakorn/shap-svg/blob/main/docs/BAR.md) | `shap.plots.bar` | mean(\|SHAP value\|) per Feature across Samples |
57
+ | `Plots.beeswarm` | `shap.plots.beeswarm` | one dot per Sample per Feature, coloured by Feature value |
58
+ | `Plots.heatmap` | `shap.plots.heatmap` | Samples × Features coloured by SHAP value, with the Model output line above |
59
+ | `Plots.waterfall` | `shap.plots.waterfall` | how one Sample's prediction is built from the Base value to the Model output |
60
+ | [`Plots.scatter`](https://github.com/princepongsakorn/shap-svg/blob/main/docs/SCATTER.md) | `shap.plots.scatter` | one Feature's value against its SHAP value across Samples |
61
+ | [`Plots.embedding`](https://github.com/princepongsakorn/shap-svg/blob/main/docs/EMBEDDING.md) | `shap.plots.embedding` | Samples positioned by similarity between their SHAP value vectors |
62
+ | [`Plots.decision`](https://github.com/princepongsakorn/shap-svg/blob/main/docs/DECISION.md) | `shap.plots.decision` | cumulative Feature effects from the Base value to each Sample's Model output |
63
+ | [`Plots.force`](https://github.com/princepongsakorn/shap-svg/blob/main/docs/FORCE.md) | `shap.plots.force` | one Sample's positive and negative Feature effects in a compact row |
33
64
 
34
65
  Every chart is a pure component: all state that changes what is drawn arrives through props, so the
35
66
  host application owns its own controls. The only internal state is hover highlighting.
@@ -122,7 +153,20 @@ export function ExplanationView() {
122
153
  ```
123
154
 
124
155
  The charts are named the way `shap` names them in Python: `shap.plots.bar` becomes `<Plots.bar />`.
125
- `Plots` brings all four charts into your bundle, even if a page draws one — about 30 KB minified.
156
+ `Plots` contains all eight charts:
157
+
158
+ ```ts
159
+ Plots.bar;
160
+ Plots.beeswarm;
161
+ Plots.heatmap;
162
+ Plots.waterfall;
163
+ Plots.scatter;
164
+ Plots.embedding;
165
+ Plots.decision;
166
+ Plots.force;
167
+ ```
168
+
169
+ One `Plots` import brings all eight into your bundle even if a page draws one.
126
170
 
127
171
  From here every control — how many features, grouping, sorting, precision — is a prop. Changing one
128
172
  redraws from the payload already in memory; nothing goes back to the server.
@@ -161,30 +205,68 @@ Field names follow `shap.Explanation`, so a Python service can serialise one dir
161
205
 
162
206
  ## Props
163
207
 
164
- Shared by all four charts:
208
+ Shared by all eight charts:
165
209
 
166
210
  | Prop | Default | |
167
211
  | --- | --- | --- |
168
212
  | `explanation` | — | the payload above |
169
- | `maxDisplay` | `10` | features shown before the rest collapse into one "other features" row |
170
- | `faithfulOtherRow` | `false` | `true` reproduces SHAP's own behaviour, where the last displayed row absorbs the feature ranked `maxDisplay` |
171
213
  | `groupByGenus` | `false` | sum `Genus_species` features into their genus first |
172
214
  | `classIndex` | `1` | which output to draw for multi-output explanations |
173
- | `width` | `720` | SVG width in pixels |
174
- | `rowHeight` | per chart | pixels per feature row |
215
+ | `width` | per chart | SVG width in pixels |
175
216
  | `labels` | SHAP's wording | text the chart draws; see [Wording](#wording) |
176
- | `onFeatureClick` | — | called with the feature index, or `null` for the "other" row |
177
217
 
178
218
  Per chart:
179
219
 
180
220
  | Chart | Prop | Default | |
181
221
  | --- | --- | --- | --- |
222
+ | `Plots.bar`, `Plots.beeswarm`, `Plots.heatmap`, `Plots.waterfall`, `Plots.force` | `maxDisplay` | `10` | Features shown before the rest collapse into an Other features row |
223
+ | `Plots.decision` | `maxDisplay` | `15` | maximum number of Feature rows |
224
+ | `Plots.bar`, `Plots.beeswarm`, `Plots.heatmap`, `Plots.waterfall`, `Plots.force` | `faithfulOtherRow` | `false` | reproduce SHAP's boundary-row collapse when `true` |
225
+ | `Plots.bar`, `Plots.heatmap`, `Plots.decision` | `rowHeight` | `26` | pixels per Feature row |
226
+ | `Plots.beeswarm` | `rowHeight` | `28` | pixels per Feature row |
227
+ | `Plots.waterfall` | `rowHeight` | `30` | pixels per Feature row |
182
228
  | `Plots.beeswarm`, `Plots.heatmap` | `rowSort` | `"importance"` | `"importance"`, `"name"` or `"featureValue"`; reorders the rows shown, never which rows are shown |
183
229
  | `Plots.beeswarm`, `Plots.heatmap` | `colorBar` | `true` | SHAP's colour bar right of the plot — Low to High feature value on the beeswarm, the SHAP value range on the heatmap. The plot narrows when the right margin cannot hold it |
184
230
  | `Plots.beeswarm` | `seed`, `dotRadius` | `0`, `3` | jitter is seeded, so a chart is identical on every render |
185
231
  | `Plots.heatmap` | `onSampleClick` | — | called with the column's `sample_ids` entry |
186
232
  | `Plots.waterfall` | `sampleIndex` | `0` | which sample to explain |
187
233
  | `Plots.waterfall` | `decimals` | `2` | `2`, `3`, `4` or `"percent"`; display only |
234
+ | `Plots.scatter` | `feature` | — | required Feature name or zero-based index |
235
+ | `Plots.scatter` | `colorFeature`, `colorFeatureMinScore` | `"auto"`, `0.2` | colour Feature selection and minimum automatic interaction score |
236
+ | `Plots.scatter` | `xScale`, `trend` | `"log"`, `true` | Feature-value scale and binned-median trend |
237
+ | `Plots.embedding` | `colorBy`, `coords` | `"sum"`, — | colour source and optional external Sample coordinates |
238
+ | `Plots.decision` | `sampleIndices` | every Sample | zero-based Sample indices to draw |
239
+ | `Plots.scatter`, `Plots.embedding`, `Plots.decision` | `colormap` | `"red_blue"` | `"red_blue"` or `"red_white_blue"` |
240
+ | `Plots.scatter`, `Plots.embedding`, `Plots.decision`, `Plots.force` | `tableView` | `"hidden"` | `"hidden"`, `"visible"`, or `"none"` |
241
+
242
+ The complete props and examples for the new charts are in [Scatter](https://github.com/princepongsakorn/shap-svg/blob/main/docs/SCATTER.md),
243
+ [Embedding](https://github.com/princepongsakorn/shap-svg/blob/main/docs/EMBEDDING.md), [Decision](https://github.com/princepongsakorn/shap-svg/blob/main/docs/DECISION.md), and [Force](https://github.com/princepongsakorn/shap-svg/blob/main/docs/FORCE.md).
244
+
245
+ ## Beyond SHAP
246
+
247
+ The browser charts add six deliberate improvements to the behaviour in SHAP's Python plotters:
248
+
249
+ - **A neutral-midpoint colormap option.** The `red_blue` table used by `_scatter.py` and
250
+ `_embedding.py` has its darkest step at the midpoint: OKLab lightness 0.512, against 0.636 and
251
+ 0.635 at the poles. That gives a Feature that contributed nothing the greatest visual weight.
252
+ `red_white_blue` has the correct lightness shape and is available through `colormap`; `red_blue`
253
+ remains the default for fidelity.
254
+ - **A symmetric decision-plot axis.** `_decision.py` carries a comment promising a symmetric axis
255
+ above a branch that does not always deliver one. Because its colour scale is clamped to those
256
+ limits, the asymmetry moves the neutral colour away from the Base value. `Plots.decision` keeps
257
+ equal reach on both sides.
258
+ - **The interaction score is shown.** `_scatter.py` takes the first result from
259
+ `approximate_interactions` without asking how strong it is, so colour chosen from noise looks like
260
+ colour chosen from a real interaction. `Plots.scatter` shows the score normalised into 0–1 and
261
+ declines to colour below `colorFeatureMinScore`.
262
+ - **The Genus view.** `groupByGenus` switches all four new charts from the Species view to the Genus
263
+ view before layout.
264
+ - **A dose–response trend line.** `Plots.scatter` draws a binned median over detected Samples only;
265
+ `_scatter.py` has no equivalent.
266
+ - **A table view.** All four new charts put their numbers in a real table by default, visually hidden
267
+ until `tableView="visible"`; unlike the matplotlib images produced by `_scatter.py`,
268
+ `_embedding.py`, `_decision.py`, and `_force_matplotlib.py`, the values are reachable by a screen
269
+ reader and text search.
188
270
 
189
271
  ## Wording
190
272