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.
@@ -0,0 +1,456 @@
1
+ import { P as ParsedExplanation, D as DisplayRows, V as ValuePrecision, R as RowSort } from './format-wEav_cPe.js';
2
+ export { a as DisplayRow, E as Explanation, I as InvalidExplanationError, U as UnsupportedContractVersionError, f as formatFeatureLabel, b as formatLevel, c as formatShapValue, s as sortDisplayRows } from './format-wEav_cPe.js';
3
+
4
+ /**
5
+ * Taxonomy-aware views of an Explanation.
6
+ *
7
+ * Feature names in this platform are `Genus_species`, which is a real structure
8
+ * the generic charts ignore. At the species level a genus the model genuinely
9
+ * uses can be spread across a dozen columns, each with a SHAP value small
10
+ * enough to read as noise, while the genus as a whole is one of the strongest
11
+ * signals present. The runtime already offers the same collapse behind
12
+ * `?aggregate_by=genus`; doing it here instead costs no request, because the
13
+ * per-species values are already in the browser.
14
+ */
15
+ /** The genus part of a `Genus_species` name. */
16
+ declare function genusOf(featureName: string): string;
17
+ type GenusGrouping = {
18
+ /** Genera in first-seen order. */
19
+ genera: string[];
20
+ /** For each genus, the Feature indices belonging to it. */
21
+ memberIndices: number[][];
22
+ };
23
+ /**
24
+ * Group Feature indices by genus, preserving first-seen order.
25
+ *
26
+ * Order matters: it is what keeps the grouping stable across requests, and it
27
+ * is the same rule `_aggregate_shap_by_genus` uses in the runtime, so the two
28
+ * paths cannot disagree about which column belongs where.
29
+ */
30
+ declare function groupByGenus(featureNames: string[]): GenusGrouping;
31
+ type AggregatedExplanation = {
32
+ values: number[][];
33
+ data: number[][];
34
+ featureNames: string[];
35
+ };
36
+ /**
37
+ * Collapse `Genus_species` columns into one column per genus.
38
+ *
39
+ * Summing is the correct operator, not averaging: SHAP is additive, so the
40
+ * per-Feature values satisfy `base + sum(phi) = f(x)`. A sum within groups is a
41
+ * re-partition of that same total, which leaves the identity intact — every
42
+ * waterfall still adds up, and there is a test saying so. Abundance is summed
43
+ * for the same reason, giving the genus's total relative abundance, which is
44
+ * what the colour should mean once the rows are genera.
45
+ */
46
+ declare function aggregateByGenus(values: number[][], data: number[][], featureNames: string[]): AggregatedExplanation;
47
+ /**
48
+ * A parsed Explanation with its Feature axis collapsed to genera.
49
+ *
50
+ * Only the Feature axis changes. Base values, Sample ids and the Sample count
51
+ * carry through untouched, so everything downstream — ordering, the Other row,
52
+ * the waterfall walk — runs unchanged on genera, and `base + sum(phi) = f(x)`
53
+ * still holds for every Sample. The input is not mutated.
54
+ */
55
+ declare function groupExplanationByGenus(explanation: ParsedExplanation): ParsedExplanation;
56
+
57
+ declare function parseExplanation(input: unknown, opts?: {
58
+ classIndex?: number;
59
+ }): ParsedExplanation;
60
+
61
+ /** mean(|phi|) over Samples, per Feature — what shap.plots.bar collapses a 2-D Explanation to. */
62
+ declare function globalImportance(e: ParsedExplanation): number[];
63
+ /** Descending by importance; ties resolved by ascending index so renders are reproducible. */
64
+ declare function orderFeatures(importance: number[]): number[];
65
+
66
+ /**
67
+ * Spec 3.4. `faithfulOtherRow` reproduces shap/plots/_bar.py:228-241, where the last displayed row
68
+ * absorbs the Feature ranked `maxDisplay` — so maxDisplay=15 shows 14 real Features. The default
69
+ * corrected mode shows `maxDisplay` real Features and adds the Other features row alongside.
70
+ */
71
+ declare function collapseToDisplay(featureNames: string[], importance: number[], order: number[], maxDisplay: number, faithfulOtherRow: boolean): DisplayRows;
72
+
73
+ type AxisTick = {
74
+ value: number;
75
+ x: number;
76
+ label: string;
77
+ };
78
+ type AxisSpine = {
79
+ x1: number;
80
+ x2: number;
81
+ y: number;
82
+ };
83
+ type AxisTitle = {
84
+ text: string;
85
+ x: number;
86
+ y: number;
87
+ fontSize: number;
88
+ };
89
+
90
+ declare const POSITIVE_COLOR = "#ff0051";
91
+ declare const NEGATIVE_COLOR = "#008bfb";
92
+ type BarLayoutOptions = {
93
+ width: number;
94
+ rowHeight: number;
95
+ marginLeft: number;
96
+ marginRight: number;
97
+ marginTop: number;
98
+ };
99
+ type BarGeometry = {
100
+ label: string;
101
+ featureIndex: number | null;
102
+ isOtherRow: boolean;
103
+ value: number;
104
+ x: number;
105
+ y: number;
106
+ width: number;
107
+ height: number;
108
+ color: string;
109
+ /** vertical centre of the row, for label baselines */
110
+ centerY: number;
111
+ };
112
+ type BarLayout = {
113
+ bars: BarGeometry[];
114
+ xDomain: [number, number];
115
+ xZero: number;
116
+ plotWidth: number;
117
+ /** Bottom edge of the last row, where the axis area begins. */
118
+ plotBottom: number;
119
+ xTicks: AxisTick[];
120
+ /** The bottom spine, which _bar.py:327-330 never hides. */
121
+ xSpine: AxisSpine | null;
122
+ xTitle: AxisTitle;
123
+ /**
124
+ * The solid vertical at zero. Always present, because SHAP's two code paths
125
+ * converge on it: _bar.py:252-254 draws axvline(0) when a value is negative,
126
+ * and _bar.py:329-330 hides the left spine only in that same case. With no
127
+ * negatives the spine stays and barh pins the axes' left edge to 0 — so
128
+ * either way there is one line at zero. Mean |SHAP| is never negative, so the
129
+ * summary chart always takes the spine path.
130
+ */
131
+ zeroLine: {
132
+ x: number;
133
+ y1: number;
134
+ y2: number;
135
+ };
136
+ height: number;
137
+ };
138
+ declare function barLayout(rows: DisplayRows, opts: BarLayoutOptions): BarLayout;
139
+
140
+ /** Fixed on-screen arrowhead length; matplotlib's equivalent is 0.08 inches. */
141
+ declare const WATERFALL_HEAD_LENGTH_PX = 8;
142
+ declare const WATERFALL_TICK_LABEL_DY = 18;
143
+ declare const WATERFALL_BASE_LABEL_DY = 36;
144
+ type WaterfallRow = {
145
+ label: string;
146
+ featureIndex: number | null;
147
+ isOtherRow: boolean;
148
+ /** The full, unrounded SHAP contribution represented by this row. */
149
+ value: number;
150
+ /** Start of the arrow in value space while walking backward from f(x). */
151
+ left: number;
152
+ /** Signed full arrow width in value space. */
153
+ width: number;
154
+ /** matplotlib-compatible row number, counted upward from the bottom. */
155
+ row: number;
156
+ color: string;
157
+ };
158
+ type WaterfallRows = {
159
+ /** Rows are ordered top-to-bottom. */
160
+ rows: WaterfallRow[];
161
+ baseValue: number;
162
+ modelOutput: number;
163
+ collapsedCount: number;
164
+ };
165
+ type WaterfallLayoutOptions = {
166
+ width: number;
167
+ rowHeight: number;
168
+ marginLeft: number;
169
+ marginRight: number;
170
+ marginTop: number;
171
+ /** Decimal places for the bar labels. Display only; omit for 3 significant figures. */
172
+ decimals?: ValuePrecision;
173
+ };
174
+ type Point = {
175
+ x: number;
176
+ y: number;
177
+ };
178
+ /** Where a bar's numeric label goes, and roughly how wide it is. */
179
+ type WaterfallValueLabel = {
180
+ x: number;
181
+ anchor: "start" | "middle" | "end";
182
+ /** Estimated from the glyph count — enough to decide whether it fits. */
183
+ estimatedWidth: number;
184
+ /** Drawn over the bar, so the renderer must paint it in a contrasting colour. */
185
+ inside: boolean;
186
+ text: string;
187
+ };
188
+ type WaterfallArrowGeometry = WaterfallRow & {
189
+ points: Point[];
190
+ startX: number;
191
+ endX: number;
192
+ y: number;
193
+ centerY: number;
194
+ height: number;
195
+ headLength: number;
196
+ valueLabel: WaterfallValueLabel;
197
+ };
198
+ type WaterfallAxisMark = {
199
+ kind: "base" | "output";
200
+ value: number;
201
+ x: number;
202
+ /** Vertical extent of the dashed rule. The two do not match — see below. */
203
+ y1: number;
204
+ y2: number;
205
+ label: string;
206
+ };
207
+ /**
208
+ * The dashed vertical joining one bar's start to the next bar's end.
209
+ *
210
+ * shap/plots/_waterfall.py:130-137 draws these inside the loop that walks the
211
+ * contributions, at `loc` immediately after `loc -= sval` — so the line sits
212
+ * exactly where the two bars meet, which is what shows the reader that the
213
+ * steps really do join up.
214
+ */
215
+ type WaterfallConnector = {
216
+ x: number;
217
+ y1: number;
218
+ y2: number;
219
+ };
220
+ type WaterfallTick = {
221
+ value: number;
222
+ x: number;
223
+ label: string;
224
+ };
225
+ type WaterfallSeparator = {
226
+ y: number;
227
+ x1: number;
228
+ x2: number;
229
+ };
230
+ type WaterfallLayout = {
231
+ arrows: WaterfallArrowGeometry[];
232
+ axisMarks: WaterfallAxisMark[];
233
+ connectors: WaterfallConnector[];
234
+ xTicks: WaterfallTick[];
235
+ separators: WaterfallSeparator[];
236
+ xDomain: [number, number];
237
+ plotWidth: number;
238
+ /** Left and right edge of the plot area, where the axis is drawn. */
239
+ plotLeft: number;
240
+ plotRight: number;
241
+ plotBottom: number;
242
+ height: number;
243
+ };
244
+ /**
245
+ * The value-space part of shap/plots/_waterfall.py::waterfall_legacy.
246
+ * It walks backward from f(x), leaving pixel scaling and arrowheads to waterfallLayout.
247
+ */
248
+ declare function waterfallRows(explanation: ParsedExplanation, sampleIndex: number, maxDisplay: number, faithfulOtherRow: boolean): WaterfallRows;
249
+ declare function waterfallLayout(valueRows: WaterfallRows, opts: WaterfallLayoutOptions): WaterfallLayout;
250
+
251
+ type ColormapName = "red_blue" | "red_white_blue";
252
+ /** Samples a captured SHAP colour map, interpolating adjacent LUT entries in sRGB. */
253
+ declare function sampleColormap(name: ColormapName, t: number): string;
254
+
255
+ declare const BEESWARM_MISSING_COLOR = "#777777";
256
+ declare const BEESWARM_ROW_HEIGHT = 0.4;
257
+ type BeeswarmPoint = {
258
+ sampleIndex: number;
259
+ /** SHAP value in value space. */
260
+ x: number;
261
+ /** SHAP-compatible row coordinate, including signed vertical jitter. */
262
+ y: number;
263
+ /** Original Feature value before percentile clipping. */
264
+ featureValue: number;
265
+ /** Value sent through the colour map, or null when the Feature value is missing. */
266
+ colorValue: number | null;
267
+ color: string;
268
+ };
269
+ type BeeswarmRow = {
270
+ label: string;
271
+ featureIndex: number | null;
272
+ isOtherRow: boolean;
273
+ /** SHAP row number, counted upward from the bottom. */
274
+ rowIndex: number;
275
+ vmin: number;
276
+ vmax: number;
277
+ points: BeeswarmPoint[];
278
+ };
279
+ type BeeswarmRows = {
280
+ /** Rows are ordered top-to-bottom. */
281
+ rows: BeeswarmRow[];
282
+ collapsedCount: number;
283
+ };
284
+ type BeeswarmLayoutOptions = {
285
+ width: number;
286
+ rowHeight: number;
287
+ marginLeft: number;
288
+ marginRight: number;
289
+ marginTop: number;
290
+ dotRadius: number;
291
+ };
292
+ type BeeswarmPointGeometry = Omit<BeeswarmPoint, "x" | "y"> & {
293
+ x: number;
294
+ y: number;
295
+ valueX: number;
296
+ valueY: number;
297
+ radius: number;
298
+ };
299
+ type BeeswarmRowGeometry = Omit<BeeswarmRow, "points"> & {
300
+ centerY: number;
301
+ points: BeeswarmPointGeometry[];
302
+ };
303
+ type BeeswarmLayout = {
304
+ rows: BeeswarmRowGeometry[];
305
+ xDomain: [number, number];
306
+ xZero: number;
307
+ plotWidth: number;
308
+ plotBottom: number;
309
+ xTicks: AxisTick[];
310
+ /** The bottom spine, which _beeswarm.py:493-495 leaves visible. */
311
+ xSpine: AxisSpine | null;
312
+ xTitle: AxisTitle;
313
+ height: number;
314
+ };
315
+ /**
316
+ * Computes SHAP-compatible beeswarm rows entirely in value space. Pixel scaling belongs in
317
+ * beeswarmLayout so golden values can be compared without a viewport.
318
+ */
319
+ declare function beeswarmRows(explanation: ParsedExplanation, maxDisplay: number, faithfulOtherRow: boolean, seed?: number, rowSort?: RowSort): BeeswarmRows;
320
+ /** Projects beeswarm value-space rows into SVG coordinates. */
321
+ declare function beeswarmLayout(valueRows: BeeswarmRows, opts: BeeswarmLayoutOptions): BeeswarmLayout;
322
+
323
+ type HeatmapCell = {
324
+ /** Original index in the Explanation, before Sample ordering. */
325
+ sampleIndex: number;
326
+ /** Full-precision SHAP value represented by this cell. */
327
+ value: number;
328
+ /** Value after clipping to the shared symmetric colour domain. */
329
+ colorValue: number;
330
+ color: string;
331
+ };
332
+ type HeatmapColumn = {
333
+ sampleIndex: number;
334
+ /** The record UUID: the key for click-through, never shown to a person. */
335
+ sampleId?: string;
336
+ /** What a person reads for this Sample, when the payload carries labels. */
337
+ sampleLabel?: string;
338
+ /** Sum of all SHAP values for this Sample. */
339
+ total: number;
340
+ };
341
+ type HeatmapRow = {
342
+ label: string;
343
+ featureIndex: number | null;
344
+ isOtherRow: boolean;
345
+ importance: number;
346
+ /** Importance divided by the largest displayed-row importance. */
347
+ sideBarValue: number;
348
+ cells: HeatmapCell[];
349
+ };
350
+ type HeatmapRows = {
351
+ /** Header of the uploaded column the labels came from, when it had one. */
352
+ sampleLabelColumn?: string;
353
+ /** Feature rows in top-to-bottom display order. */
354
+ rows: HeatmapRow[];
355
+ /** Sample columns in descending total-attribution order. */
356
+ columns: HeatmapColumn[];
357
+ /** Sample totals in the same order as columns. */
358
+ fxLine: number[];
359
+ vmin: number;
360
+ vmax: number;
361
+ collapsedCount: number;
362
+ };
363
+ type HeatmapLayoutOptions = {
364
+ width: number;
365
+ rowHeight: number;
366
+ marginLeft: number;
367
+ marginRight: number;
368
+ /** Top of the matrix; the f(x) line occupies the space above it. */
369
+ marginTop: number;
370
+ };
371
+ type HeatmapCellGeometry = HeatmapCell & {
372
+ x: number;
373
+ y: number;
374
+ width: number;
375
+ height: number;
376
+ };
377
+ type HeatmapSideBarGeometry = {
378
+ value: number;
379
+ x: number;
380
+ y: number;
381
+ width: number;
382
+ height: number;
383
+ };
384
+ type HeatmapRowGeometry = Omit<HeatmapRow, "cells"> & {
385
+ centerY: number;
386
+ cells: HeatmapCellGeometry[];
387
+ sideBar: HeatmapSideBarGeometry;
388
+ };
389
+ type HeatmapColumnGeometry = HeatmapColumn & {
390
+ x: number;
391
+ centerX: number;
392
+ width: number;
393
+ };
394
+ type HeatmapLinePoint = {
395
+ sampleIndex: number;
396
+ sampleId?: string;
397
+ value: number;
398
+ x: number;
399
+ y: number;
400
+ };
401
+ type HeatmapAxisMark = {
402
+ value: number;
403
+ y: number;
404
+ label: string;
405
+ };
406
+ type HeatmapSpine = {
407
+ x: number;
408
+ y1: number;
409
+ y2: number;
410
+ };
411
+ /** An outward tick on the left edge, one per Feature row. */
412
+ type HeatmapYTick = {
413
+ y: number;
414
+ x1: number;
415
+ x2: number;
416
+ };
417
+ type HeatmapLayout = {
418
+ sampleLabelColumn?: string;
419
+ rows: HeatmapRowGeometry[];
420
+ columns: HeatmapColumnGeometry[];
421
+ fxLine: HeatmapLinePoint[];
422
+ fxAxisMarks: HeatmapAxisMark[];
423
+ fxDomain: [number, number];
424
+ separatorY: number;
425
+ gridLeft: number;
426
+ gridRight: number;
427
+ gridTop: number;
428
+ plotBottom: number;
429
+ /**
430
+ * _heatmap.py:135 shows the left and right spines (and hides top and bottom),
431
+ * and :136 bounds them with set_bounds(n - row_height, -row_height). With
432
+ * row_height = 0.5 (:116) that is exactly the outer edge of the first and last
433
+ * row — so they frame the grid and stop short of the f(x) chart above it. The
434
+ * side bars are drawn with clip_on=False (:173), outside the right spine.
435
+ */
436
+ spines: {
437
+ left: HeatmapSpine;
438
+ right: HeatmapSpine;
439
+ };
440
+ /** yaxis.set_ticks_position("left") with tick_params(direction="out"), :134,:138. */
441
+ yTicks: HeatmapYTick[];
442
+ /** Ticks along the Sample axis, at the centre of each ticked column. */
443
+ xTicks: AxisTick[];
444
+ /** Always null: _heatmap.py:137 hides the bottom spine. */
445
+ xSpine: AxisSpine | null;
446
+ xTitle: AxisTitle;
447
+ plotWidth: number;
448
+ cellWidth: number;
449
+ height: number;
450
+ };
451
+ /** Computes the SHAP-compatible orderings, collapse, colours, line, and side bars in value space. */
452
+ declare function heatmapRows(explanation: ParsedExplanation, maxDisplay: number, faithfulOtherRow: boolean, rowSort?: RowSort): HeatmapRows;
453
+ /** Projects heatmap value-space rows into SVG geometry. */
454
+ declare function heatmapLayout(valueRows: HeatmapRows, opts: HeatmapLayoutOptions): HeatmapLayout;
455
+
456
+ export { type AggregatedExplanation, BEESWARM_MISSING_COLOR, BEESWARM_ROW_HEIGHT, type BarGeometry, type BarLayout, type BarLayoutOptions, type BeeswarmLayout, type BeeswarmLayoutOptions, type BeeswarmPoint, type BeeswarmPointGeometry, type BeeswarmRow, type BeeswarmRowGeometry, type BeeswarmRows, type ColormapName, DisplayRows, type GenusGrouping, type HeatmapAxisMark, type HeatmapCell, type HeatmapCellGeometry, type HeatmapColumn, type HeatmapColumnGeometry, type HeatmapLayout, type HeatmapLayoutOptions, type HeatmapLinePoint, type HeatmapRow, type HeatmapRowGeometry, type HeatmapRows, type HeatmapSideBarGeometry, type HeatmapSpine, type HeatmapYTick, NEGATIVE_COLOR, POSITIVE_COLOR, ParsedExplanation, type Point, RowSort, ValuePrecision, WATERFALL_BASE_LABEL_DY, WATERFALL_HEAD_LENGTH_PX, WATERFALL_TICK_LABEL_DY, type WaterfallArrowGeometry, type WaterfallAxisMark, type WaterfallConnector, type WaterfallLayout, type WaterfallLayoutOptions, type WaterfallRow, type WaterfallRows, type WaterfallSeparator, type WaterfallTick, type WaterfallValueLabel, aggregateByGenus, barLayout, beeswarmLayout, beeswarmRows, collapseToDisplay, genusOf, globalImportance, groupByGenus, groupExplanationByGenus, heatmapLayout, heatmapRows, orderFeatures, parseExplanation, sampleColormap, waterfallLayout, waterfallRows };
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ import {
2
+ BEESWARM_MISSING_COLOR,
3
+ BEESWARM_ROW_HEIGHT,
4
+ InvalidExplanationError,
5
+ NEGATIVE_COLOR,
6
+ POSITIVE_COLOR,
7
+ UnsupportedContractVersionError,
8
+ WATERFALL_BASE_LABEL_DY,
9
+ WATERFALL_HEAD_LENGTH_PX,
10
+ WATERFALL_TICK_LABEL_DY,
11
+ aggregateByGenus,
12
+ barLayout,
13
+ beeswarmLayout,
14
+ beeswarmRows,
15
+ collapseToDisplay,
16
+ formatFeatureLabel,
17
+ formatLevel,
18
+ formatShapValue,
19
+ genusOf,
20
+ globalImportance,
21
+ groupByGenus,
22
+ groupExplanationByGenus,
23
+ heatmapLayout,
24
+ heatmapRows,
25
+ orderFeatures,
26
+ parseExplanation,
27
+ sampleColormap,
28
+ sortDisplayRows,
29
+ waterfallLayout,
30
+ waterfallRows
31
+ } from "./chunk-OXFKP5I3.js";
32
+ export {
33
+ BEESWARM_MISSING_COLOR,
34
+ BEESWARM_ROW_HEIGHT,
35
+ InvalidExplanationError,
36
+ NEGATIVE_COLOR,
37
+ POSITIVE_COLOR,
38
+ UnsupportedContractVersionError,
39
+ WATERFALL_BASE_LABEL_DY,
40
+ WATERFALL_HEAD_LENGTH_PX,
41
+ WATERFALL_TICK_LABEL_DY,
42
+ aggregateByGenus,
43
+ barLayout,
44
+ beeswarmLayout,
45
+ beeswarmRows,
46
+ collapseToDisplay,
47
+ formatFeatureLabel,
48
+ formatLevel,
49
+ formatShapValue,
50
+ genusOf,
51
+ globalImportance,
52
+ groupByGenus,
53
+ groupExplanationByGenus,
54
+ heatmapLayout,
55
+ heatmapRows,
56
+ orderFeatures,
57
+ parseExplanation,
58
+ sampleColormap,
59
+ sortDisplayRows,
60
+ waterfallLayout,
61
+ waterfallRows
62
+ };
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}