shap-svg 0.1.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/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/chunk-OXFKP5I3.js +1566 -0
- package/dist/chunk-OXFKP5I3.js.map +1 -0
- package/dist/format-wEav_cPe.d.cts +111 -0
- package/dist/format-wEav_cPe.d.ts +111 -0
- package/dist/index.cjs +1616 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +456 -0
- package/dist/index.d.ts +456 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +2323 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +66 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +776 -0
- package/dist/react.js.map +1 -0
- package/package.json +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pongsakorn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# shap-svg
|
|
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.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install shap-svg
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Why this exists, and why the name
|
|
11
|
+
|
|
12
|
+
SHAP draws its plots with matplotlib, on the machine that computed the values. A web application
|
|
13
|
+
that wants to show them usually ends up rendering PNGs on the server and sending pictures to the
|
|
14
|
+
browser: every change of view is another round trip, and nothing in the picture can be hovered,
|
|
15
|
+
clicked or resized.
|
|
16
|
+
|
|
17
|
+
`shap-svg` takes the other route. The server sends the **SHAP values** — the same arrays a
|
|
18
|
+
`shap.Explanation` holds — and the charts are drawn as SVG on the client. Changing how many features
|
|
19
|
+
are shown, grouping them, sorting them or expanding a chart costs no request, because the numbers are
|
|
20
|
+
already there.
|
|
21
|
+
|
|
22
|
+
The name says exactly that: **SHAP** values in, **SVG** out. It is an independent project and is not
|
|
23
|
+
affiliated with the SHAP authors.
|
|
24
|
+
|
|
25
|
+
## What it draws
|
|
26
|
+
|
|
27
|
+
| Component | SHAP counterpart | Shows |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `ShapBar` | `shap.plots.bar` | mean(\|SHAP value\|) per feature across samples |
|
|
30
|
+
| `ShapBeeswarm` | `shap.plots.beeswarm` | one dot per sample per feature, coloured by feature value |
|
|
31
|
+
| `ShapHeatmap` | `shap.plots.heatmap` | samples × features coloured by SHAP value, with the f(x) line above |
|
|
32
|
+
| `ShapWaterfall` | `shap.plots.waterfall` | how one sample's prediction is built from E[f(X)] to f(x) |
|
|
33
|
+
|
|
34
|
+
Every chart is a pure component: all state that changes what is drawn arrives through props, so the
|
|
35
|
+
host application owns its own controls. The only internal state is hover highlighting.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ShapBeeswarm, ShapWaterfall } from "shap-svg/react";
|
|
41
|
+
|
|
42
|
+
<ShapBeeswarm explanation={explanation} maxDisplay={15} groupByGenus rowSort="name" />
|
|
43
|
+
<ShapWaterfall explanation={explanation} sampleIndex={0} decimals="percent" />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The framework-free core — parsing, ordering, collapsing, layout and colour — has no React import and
|
|
47
|
+
can drive any renderer:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { parseExplanation, heatmapRows, heatmapLayout } from "shap-svg";
|
|
51
|
+
|
|
52
|
+
const rows = heatmapRows(parseExplanation(payload), 15, false);
|
|
53
|
+
const layout = heatmapLayout(rows, { width: 720, rowHeight: 26, marginLeft: 260, marginRight: 100, marginTop: 60 });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
React (≥ 18) is an optional peer dependency, needed only for `shap-svg/react`.
|
|
57
|
+
|
|
58
|
+
## The explanation payload
|
|
59
|
+
|
|
60
|
+
Field names follow `shap.Explanation`, so a Python service can serialise one directly:
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
{
|
|
64
|
+
"contract_version": 1,
|
|
65
|
+
"values": [[0.012, -0.004]], // n samples × p features (or n × p × classes)
|
|
66
|
+
"base_values": [0.5238], // per sample; a scalar is accepted and repeated
|
|
67
|
+
"data": [[0.041, 0.0]], // n × p feature values
|
|
68
|
+
"feature_names": ["Bacteroides_dorei", "Parvimonas_micra"],
|
|
69
|
+
"sample_ids": ["3f30aca9-…"], // optional: stable keys for click-through
|
|
70
|
+
"sample_labels": ["SAMD00114722"], // optional: what a person reads; never used as a key
|
|
71
|
+
"sample_label_column": "sample_id", // optional: where the labels came from
|
|
72
|
+
"model_name": "crc-rynazal-notebook" // optional
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`parseExplanation` validates shapes and rejects non-finite numbers before anything is drawn.
|
|
77
|
+
|
|
78
|
+
## Props
|
|
79
|
+
|
|
80
|
+
Shared by all four charts:
|
|
81
|
+
|
|
82
|
+
| Prop | Default | |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `explanation` | — | the payload above |
|
|
85
|
+
| `maxDisplay` | `10` | features shown before the rest collapse into one "other features" row |
|
|
86
|
+
| `faithfulOtherRow` | `false` | `true` reproduces SHAP's own behaviour, where the last displayed row absorbs the feature ranked `maxDisplay` |
|
|
87
|
+
| `groupByGenus` | `false` | sum `Genus_species` features into their genus first |
|
|
88
|
+
| `classIndex` | `1` | which output to draw for multi-output explanations |
|
|
89
|
+
| `width` | `720` | SVG width in pixels |
|
|
90
|
+
| `rowHeight` | per chart | pixels per feature row |
|
|
91
|
+
| `onFeatureClick` | — | called with the feature index, or `null` for the "other" row |
|
|
92
|
+
|
|
93
|
+
Per chart:
|
|
94
|
+
|
|
95
|
+
| Chart | Prop | Default | |
|
|
96
|
+
| --- | --- | --- | --- |
|
|
97
|
+
| `ShapBeeswarm`, `ShapHeatmap` | `rowSort` | `"importance"` | `"importance"`, `"name"` or `"featureValue"`; reorders the rows shown, never which rows are shown |
|
|
98
|
+
| `ShapBeeswarm` | `seed`, `dotRadius` | `0`, `3` | jitter is seeded, so a chart is identical on every render |
|
|
99
|
+
| `ShapHeatmap` | `onSampleClick` | — | called with the column's `sample_ids` entry |
|
|
100
|
+
| `ShapWaterfall` | `sampleIndex` | `0` | which sample to explain |
|
|
101
|
+
| `ShapWaterfall` | `decimals` | `2` | `2`, `3`, `4` or `"percent"`; display only |
|
|
102
|
+
|
|
103
|
+
## Faithful to SHAP where it matters
|
|
104
|
+
|
|
105
|
+
Ordering, the "other features" partition, colour maps, tick placement and the waterfall's geometry are
|
|
106
|
+
taken from SHAP 0.49.1's plotting code and tested against values captured by running SHAP itself:
|
|
107
|
+
|
|
108
|
+
- feature order and the collapsed row follow `shap.plots` exactly (with `faithfulOtherRow` for the
|
|
109
|
+
byte-compatible variant);
|
|
110
|
+
- `red_blue` and `red_white_blue` are 256-entry tables captured from SHAP, not re-derived;
|
|
111
|
+
- axis ticks use matplotlib's `MaxNLocator` rule, and axis ranges its default margins;
|
|
112
|
+
- the waterfall draws SHAP's dashed connectors, its one-row base-value rule, and places value labels
|
|
113
|
+
inside or outside each arrow by the same rule.
|
|
114
|
+
|
|
115
|
+
A few choices deliberately differ, each because SHAP's version does not read well in a browser:
|
|
116
|
+
|
|
117
|
+
- numbers use significant figures (or a chosen precision) rather than `%0.03f`, which prints most
|
|
118
|
+
small SHAP values as `0`;
|
|
119
|
+
- species names are set in italics with underscores replaced by spaces;
|
|
120
|
+
- colour scales carry numeric ends rather than only "High" and "Low";
|
|
121
|
+
- the heatmap's f(x) line has a real axis;
|
|
122
|
+
- heatmap columns are ordered by each sample's total SHAP value rather than by hierarchical clustering;
|
|
123
|
+
- the beeswarm's jitter is seeded rather than random.
|
|
124
|
+
|
|
125
|
+
The golden fixtures under `tests/` and `fixtures/` were generated by
|
|
126
|
+
`tools/shap_fixtures/generate.py` in
|
|
127
|
+
[explainable-ai-microbiome-platform](https://github.com/princepongsakorn/explainable-ai-microbiome-platform),
|
|
128
|
+
where this package started.
|
|
129
|
+
|
|
130
|
+
## Related
|
|
131
|
+
|
|
132
|
+
- [mlflow-explainable](https://github.com/princepongsakorn/mlflow-explainable) logs a model and its
|
|
133
|
+
SHAP explainer to MLflow as one artifact — the Python side that produces the values these charts draw.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT
|