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/dist/react.js ADDED
@@ -0,0 +1,776 @@
1
+ import {
2
+ TICK_LABEL_DY,
3
+ TICK_LENGTH,
4
+ WATERFALL_BASE_LABEL_DY,
5
+ WATERFALL_TICK_LABEL_DY,
6
+ barLayout,
7
+ beeswarmLayout,
8
+ beeswarmRows,
9
+ collapseToDisplay,
10
+ formatShapValue,
11
+ globalImportance,
12
+ groupExplanationByGenus,
13
+ heatmapLayout,
14
+ heatmapRows,
15
+ orderFeatures,
16
+ parseExplanation,
17
+ sampleColormap,
18
+ waterfallLayout,
19
+ waterfallRows
20
+ } from "./chunk-OXFKP5I3.js";
21
+
22
+ // src/react/ShapBar.tsx
23
+ import { useMemo, useState } from "react";
24
+
25
+ // src/react/XAxis.tsx
26
+ import { jsx, jsxs } from "react/jsx-runtime";
27
+ function XAxis({
28
+ ticks,
29
+ spine,
30
+ title,
31
+ plotBottom,
32
+ tickFontSize = 11
33
+ }) {
34
+ return /* @__PURE__ */ jsxs("g", { "aria-hidden": "true", children: [
35
+ spine && /* @__PURE__ */ jsx(
36
+ "line",
37
+ {
38
+ x1: spine.x1,
39
+ x2: spine.x2,
40
+ y1: spine.y,
41
+ y2: spine.y,
42
+ stroke: "#333333",
43
+ strokeWidth: 1
44
+ }
45
+ ),
46
+ ticks.map((tick) => /* @__PURE__ */ jsxs("g", { children: [
47
+ /* @__PURE__ */ jsx(
48
+ "line",
49
+ {
50
+ x1: tick.x,
51
+ x2: tick.x,
52
+ y1: plotBottom,
53
+ y2: plotBottom + TICK_LENGTH,
54
+ stroke: "#333333",
55
+ strokeWidth: 1
56
+ }
57
+ ),
58
+ /* @__PURE__ */ jsx(
59
+ "text",
60
+ {
61
+ x: tick.x,
62
+ y: plotBottom + TICK_LABEL_DY,
63
+ textAnchor: "middle",
64
+ fontSize: tickFontSize,
65
+ fill: "#333333",
66
+ children: tick.label
67
+ }
68
+ )
69
+ ] }, `xtick-${tick.value}`)),
70
+ /* @__PURE__ */ jsx(
71
+ "text",
72
+ {
73
+ x: title.x,
74
+ y: title.y,
75
+ textAnchor: "middle",
76
+ fontSize: title.fontSize,
77
+ fill: "#333333",
78
+ children: title.text
79
+ }
80
+ )
81
+ ] });
82
+ }
83
+
84
+ // src/react/ShapBar.tsx
85
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
86
+ function ShapBar({
87
+ explanation,
88
+ maxDisplay = 10,
89
+ faithfulOtherRow = false,
90
+ groupByGenus = false,
91
+ classIndex = 1,
92
+ width = 720,
93
+ rowHeight = 26,
94
+ onFeatureClick
95
+ }) {
96
+ const [hovered, setHovered] = useState(null);
97
+ const layout = useMemo(() => {
98
+ const raw = parseExplanation(explanation, { classIndex });
99
+ const parsed = groupByGenus ? groupExplanationByGenus(raw) : raw;
100
+ const importance = globalImportance(parsed);
101
+ const order = orderFeatures(importance);
102
+ const rows = collapseToDisplay(
103
+ parsed.featureNames,
104
+ importance,
105
+ order,
106
+ maxDisplay,
107
+ faithfulOtherRow
108
+ );
109
+ return barLayout(rows, {
110
+ width,
111
+ rowHeight,
112
+ marginLeft: 260,
113
+ marginRight: 90,
114
+ marginTop: 8
115
+ });
116
+ }, [groupByGenus, explanation, maxDisplay, faithfulOtherRow, classIndex, width, rowHeight]);
117
+ return /* @__PURE__ */ jsxs2(
118
+ "svg",
119
+ {
120
+ width,
121
+ height: layout.height,
122
+ role: "img",
123
+ "aria-label": "Mean absolute SHAP value per feature",
124
+ children: [
125
+ /* @__PURE__ */ jsx2(
126
+ "line",
127
+ {
128
+ x1: layout.zeroLine.x,
129
+ x2: layout.zeroLine.x,
130
+ y1: layout.zeroLine.y1,
131
+ y2: layout.zeroLine.y2,
132
+ stroke: "#333333",
133
+ strokeWidth: 1
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsx2(
137
+ XAxis,
138
+ {
139
+ ticks: layout.xTicks,
140
+ spine: layout.xSpine,
141
+ title: layout.xTitle,
142
+ plotBottom: layout.plotBottom,
143
+ tickFontSize: 11
144
+ }
145
+ ),
146
+ layout.bars.map((bar, i) => /* @__PURE__ */ jsxs2(
147
+ "g",
148
+ {
149
+ onMouseEnter: () => setHovered(i),
150
+ onMouseLeave: () => setHovered(null),
151
+ onClick: () => onFeatureClick == null ? void 0 : onFeatureClick(bar.featureIndex),
152
+ style: { cursor: onFeatureClick ? "pointer" : "default" },
153
+ children: [
154
+ /* @__PURE__ */ jsx2(
155
+ "rect",
156
+ {
157
+ x: 0,
158
+ y: bar.y - (rowHeight - bar.height) / 2,
159
+ width,
160
+ height: rowHeight,
161
+ fill: hovered === i ? "#00000008" : "transparent"
162
+ }
163
+ ),
164
+ /* @__PURE__ */ jsx2(
165
+ "text",
166
+ {
167
+ x: 250,
168
+ y: bar.centerY,
169
+ textAnchor: "end",
170
+ dominantBaseline: "middle",
171
+ fontSize: 13,
172
+ fill: "#333333",
173
+ fontStyle: bar.isOtherRow ? "normal" : "italic",
174
+ children: bar.label
175
+ }
176
+ ),
177
+ /* @__PURE__ */ jsx2(
178
+ "rect",
179
+ {
180
+ x: bar.x,
181
+ y: bar.y,
182
+ width: bar.width,
183
+ height: bar.height,
184
+ fill: bar.color,
185
+ stroke: "rgba(255,255,255,0.8)",
186
+ strokeWidth: 1
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsx2(
190
+ "text",
191
+ {
192
+ x: bar.x + bar.width + 6,
193
+ y: bar.centerY,
194
+ dominantBaseline: "middle",
195
+ fontSize: 12,
196
+ fill: bar.color,
197
+ children: formatShapValue(bar.value)
198
+ }
199
+ )
200
+ ]
201
+ },
202
+ `row-${i}`
203
+ ))
204
+ ]
205
+ }
206
+ );
207
+ }
208
+
209
+ // src/react/ShapWaterfall.tsx
210
+ import { useMemo as useMemo2, useState as useState2 } from "react";
211
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
212
+ function ShapWaterfall({
213
+ explanation,
214
+ sampleIndex = 0,
215
+ maxDisplay = 10,
216
+ faithfulOtherRow = false,
217
+ groupByGenus = false,
218
+ classIndex = 1,
219
+ width = 720,
220
+ rowHeight = 30,
221
+ decimals = 2,
222
+ onFeatureClick
223
+ }) {
224
+ const [hovered, setHovered] = useState2(null);
225
+ const marginTop = 34;
226
+ const layout = useMemo2(() => {
227
+ const raw = parseExplanation(explanation, { classIndex });
228
+ const parsed = groupByGenus ? groupExplanationByGenus(raw) : raw;
229
+ const rows = waterfallRows(parsed, sampleIndex, maxDisplay, faithfulOtherRow);
230
+ return waterfallLayout(rows, {
231
+ width,
232
+ rowHeight,
233
+ marginLeft: 260,
234
+ marginRight: 110,
235
+ marginTop,
236
+ decimals
237
+ });
238
+ }, [
239
+ groupByGenus,
240
+ explanation,
241
+ sampleIndex,
242
+ maxDisplay,
243
+ faithfulOtherRow,
244
+ classIndex,
245
+ width,
246
+ rowHeight,
247
+ decimals
248
+ ]);
249
+ return /* @__PURE__ */ jsxs3(
250
+ "svg",
251
+ {
252
+ width,
253
+ height: layout.height,
254
+ role: "img",
255
+ "aria-label": `Local SHAP waterfall for Sample ${sampleIndex}`,
256
+ children: [
257
+ layout.separators.map((separator, index) => /* @__PURE__ */ jsx3(
258
+ "line",
259
+ {
260
+ x1: separator.x1,
261
+ x2: separator.x2,
262
+ y1: separator.y,
263
+ y2: separator.y,
264
+ stroke: "#cccccc",
265
+ strokeWidth: 1,
266
+ strokeDasharray: "1 5"
267
+ },
268
+ `separator-${index}`
269
+ )),
270
+ /* @__PURE__ */ jsxs3("g", { "aria-hidden": "true", children: [
271
+ /* @__PURE__ */ jsx3(
272
+ "line",
273
+ {
274
+ x1: layout.plotLeft,
275
+ x2: layout.plotRight,
276
+ y1: layout.plotBottom,
277
+ y2: layout.plotBottom,
278
+ stroke: "#333333",
279
+ strokeWidth: 1
280
+ }
281
+ ),
282
+ layout.xTicks.map((tick) => /* @__PURE__ */ jsxs3("g", { children: [
283
+ /* @__PURE__ */ jsx3(
284
+ "line",
285
+ {
286
+ x1: tick.x,
287
+ x2: tick.x,
288
+ y1: layout.plotBottom,
289
+ y2: layout.plotBottom + 5,
290
+ stroke: "#333333",
291
+ strokeWidth: 1
292
+ }
293
+ ),
294
+ /* @__PURE__ */ jsx3(
295
+ "text",
296
+ {
297
+ x: tick.x,
298
+ y: layout.plotBottom + WATERFALL_TICK_LABEL_DY,
299
+ textAnchor: "middle",
300
+ fontSize: 11,
301
+ fill: "#333333",
302
+ children: tick.label
303
+ }
304
+ )
305
+ ] }, `tick-${tick.value}`))
306
+ ] }),
307
+ layout.connectors.map((connector, index) => /* @__PURE__ */ jsx3(
308
+ "line",
309
+ {
310
+ x1: connector.x,
311
+ x2: connector.x,
312
+ y1: connector.y1,
313
+ y2: connector.y2,
314
+ stroke: "#bbbbbb",
315
+ strokeWidth: 1,
316
+ strokeDasharray: "4 4"
317
+ },
318
+ `connector-${index}`
319
+ )),
320
+ layout.axisMarks.map((mark) => /* @__PURE__ */ jsxs3("g", { children: [
321
+ /* @__PURE__ */ jsx3(
322
+ "line",
323
+ {
324
+ x1: mark.x,
325
+ x2: mark.x,
326
+ y1: mark.y1,
327
+ y2: mark.y2,
328
+ stroke: "#bbbbbb",
329
+ strokeWidth: 1,
330
+ strokeDasharray: "4 4"
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsx3(
334
+ "text",
335
+ {
336
+ x: mark.x,
337
+ y: mark.kind === "output" ? marginTop - 10 : layout.plotBottom + WATERFALL_BASE_LABEL_DY,
338
+ textAnchor: "middle",
339
+ fontSize: 12,
340
+ fill: "#777777",
341
+ children: mark.label
342
+ }
343
+ )
344
+ ] }, mark.kind)),
345
+ layout.arrows.map((arrow, index) => /* @__PURE__ */ jsxs3(
346
+ "g",
347
+ {
348
+ onMouseEnter: () => setHovered(index),
349
+ onMouseLeave: () => setHovered(null),
350
+ onClick: () => onFeatureClick == null ? void 0 : onFeatureClick(arrow.featureIndex),
351
+ style: { cursor: onFeatureClick ? "pointer" : "default" },
352
+ children: [
353
+ /* @__PURE__ */ jsx3(
354
+ "rect",
355
+ {
356
+ x: 0,
357
+ y: arrow.centerY - rowHeight / 2,
358
+ width,
359
+ height: rowHeight,
360
+ fill: hovered === index ? "#00000008" : "transparent"
361
+ }
362
+ ),
363
+ /* @__PURE__ */ jsx3(
364
+ "text",
365
+ {
366
+ x: 250,
367
+ y: arrow.centerY,
368
+ textAnchor: "end",
369
+ dominantBaseline: "middle",
370
+ fontSize: 13,
371
+ fill: "#333333",
372
+ fontStyle: arrow.isOtherRow ? "normal" : "italic",
373
+ children: arrow.label
374
+ }
375
+ ),
376
+ /* @__PURE__ */ jsx3(
377
+ "polygon",
378
+ {
379
+ points: arrow.points.map((point) => `${point.x},${point.y}`).join(" "),
380
+ fill: arrow.color,
381
+ stroke: "rgba(255,255,255,0.8)",
382
+ strokeWidth: 1
383
+ }
384
+ ),
385
+ /* @__PURE__ */ jsx3(
386
+ "text",
387
+ {
388
+ x: arrow.valueLabel.x,
389
+ y: arrow.centerY,
390
+ textAnchor: arrow.valueLabel.anchor,
391
+ dominantBaseline: "middle",
392
+ fontSize: 12,
393
+ fill: arrow.valueLabel.inside ? "#ffffff" : arrow.color,
394
+ children: arrow.valueLabel.text
395
+ }
396
+ )
397
+ ]
398
+ },
399
+ `row-${index}`
400
+ ))
401
+ ]
402
+ }
403
+ );
404
+ }
405
+
406
+ // src/react/ShapBeeswarm.tsx
407
+ import { useMemo as useMemo3, useState as useState3 } from "react";
408
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
409
+ function ShapBeeswarm({
410
+ explanation,
411
+ maxDisplay = 10,
412
+ faithfulOtherRow = false,
413
+ groupByGenus = false,
414
+ rowSort = "importance",
415
+ classIndex = 1,
416
+ width = 720,
417
+ rowHeight = 28,
418
+ seed = 0,
419
+ dotRadius = 3,
420
+ onFeatureClick
421
+ }) {
422
+ const [hovered, setHovered] = useState3(null);
423
+ const marginTop = 8;
424
+ const layout = useMemo3(() => {
425
+ const raw = parseExplanation(explanation, { classIndex });
426
+ const parsed = groupByGenus ? groupExplanationByGenus(raw) : raw;
427
+ const rows = beeswarmRows(parsed, maxDisplay, faithfulOtherRow, seed, rowSort);
428
+ return beeswarmLayout(rows, {
429
+ width,
430
+ rowHeight,
431
+ marginLeft: 260,
432
+ marginRight: 90,
433
+ marginTop,
434
+ dotRadius
435
+ });
436
+ }, [
437
+ groupByGenus,
438
+ rowSort,
439
+ explanation,
440
+ maxDisplay,
441
+ faithfulOtherRow,
442
+ classIndex,
443
+ width,
444
+ rowHeight,
445
+ seed,
446
+ dotRadius
447
+ ]);
448
+ const handlePointHover = (rowIndex, event) => {
449
+ var _a, _b;
450
+ const attribute = (_b = (_a = event.target).getAttribute) == null ? void 0 : _b.call(_a, "data-point-index");
451
+ if (attribute === null || attribute === void 0) return;
452
+ const pointIndex = Number(attribute);
453
+ if (Number.isInteger(pointIndex)) setHovered({ rowIndex, pointIndex });
454
+ };
455
+ const fallbackRow = layout.rows[0];
456
+ const activeRow = hovered ? layout.rows[hovered.rowIndex] : fallbackRow;
457
+ const activePoint = hovered ? activeRow == null ? void 0 : activeRow.points[hovered.pointIndex] : activeRow == null ? void 0 : activeRow.points[0];
458
+ const legendSteps = 32;
459
+ return /* @__PURE__ */ jsxs4("svg", { width, height: layout.height, role: "img", "aria-label": "Global SHAP beeswarm", children: [
460
+ /* @__PURE__ */ jsx4(
461
+ "line",
462
+ {
463
+ x1: layout.xZero,
464
+ x2: layout.xZero,
465
+ y1: marginTop,
466
+ y2: layout.plotBottom,
467
+ stroke: "#777777",
468
+ strokeWidth: 1
469
+ }
470
+ ),
471
+ layout.rows.map((row, rowIndex) => /* @__PURE__ */ jsxs4(
472
+ "g",
473
+ {
474
+ onMouseOver: (event) => handlePointHover(rowIndex, event),
475
+ onMouseLeave: () => setHovered(null),
476
+ onClick: () => onFeatureClick == null ? void 0 : onFeatureClick(row.featureIndex),
477
+ style: { cursor: onFeatureClick ? "pointer" : "default" },
478
+ children: [
479
+ /* @__PURE__ */ jsx4(
480
+ "rect",
481
+ {
482
+ x: 0,
483
+ y: row.centerY - rowHeight / 2,
484
+ width,
485
+ height: rowHeight,
486
+ fill: (hovered == null ? void 0 : hovered.rowIndex) === rowIndex ? "#00000008" : "transparent"
487
+ }
488
+ ),
489
+ /* @__PURE__ */ jsx4(
490
+ "text",
491
+ {
492
+ x: 250,
493
+ y: row.centerY,
494
+ textAnchor: "end",
495
+ dominantBaseline: "middle",
496
+ fontSize: 13,
497
+ fill: "#333333",
498
+ fontStyle: row.isOtherRow ? "normal" : "italic",
499
+ children: row.label
500
+ }
501
+ ),
502
+ row.points.map((point, pointIndex) => /* @__PURE__ */ jsx4(
503
+ "circle",
504
+ {
505
+ "data-point-index": pointIndex,
506
+ cx: point.x,
507
+ cy: point.y,
508
+ r: point.radius,
509
+ fill: point.color,
510
+ fillOpacity: 0.7
511
+ },
512
+ `point-${point.sampleIndex}`
513
+ ))
514
+ ]
515
+ },
516
+ `row-${rowIndex}`
517
+ )),
518
+ /* @__PURE__ */ jsx4(
519
+ XAxis,
520
+ {
521
+ ticks: layout.xTicks,
522
+ spine: layout.xSpine,
523
+ title: layout.xTitle,
524
+ plotBottom: layout.plotBottom,
525
+ tickFontSize: 11
526
+ }
527
+ ),
528
+ activeRow && /* @__PURE__ */ jsxs4("g", { "aria-label": "Feature value colour scale", children: [
529
+ Array.from({ length: legendSteps }, (_, index) => /* @__PURE__ */ jsx4(
530
+ "rect",
531
+ {
532
+ x: width - 160 + index * 4,
533
+ y: layout.height - 22,
534
+ width: 4,
535
+ height: 7,
536
+ fill: sampleColormap("red_blue", index / (legendSteps - 1))
537
+ },
538
+ `legend-${index}`
539
+ )),
540
+ /* @__PURE__ */ jsx4("text", { x: width - 160, y: layout.height - 3, fontSize: 10, fill: "#555555", children: formatShapValue(activeRow.vmin) }),
541
+ /* @__PURE__ */ jsx4(
542
+ "text",
543
+ {
544
+ x: width - 32,
545
+ y: layout.height - 3,
546
+ textAnchor: "end",
547
+ fontSize: 10,
548
+ fill: "#555555",
549
+ children: formatShapValue(activeRow.vmax)
550
+ }
551
+ )
552
+ ] }),
553
+ activeRow && activePoint && /* @__PURE__ */ jsxs4(
554
+ "g",
555
+ {
556
+ opacity: hovered ? 1 : 0,
557
+ pointerEvents: "none",
558
+ transform: `translate(${activePoint.x + 8} ${activePoint.y - 8})`,
559
+ children: [
560
+ /* @__PURE__ */ jsx4("rect", { x: 0, y: -16, width: 210, height: 54, rx: 3, fill: "#ffffff", stroke: "#cccccc" }),
561
+ /* @__PURE__ */ jsx4("text", { x: 7, y: 0, fontSize: 11, fill: "#222222", children: activeRow.label }),
562
+ /* @__PURE__ */ jsx4("text", { x: 7, y: 15, fontSize: 11, fill: "#222222", children: `SHAP value: ${formatShapValue(activePoint.valueX)}` }),
563
+ /* @__PURE__ */ jsx4("text", { x: 7, y: 30, fontSize: 11, fill: "#222222", children: `Feature value: ${Number.isFinite(activePoint.featureValue) ? formatShapValue(activePoint.featureValue) : "missing"}` })
564
+ ]
565
+ }
566
+ )
567
+ ] });
568
+ }
569
+
570
+ // src/react/ShapHeatmap.tsx
571
+ import { useMemo as useMemo4, useState as useState4 } from "react";
572
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
573
+ function ShapHeatmap({
574
+ explanation,
575
+ maxDisplay = 10,
576
+ faithfulOtherRow = false,
577
+ groupByGenus = false,
578
+ rowSort = "importance",
579
+ classIndex = 1,
580
+ width = 720,
581
+ rowHeight = 26,
582
+ onFeatureClick,
583
+ onSampleClick
584
+ }) {
585
+ const [hoveredColumn, setHoveredColumn] = useState4(null);
586
+ const marginTop = 72;
587
+ const layout = useMemo4(() => {
588
+ const raw = parseExplanation(explanation, { classIndex });
589
+ const parsed = groupByGenus ? groupExplanationByGenus(raw) : raw;
590
+ const rows = heatmapRows(parsed, maxDisplay, faithfulOtherRow, rowSort);
591
+ return heatmapLayout(rows, {
592
+ width,
593
+ rowHeight,
594
+ marginLeft: 260,
595
+ marginRight: 100,
596
+ marginTop
597
+ });
598
+ }, [groupByGenus, rowSort, explanation, maxDisplay, faithfulOtherRow, classIndex, width, rowHeight]);
599
+ const activeColumn = hoveredColumn === null ? void 0 : layout.columns[hoveredColumn];
600
+ const nameOf = (column) => {
601
+ if (!column.sampleLabel) return `Sample ${column.sampleIndex + 1}`;
602
+ return layout.sampleLabelColumn ? `${layout.sampleLabelColumn}: ${column.sampleLabel}` : column.sampleLabel;
603
+ };
604
+ const tooltipWidth = activeColumn ? Math.max(190, nameOf(activeColumn).length * 6.5 + 16) : 190;
605
+ const tooltipX = activeColumn ? Math.max(0, Math.min(activeColumn.centerX + 8, width - tooltipWidth - 8)) : 0;
606
+ return /* @__PURE__ */ jsxs5("svg", { width, height: layout.height, role: "img", "aria-label": "Global SHAP heatmap", children: [
607
+ layout.fxAxisMarks.map((mark) => /* @__PURE__ */ jsxs5("g", { children: [
608
+ /* @__PURE__ */ jsx5(
609
+ "line",
610
+ {
611
+ x1: layout.gridLeft - 4,
612
+ x2: layout.gridRight,
613
+ y1: mark.y,
614
+ y2: mark.y,
615
+ stroke: "#dddddd",
616
+ strokeWidth: 1
617
+ }
618
+ ),
619
+ /* @__PURE__ */ jsx5(
620
+ "text",
621
+ {
622
+ x: layout.gridLeft - 8,
623
+ y: mark.y,
624
+ textAnchor: "end",
625
+ dominantBaseline: "middle",
626
+ fontSize: 10,
627
+ fill: "#666666",
628
+ children: mark.label
629
+ }
630
+ )
631
+ ] }, `fx-axis-${mark.value}`)),
632
+ /* @__PURE__ */ jsx5(
633
+ "polyline",
634
+ {
635
+ points: layout.fxLine.map((point) => `${point.x},${point.y}`).join(" "),
636
+ fill: "none",
637
+ stroke: "#333333",
638
+ strokeWidth: 1.5
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsx5(
642
+ "line",
643
+ {
644
+ x1: layout.gridLeft,
645
+ x2: layout.gridRight,
646
+ y1: layout.separatorY,
647
+ y2: layout.separatorY,
648
+ stroke: "#888888",
649
+ strokeWidth: 1,
650
+ strokeDasharray: "4 4"
651
+ }
652
+ ),
653
+ /* @__PURE__ */ jsx5(
654
+ XAxis,
655
+ {
656
+ ticks: layout.xTicks,
657
+ spine: layout.xSpine,
658
+ title: layout.xTitle,
659
+ plotBottom: layout.plotBottom,
660
+ tickFontSize: 10
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsxs5("g", { "aria-hidden": "true", children: [
664
+ [layout.spines.left, layout.spines.right].map((spine, index) => /* @__PURE__ */ jsx5(
665
+ "line",
666
+ {
667
+ x1: spine.x,
668
+ x2: spine.x,
669
+ y1: spine.y1,
670
+ y2: spine.y2,
671
+ stroke: "#333333",
672
+ strokeWidth: 1
673
+ },
674
+ `spine-${index}`
675
+ )),
676
+ layout.yTicks.map((tick, index) => /* @__PURE__ */ jsx5(
677
+ "line",
678
+ {
679
+ x1: tick.x1,
680
+ x2: tick.x2,
681
+ y1: tick.y,
682
+ y2: tick.y,
683
+ stroke: "#333333",
684
+ strokeWidth: 1
685
+ },
686
+ `ytick-${index}`
687
+ ))
688
+ ] }),
689
+ layout.rows.map((row, rowIndex) => /* @__PURE__ */ jsxs5(
690
+ "g",
691
+ {
692
+ onClick: () => onFeatureClick == null ? void 0 : onFeatureClick(row.featureIndex),
693
+ style: { cursor: onFeatureClick ? "pointer" : "default" },
694
+ children: [
695
+ /* @__PURE__ */ jsx5(
696
+ "text",
697
+ {
698
+ x: 250,
699
+ y: row.centerY,
700
+ textAnchor: "end",
701
+ dominantBaseline: "middle",
702
+ fontSize: 13,
703
+ fill: "#333333",
704
+ fontStyle: row.isOtherRow ? "normal" : "italic",
705
+ children: row.label
706
+ }
707
+ ),
708
+ row.cells.map((cell, columnIndex) => /* @__PURE__ */ jsx5(
709
+ "rect",
710
+ {
711
+ x: cell.x,
712
+ y: cell.y,
713
+ width: cell.width,
714
+ height: cell.height,
715
+ fill: cell.color
716
+ },
717
+ `cell-${columnIndex}`
718
+ )),
719
+ /* @__PURE__ */ jsx5(
720
+ "rect",
721
+ {
722
+ x: row.sideBar.x,
723
+ y: row.sideBar.y,
724
+ width: row.sideBar.width,
725
+ height: row.sideBar.height,
726
+ fill: "#777777"
727
+ }
728
+ )
729
+ ]
730
+ },
731
+ `row-${rowIndex}`
732
+ )),
733
+ activeColumn && /* @__PURE__ */ jsx5(
734
+ "rect",
735
+ {
736
+ x: activeColumn.x,
737
+ y: 8,
738
+ width: activeColumn.width,
739
+ height: layout.plotBottom - 8,
740
+ fill: "#000000",
741
+ fillOpacity: 0.1,
742
+ pointerEvents: "none"
743
+ }
744
+ ),
745
+ layout.columns.map((column, columnIndex) => /* @__PURE__ */ jsx5(
746
+ "rect",
747
+ {
748
+ x: column.x,
749
+ y: 8,
750
+ width: column.width,
751
+ height: layout.plotBottom - 8,
752
+ fill: "transparent",
753
+ "aria-label": `${nameOf(column)}, total SHAP value ${formatShapValue(column.total)}`,
754
+ onMouseEnter: () => setHoveredColumn(columnIndex),
755
+ onMouseLeave: () => setHoveredColumn(null),
756
+ onClick: () => {
757
+ if (column.sampleId !== void 0) onSampleClick == null ? void 0 : onSampleClick(column.sampleId);
758
+ },
759
+ style: { cursor: column.sampleId && onSampleClick ? "pointer" : "default" }
760
+ },
761
+ `column-hit-${column.sampleIndex}`
762
+ )),
763
+ activeColumn && /* @__PURE__ */ jsxs5("g", { pointerEvents: "none", transform: `translate(${tooltipX} 10)`, children: [
764
+ /* @__PURE__ */ jsx5("rect", { x: 0, y: 0, width: tooltipWidth, height: 42, rx: 3, fill: "#ffffff", stroke: "#cccccc" }),
765
+ /* @__PURE__ */ jsx5("text", { x: 7, y: 15, fontSize: 11, fill: "#222222", children: nameOf(activeColumn) }),
766
+ /* @__PURE__ */ jsx5("text", { x: 7, y: 31, fontSize: 11, fill: "#222222", children: `\u03A3\u03C6: ${formatShapValue(activeColumn.total)}` })
767
+ ] })
768
+ ] });
769
+ }
770
+ export {
771
+ ShapBar,
772
+ ShapBeeswarm,
773
+ ShapHeatmap,
774
+ ShapWaterfall
775
+ };
776
+ //# sourceMappingURL=react.js.map