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 @@
1
+ {"version":3,"sources":["../src/core/types.ts","../src/core/taxonomy.ts","../src/core/rowSort.ts","../src/core/parse.ts","../src/core/order.ts","../src/core/format.ts","../src/core/collapse.ts","../src/core/ticks.ts","../src/core/barLayout.ts","../src/core/waterfallLayout.ts","../src/core/colormaps.json","../src/core/colormap.ts","../src/core/beeswarmLayout.ts","../src/core/heatmapLayout.ts"],"sourcesContent":["/** The wire payload. Core fields are named exactly as shap.Explanation names them. */\nexport type Explanation = {\n contract_version: number;\n values: number[][] | number[][][];\n base_values: number | number[] | number[][];\n data: number[][];\n feature_names: string[];\n sample_ids?: string[];\n /** n — what a person reads for each Sample; never a join key. */\n sample_labels?: string[];\n /** Header of the uploaded column the labels came from, when it had one. */\n sample_label_column?: string;\n output_names?: string[];\n model_name?: string;\n model_version?: string;\n};\n\n/** A validated Explanation with the class axis resolved away. */\nexport type ParsedExplanation = {\n /** n x p, class already selected */\n values: number[][];\n /** n x p */\n data: number[][];\n /** length n — always per Sample, even when the wire form was a scalar */\n baseValues: number[];\n /** length p */\n featureNames: string[];\n /** length n, undefined when the payload omitted it */\n sampleIds?: string[];\n /** length n — display names. sampleIds stays the key for joins and click-through. */\n sampleLabels?: string[];\n sampleLabelColumn?: string;\n outputName?: string;\n nSamples: number;\n nFeatures: number;\n};\n\n/** One row of a chart: either a real Feature or the collapsed Other features row. */\nexport type DisplayRow = {\n label: string;\n /** index into ParsedExplanation.featureNames, or null for the Other features row */\n featureIndex: number | null;\n /** the value this row draws — for the bar chart, mean(|phi|) */\n value: number;\n isOtherRow: boolean;\n};\n\nexport type DisplayRows = {\n rows: DisplayRow[];\n /** how many Features were folded into the Other features row; 0 when none */\n collapsedCount: number;\n};\n\nexport class UnsupportedContractVersionError extends Error {\n constructor(public readonly received: unknown) {\n super(`shap-svg supports contract_version 1, received ${JSON.stringify(received)}`);\n this.name = \"UnsupportedContractVersionError\";\n }\n}\n\nexport class InvalidExplanationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidExplanationError\";\n }\n}\n","import { ParsedExplanation } from \"./types\";\n\n/**\n * Taxonomy-aware views of an Explanation.\n *\n * Feature names in this platform are `Genus_species`, which is a real structure\n * the generic charts ignore. At the species level a genus the model genuinely\n * uses can be spread across a dozen columns, each with a SHAP value small\n * enough to read as noise, while the genus as a whole is one of the strongest\n * signals present. The runtime already offers the same collapse behind\n * `?aggregate_by=genus`; doing it here instead costs no request, because the\n * per-species values are already in the browser.\n */\n\n/** The genus part of a `Genus_species` name. */\nexport function genusOf(featureName: string): string {\n const underscore = featureName.indexOf(\"_\");\n return underscore === -1 ? featureName : featureName.slice(0, underscore);\n}\n\nexport type GenusGrouping = {\n /** Genera in first-seen order. */\n genera: string[];\n /** For each genus, the Feature indices belonging to it. */\n memberIndices: number[][];\n};\n\n/**\n * Group Feature indices by genus, preserving first-seen order.\n *\n * Order matters: it is what keeps the grouping stable across requests, and it\n * is the same rule `_aggregate_shap_by_genus` uses in the runtime, so the two\n * paths cannot disagree about which column belongs where.\n */\nexport function groupByGenus(featureNames: string[]): GenusGrouping {\n const genera: string[] = [];\n const memberIndices: number[][] = [];\n const seen = new Map<string, number>();\n\n featureNames.forEach((name, index) => {\n const genus = genusOf(name);\n let slot = seen.get(genus);\n if (slot === undefined) {\n slot = genera.length;\n seen.set(genus, slot);\n genera.push(genus);\n memberIndices.push([]);\n }\n memberIndices[slot].push(index);\n });\n\n return { genera, memberIndices };\n}\n\nexport type AggregatedExplanation = {\n values: number[][];\n data: number[][];\n featureNames: string[];\n};\n\n/**\n * Collapse `Genus_species` columns into one column per genus.\n *\n * Summing is the correct operator, not averaging: SHAP is additive, so the\n * per-Feature values satisfy `base + sum(phi) = f(x)`. A sum within groups is a\n * re-partition of that same total, which leaves the identity intact — every\n * waterfall still adds up, and there is a test saying so. Abundance is summed\n * for the same reason, giving the genus's total relative abundance, which is\n * what the colour should mean once the rows are genera.\n */\nexport function aggregateByGenus(\n values: number[][],\n data: number[][],\n featureNames: string[],\n): AggregatedExplanation {\n const { genera, memberIndices } = groupByGenus(featureNames);\n const sumInto = (rows: number[][]) =>\n rows.map((row) =>\n memberIndices.map((members) =>\n members.reduce((total, index) => total + (row[index] ?? 0), 0),\n ),\n );\n\n return {\n values: sumInto(values),\n data: sumInto(data),\n featureNames: genera,\n };\n}\n\n/**\n * A parsed Explanation with its Feature axis collapsed to genera.\n *\n * Only the Feature axis changes. Base values, Sample ids and the Sample count\n * carry through untouched, so everything downstream — ordering, the Other row,\n * the waterfall walk — runs unchanged on genera, and `base + sum(phi) = f(x)`\n * still holds for every Sample. The input is not mutated.\n */\nexport function groupExplanationByGenus(explanation: ParsedExplanation): ParsedExplanation {\n const grouped = aggregateByGenus(\n explanation.values,\n explanation.data,\n explanation.featureNames,\n );\n return {\n ...explanation,\n values: grouped.values,\n data: grouped.data,\n featureNames: grouped.featureNames,\n nFeatures: grouped.featureNames.length,\n };\n}\n","import { DisplayRows } from \"./types\";\n\n/**\n * The order the displayed Feature rows are drawn in.\n *\n * `importance` is SHAP's own order and the default. The other two only\n * *reorder* the rows importance already chose — they never change which\n * Features are shown. Sorting every Feature by name and taking the first N\n * would show whatever happens to begin with A, not what the model relies on.\n */\nexport type RowSort = \"importance\" | \"name\" | \"featureValue\";\n\n/**\n * Reorder a collapsed set of rows.\n *\n * * `name` — alphabetical, ignoring case.\n * * `featureValue` — mean feature value across Samples, highest first. For\n * this platform that is mean relative abundance, so the most abundant taxa\n * sit on top.\n *\n * Ties keep importance order, so a sort is stable and repeatable. The Other\n * row is not a Feature and always stays last.\n */\nexport function sortDisplayRows(\n display: DisplayRows,\n sort: RowSort,\n data: number[][],\n): DisplayRows {\n if (sort === \"importance\") return display;\n\n const meanOf = (featureIndex: number) =>\n data.length === 0\n ? 0\n : data.reduce((sum, sample) => sum + sample[featureIndex], 0) / data.length;\n\n const keyed = display.rows\n .filter((row) => !row.isOtherRow)\n .map((row, rank) => ({\n row,\n rank,\n mean: row.featureIndex === null ? 0 : meanOf(row.featureIndex),\n }));\n\n keyed.sort((a, b) => {\n const primary = sort === \"name\"\n ? a.row.label.localeCompare(b.row.label, undefined, { sensitivity: \"base\" })\n : b.mean - a.mean;\n return primary || a.rank - b.rank;\n });\n\n return {\n ...display,\n rows: [...keyed.map((k) => k.row), ...display.rows.filter((row) => row.isOtherRow)],\n };\n}\n","import {\n Explanation,\n InvalidExplanationError,\n ParsedExplanation,\n UnsupportedContractVersionError,\n} from \"./types\";\n\nconst SUPPORTED_CONTRACT_VERSION = 1;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction assertFiniteRows(rows: unknown, field: string): asserts rows is number[][] {\n if (!Array.isArray(rows)) {\n throw new InvalidExplanationError(`${field} must be an array of Sample rows`);\n }\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n if (!Array.isArray(row)) {\n throw new InvalidExplanationError(`${field}[${i}] must be an array of Feature values`);\n }\n for (let j = 0; j < row.length; j++) {\n if (typeof row[j] !== \"number\" || !Number.isFinite(row[j])) {\n throw new InvalidExplanationError(\n `${field}[${i}][${j}] is ${String(row[j])}; the payload must contain only finite numbers`,\n );\n }\n }\n }\n}\n\nfunction assertStringArray(value: unknown, field: string): asserts value is string[] {\n if (!Array.isArray(value) || value.some((entry) => typeof entry !== \"string\")) {\n throw new InvalidExplanationError(`${field} must be an array of strings`);\n }\n}\n\nfunction selectClassValues(values: unknown, classIndex: number): number[][] {\n if (!Array.isArray(values)) {\n throw new InvalidExplanationError(\"values must be an array\");\n }\n if (!Number.isInteger(classIndex) || classIndex < 0) {\n throw new InvalidExplanationError(`classIndex must be a non-negative integer, received ${classIndex}`);\n }\n const firstRow = values[0];\n const firstCell = Array.isArray(firstRow) ? firstRow[0] : undefined;\n if (Array.isArray(firstCell)) {\n return values.map((row, sampleIndex) => {\n if (!Array.isArray(row)) {\n throw new InvalidExplanationError(`values[${sampleIndex}] must be an array of Feature values`);\n }\n return row.map((cell, featureIndex) => {\n if (!Array.isArray(cell) || classIndex >= cell.length) {\n throw new InvalidExplanationError(\n `values[${sampleIndex}][${featureIndex}] has no class ${classIndex}`,\n );\n }\n return cell[classIndex] as number;\n });\n });\n }\n return values as number[][];\n}\n\nfunction selectBaseValues(value: unknown, nSamples: number, classIndex: number): number[] {\n if (typeof value === \"number\") {\n return new Array(nSamples).fill(value);\n }\n if (!Array.isArray(value)) {\n throw new InvalidExplanationError(\"base_values must be a number, an array, or an array of class arrays\");\n }\n if (Array.isArray(value[0])) {\n return value.map((row, sampleIndex) => {\n if (!Array.isArray(row) || classIndex >= row.length) {\n throw new InvalidExplanationError(`base_values[${sampleIndex}] has no class ${classIndex}`);\n }\n return row[classIndex] as number;\n });\n }\n return value as number[];\n}\n\nexport function parseExplanation(\n input: unknown,\n opts: { classIndex?: number } = {},\n): ParsedExplanation {\n const classIndex = opts.classIndex ?? 1;\n if (!isRecord(input) || input.contract_version !== SUPPORTED_CONTRACT_VERSION) {\n throw new UnsupportedContractVersionError(isRecord(input) ? input.contract_version : undefined);\n }\n\n const e = input as Explanation;\n assertStringArray(e.feature_names, \"feature_names\");\n assertFiniteRows(e.data, \"data\");\n const values = selectClassValues(e.values, classIndex);\n assertFiniteRows(values, \"values\");\n\n const nSamples = values.length;\n const nFeatures = e.feature_names.length;\n if (e.data.length !== nSamples) {\n throw new InvalidExplanationError(`data has ${e.data.length} Samples but values has ${nSamples}`);\n }\n for (const [name, rows] of [[\"values\", values], [\"data\", e.data]] as const) {\n for (let i = 0; i < rows.length; i++) {\n if (rows[i].length !== nFeatures) {\n throw new InvalidExplanationError(\n `${name}[${i}] has ${rows[i].length} Features but feature_names has ${nFeatures}`,\n );\n }\n }\n }\n\n const baseValues = selectBaseValues(e.base_values, nSamples, classIndex);\n if (baseValues.length !== nSamples) {\n throw new InvalidExplanationError(\n `base_values has ${baseValues.length} entries but there are ${nSamples} Samples`,\n );\n }\n assertFiniteRows([baseValues], \"base_values\");\n\n if (e.sample_ids !== undefined) {\n assertStringArray(e.sample_ids, \"sample_ids\");\n if (e.sample_ids.length !== nSamples) {\n throw new InvalidExplanationError(\n `sample_ids has ${e.sample_ids.length} entries but there are ${nSamples} Samples`,\n );\n }\n }\n if (e.sample_labels !== undefined) {\n assertStringArray(e.sample_labels, \"sample_labels\");\n if (e.sample_labels.length !== nSamples) {\n throw new InvalidExplanationError(\n `sample_labels has ${e.sample_labels.length} entries but there are ${nSamples} Samples`,\n );\n }\n }\n if (e.sample_label_column !== undefined && typeof e.sample_label_column !== \"string\") {\n throw new InvalidExplanationError(\"sample_label_column must be a string\");\n }\n if (e.output_names !== undefined) assertStringArray(e.output_names, \"output_names\");\n\n return {\n values,\n data: e.data,\n baseValues,\n featureNames: e.feature_names,\n sampleIds: e.sample_ids,\n sampleLabels: e.sample_labels,\n sampleLabelColumn: e.sample_label_column,\n outputName: e.output_names?.[0],\n nSamples,\n nFeatures,\n };\n}\n","import { ParsedExplanation } from \"./types\";\n\n/** mean(|phi|) over Samples, per Feature — what shap.plots.bar collapses a 2-D Explanation to. */\nexport function globalImportance(e: ParsedExplanation): number[] {\n const out = new Array<number>(e.nFeatures).fill(0);\n for (const row of e.values) {\n for (let j = 0; j < e.nFeatures; j++) out[j] += Math.abs(row[j]);\n }\n return out.map((sum) => sum / e.nSamples);\n}\n\n/** Descending by importance; ties resolved by ascending index so renders are reproducible. */\nexport function orderFeatures(importance: number[]): number[] {\n return importance\n .map((value, index) => ({ value, index }))\n .sort((a, b) => (b.value - a.value) || (a.index - b.index))\n .map((entry) => entry.index);\n}\n","const MINUS = \"−\";\n/** Below this magnitude, fixed notation would round away to zero at our precision. */\nconst EXPONENT_THRESHOLD = 1e-3;\n\n/** How the value labels are written. Display only. */\nexport type ValuePrecision = 2 | 3 | 4 | \"percent\";\n\n/** Decimal places a percentage is shown to. */\nconst PERCENT_DECIMALS = 2;\n\n/**\n * Spec 3.5 V1. SHAP's \"%0.03f\" renders most relative-abundance-scale values as \"0\" or \"-0\";\n * this keeps them readable and always signs the value so a bar's direction is unambiguous.\n *\n * `decimals` fixes how the value is written for display. It does not change any\n * value that is computed from — or compared against — the payload; it only\n * changes the glyphs. Values too small to survive at that precision still fall\n * back to an exponent rather than collapsing to a signed zero, which is the\n * whole point of V1: \"+0.00\" hides both the magnitude and the direction.\n *\n * \"percent\" moves the decimal point two places and adds a sign. It reaches a\n * range the fixed-decimal settings cannot: a contribution of 0.0003 is an\n * exponent at two decimals but an ordinary 0.03%, which is most of this data.\n */\nexport function formatShapValue(v: number, decimals?: ValuePrecision): string {\n if (v === 0) return \"0\";\n return (v < 0 ? MINUS : \"+\") + magnitudeOf(v, decimals);\n}\n\n/**\n * A value on the model's output scale: `E[f(X)]` and `f(x)`.\n *\n * Unsigned, unlike a contribution. SHAP draws the same distinction —\n * `_waterfall.py:327,339` format these with `\"%0.03f\"` and only the bar\n * contributions with `\"%+0.02f\"` — and it matters: a leading \"+\" on a model\n * output reads as \"went up by\", when the number is where the prediction landed,\n * not how far it moved. A minus is still kept, because nothing guarantees a\n * model output is a probability.\n */\nexport function formatLevel(v: number, decimals?: ValuePrecision): string {\n if (v === 0) return \"0\";\n return (v < 0 ? MINUS : \"\") + magnitudeOf(v, decimals);\n}\n\n/** The digits both formatters share; the caller owns the sign. */\nfunction magnitudeOf(v: number, decimals?: ValuePrecision): string {\n const magnitude = Math.abs(v);\n\n if (decimals === undefined) {\n return magnitude < EXPONENT_THRESHOLD\n ? magnitude.toExponential(0)\n : String(Number(magnitude.toPrecision(3)));\n }\n\n const places = decimals === \"percent\" ? PERCENT_DECIMALS : decimals;\n const scaled = decimals === \"percent\" ? magnitude * 100 : magnitude;\n const unit = decimals === \"percent\" ? \"%\" : \"\";\n\n // Half of the last retained place: anything under it rounds to all zeros.\n // The unit is dropped along with the fixed notation, because \"1e-7%\" reads\n // as a percentage of a percentage.\n if (scaled < 0.5 * 10 ** -places) return magnitude.toExponential(0);\n return scaled.toFixed(places) + unit;\n}\n\n/** Spec 3.5 V3. The italic styling is applied by the renderer, not here. */\nexport function formatFeatureLabel(name: string): string {\n return name.replace(/_/g, \" \");\n}\n","import { DisplayRow, DisplayRows } from \"./types\";\nimport { formatFeatureLabel } from \"./format\";\n\n/**\n * Spec 3.4. `faithfulOtherRow` reproduces shap/plots/_bar.py:228-241, where the last displayed row\n * absorbs the Feature ranked `maxDisplay` — so maxDisplay=15 shows 14 real Features. The default\n * corrected mode shows `maxDisplay` real Features and adds the Other features row alongside.\n */\nexport function collapseToDisplay(\n featureNames: string[],\n importance: number[],\n order: number[],\n maxDisplay: number,\n faithfulOtherRow: boolean,\n): DisplayRows {\n const p = order.length;\n\n if (maxDisplay >= p) {\n return {\n rows: order.map((index) => ({\n label: formatFeatureLabel(featureNames[index]),\n featureIndex: index,\n value: importance[index],\n isOtherRow: false,\n })),\n collapsedCount: 0,\n };\n }\n\n const realCount = faithfulOtherRow ? maxDisplay - 1 : maxDisplay;\n const rows: DisplayRow[] = order.slice(0, realCount).map((index) => ({\n label: formatFeatureLabel(featureNames[index]),\n featureIndex: index,\n value: importance[index],\n isOtherRow: false,\n }));\n\n const collapsed = order.slice(realCount);\n const collapsedValue = collapsed.reduce((sum, index) => sum + importance[index], 0);\n rows.push({\n label: faithfulOtherRow\n ? `Sum of ${collapsed.length} other features`\n : `${collapsed.length} other features`,\n featureIndex: null,\n value: collapsedValue,\n isOtherRow: true,\n });\n\n return { rows, collapsedCount: collapsed.length };\n}\n","/**\n * Axis ticks, chosen the way matplotlib chooses them for SHAP's figures.\n *\n * Two rules, both read from the installed matplotlib rather than approximated:\n *\n * * How many ticks fit. `XAxis.get_tick_space` (axis.py) is\n * `floor(axis_length_pt / (tick_label_size_pt * 3))` — the 3 is its estimate\n * of a tick label's aspect ratio — and `MaxNLocator` (ticker.py) clips that to\n * at most 9. Our axis length is in CSS pixels, which are 0.75 pt; the label\n * size is SHAP's own, in points.\n * * Which step. The smallest of 1, 2, 2.5, 5 or 10 times a power of ten that\n * covers the span in that many intervals.\n *\n * Evaluated at the size each chart is drawn inline, this reproduces the ticks on\n * SHAP's own reference figures for all three of this platform's charts — see\n * tests/ticks.test.ts. Tick density still follows axis width, as it does in\n * matplotlib, so the expanded view shows finer ticks than the inline one.\n */\n\nconst MINUS = \"−\";\n/** CSS defines 1px as 0.75pt. */\nconst PX_TO_PT = 0.75;\n/** MaxNLocator's upper bound on intervals when nbins is \"auto\". */\nconst MAX_TICKS = 9;\nconst LADDER = [1, 2, 2.5, 5, 10];\n\n/** matplotlib's default major tick, 3.5 pt, rounded to whole pixels. */\nexport const TICK_LENGTH = 5;\n/** Baseline of the tick labels below the axis. */\nexport const TICK_LABEL_DY = 18;\n/** Baseline of the axis title below the axis. */\nexport const AXIS_TITLE_DY = 38;\n\nexport type AxisTick = { value: number; x: number; label: string };\nexport type AxisSpine = { x1: number; x2: number; y: number };\nexport type AxisTitle = { text: string; x: number; y: number; fontSize: number };\n\n/** How many tick intervals fit along an axis, as matplotlib estimates it. */\nexport function tickSpace(axisPx: number, labelPt: number): number {\n const space = Math.floor((axisPx * PX_TO_PT) / (labelPt * 3));\n return Math.min(MAX_TICKS, Math.max(1, space));\n}\n\n/**\n * Round tick values across `[min, max]`, in at most `maxTicks` intervals.\n *\n * `integer` keeps every step whole. A heatmap's x axis counts Samples, and for a\n * handful of them matplotlib would happily tick at 0.5; there is no Sample 0.5.\n *\n * Ticks are `first + i * step`, not an accumulation, so a step of 0.1 does not\n * drift into 0.30000000000000004; values within a hair of zero are snapped to a\n * positive zero so no label reads \"−0.0\".\n */\nexport function niceTicks(\n min: number,\n max: number,\n maxTicks: number,\n { integer = false }: { integer?: boolean } = {},\n): { ticks: number[]; step: number } {\n const span = max - min;\n if (!(span > 0) || !(maxTicks > 0)) return { ticks: [], step: 0 };\n\n const rough = span / maxTicks;\n let magnitude = 10 ** Math.floor(Math.log10(rough));\n let step = 0;\n // A decade may have no admissible step once integer steps are required, so\n // walk up until one fits.\n for (let decade = 0; decade < 4 && step === 0; decade++, magnitude *= 10) {\n for (const multiple of LADDER) {\n const candidate = multiple * magnitude;\n if (candidate < rough * (1 - 1e-9)) continue;\n if (integer && (candidate < 1 || Math.abs(candidate - Math.round(candidate)) > 1e-9)) {\n continue;\n }\n step = candidate;\n break;\n }\n }\n if (step === 0) return { ticks: [], step: 0 };\n\n const first = Math.ceil(min / step - 1e-9) * step;\n const count = Math.floor((max - first) / step + 1e-9) + 1;\n const ticks: number[] = [];\n for (let i = 0; i < count; i++) {\n const value = first + i * step;\n ticks.push(Math.abs(value) < step * 1e-9 ? 0 : value);\n }\n return { ticks, step };\n}\n\n/**\n * A tick label, with as many decimals as the step needs and no more.\n *\n * A tick is a round number by construction, so \"0.1\" carries everything\n * \"0.1000\" does, and several share one axis. Negative values take the unicode\n * minus, as matplotlib's ScalarFormatter and the rest of this package do.\n */\nexport function tickLabel(value: number, step: number, percent = false): string {\n const scaled = percent ? value * 100 : value;\n const scaledStep = percent ? step * 100 : step;\n let decimals = 0;\n while (decimals < 6) {\n const factor = 10 ** decimals;\n if (Math.abs(scaledStep * factor - Math.round(scaledStep * factor)) < 1e-9) break;\n decimals++;\n }\n const digits = Math.abs(scaled).toFixed(decimals);\n const sign = scaled < 0 && Number(digits) !== 0 ? MINUS : \"\";\n return sign + digits + (percent ? \"%\" : \"\");\n}\n","import { DisplayRows } from \"./types\";\nimport {\n AXIS_TITLE_DY,\n AxisSpine,\n AxisTick,\n AxisTitle,\n niceTicks,\n tickLabel,\n tickSpace,\n} from \"./ticks\";\n\nexport const POSITIVE_COLOR = \"#ff0051\";\nexport const NEGATIVE_COLOR = \"#008bfb\";\n\n/** shap/plots/_bar.py:259 — total_width 0.7 of the row pitch. */\nconst BAR_THICKNESS_RATIO = 0.7;\n/** Room below the last row for ticks, their labels and the axis title. */\nconst AXIS_HEIGHT = 52;\n/** _bar.py:331 tick_params(\"x\", labelsize=11). */\nconst TICK_LABEL_PT = 11;\n/** _bar.py:345 set_xlabel(xlabel, fontsize=13). */\nconst TITLE_PT = 13;\n/** matplotlib's default axes.xmargin, and _bar.py:334's own x_buffer ratio. */\nconst X_MARGIN = 0.05;\n\nexport type BarLayoutOptions = {\n width: number;\n rowHeight: number;\n marginLeft: number;\n marginRight: number;\n marginTop: number;\n};\n\nexport type BarGeometry = {\n label: string;\n featureIndex: number | null;\n isOtherRow: boolean;\n value: number;\n x: number;\n y: number;\n width: number;\n height: number;\n color: string;\n /** vertical centre of the row, for label baselines */\n centerY: number;\n};\n\nexport type BarLayout = {\n bars: BarGeometry[];\n xDomain: [number, number];\n xZero: number;\n plotWidth: number;\n /** Bottom edge of the last row, where the axis area begins. */\n plotBottom: number;\n xTicks: AxisTick[];\n /** The bottom spine, which _bar.py:327-330 never hides. */\n xSpine: AxisSpine | null;\n xTitle: AxisTitle;\n /**\n * The solid vertical at zero. Always present, because SHAP's two code paths\n * converge on it: _bar.py:252-254 draws axvline(0) when a value is negative,\n * and _bar.py:329-330 hides the left spine only in that same case. With no\n * negatives the spine stays and barh pins the axes' left edge to 0 — so\n * either way there is one line at zero. Mean |SHAP| is never negative, so the\n * summary chart always takes the spine path.\n */\n zeroLine: { x: number; y1: number; y2: number };\n height: number;\n};\n\nfunction barXAxis(\n min: number,\n max: number,\n toX: (value: number) => number,\n marginLeft: number,\n plotWidth: number,\n plotBottom: number,\n): Pick<BarLayout, \"xTicks\" | \"xSpine\" | \"xTitle\"> {\n const { ticks, step } = niceTicks(min, max, tickSpace(plotWidth, TICK_LABEL_PT));\n return {\n xTicks: ticks.map((value) => ({ value, x: toX(value), label: tickLabel(value, step) })),\n xSpine: { x1: marginLeft, x2: marginLeft + plotWidth, y: plotBottom },\n xTitle: {\n // _bar.py:143-150 builds this from the Explanation's transform history:\n // \"SHAP value\" -> \"|SHAP value|\" -> \"mean(|SHAP value|)\".\n text: \"mean(|SHAP value|)\",\n x: marginLeft + plotWidth / 2,\n y: plotBottom + AXIS_TITLE_DY,\n fontSize: TITLE_PT,\n },\n };\n}\n\nexport function barLayout(rows: DisplayRows, opts: BarLayoutOptions): BarLayout {\n const { width, rowHeight, marginLeft, marginRight, marginTop } = opts;\n const plotWidth = width - marginLeft - marginRight;\n\n const values = rows.rows.map((r) => r.value);\n // Zero must always be in the domain, otherwise a bar would not start at the axis.\n const dataMin = Math.min(0, ...values);\n const dataMax = Math.max(0, ...values);\n const dataSpan = dataMax - dataMin;\n const negative = values.some((v) => v < 0);\n\n // Two 5% pads, measured by running shap.plots.bar: xlim came back as\n // max * 1.05 * 1.05. matplotlib's autoscale pads first — barh pins the edge\n // that sits at zero, so with nothing negative only the right grows — and\n // _bar.py:334-340 then reads that padded xlim and adds a 5% buffer of it:\n // to the right only, unless something is negative, then to both sides.\n const autoMin = negative ? dataMin - dataSpan * X_MARGIN : dataMin;\n const autoMax = dataMax + dataSpan * X_MARGIN;\n const buffer = (autoMax - autoMin) * X_MARGIN;\n const min = negative ? autoMin - buffer : autoMin;\n const max = autoMax + buffer;\n const span = max - min || 1;\n const toX = (v: number) => marginLeft + ((v - min) / span) * plotWidth;\n const xZero = toX(0);\n\n const barHeight = rowHeight * BAR_THICKNESS_RATIO;\n const inset = (rowHeight - barHeight) / 2;\n\n const bars: BarGeometry[] = rows.rows.map((row, i) => {\n const rowTop = marginTop + i * rowHeight;\n const end = toX(row.value);\n // shap/plots/_bar.py:267-271 colours a value of exactly zero as negative.\n const positive = row.value > 0;\n return {\n label: row.label,\n featureIndex: row.featureIndex,\n isOtherRow: row.isOtherRow,\n value: row.value,\n x: positive ? xZero : end,\n y: rowTop + inset,\n width: Math.abs(end - xZero),\n height: barHeight,\n color: positive ? POSITIVE_COLOR : NEGATIVE_COLOR,\n centerY: rowTop + rowHeight / 2,\n };\n });\n\n return {\n bars,\n xDomain: [min, max],\n xZero,\n plotWidth,\n plotBottom: marginTop + rows.rows.length * rowHeight,\n ...barXAxis(min, max, toX, marginLeft, plotWidth, marginTop + rows.rows.length * rowHeight),\n zeroLine: { x: xZero, y1: marginTop, y2: marginTop + rows.rows.length * rowHeight },\n height: marginTop + rows.rows.length * rowHeight + AXIS_HEIGHT,\n };\n}\n","import { NEGATIVE_COLOR, POSITIVE_COLOR } from \"./barLayout\";\nimport { formatFeatureLabel, formatLevel, formatShapValue, ValuePrecision } from \"./format\";\nimport { niceTicks, tickLabel, tickSpace } from \"./ticks\";\nimport { orderFeatures } from \"./order\";\nimport { ParsedExplanation } from \"./types\";\n\n/** Fixed on-screen arrowhead length; matplotlib's equivalent is 0.08 inches. */\nexport const WATERFALL_HEAD_LENGTH_PX = 8;\nconst BAR_THICKNESS_RATIO = 0.8;\n/** Tick marks, their labels, and the E[f(X)] caption below them. */\nconst AXIS_HEIGHT = 52;\nconst TICK_LENGTH = 5;\nexport const WATERFALL_TICK_LABEL_DY = 18;\nexport const WATERFALL_BASE_LABEL_DY = 36;\n/** _waterfall.py:316 ax.tick_params(labelsize=13). */\nconst TICK_LABEL_PT = 13;\n\nexport type WaterfallRow = {\n label: string;\n featureIndex: number | null;\n isOtherRow: boolean;\n /** The full, unrounded SHAP contribution represented by this row. */\n value: number;\n /** Start of the arrow in value space while walking backward from f(x). */\n left: number;\n /** Signed full arrow width in value space. */\n width: number;\n /** matplotlib-compatible row number, counted upward from the bottom. */\n row: number;\n color: string;\n};\n\nexport type WaterfallRows = {\n /** Rows are ordered top-to-bottom. */\n rows: WaterfallRow[];\n baseValue: number;\n modelOutput: number;\n collapsedCount: number;\n};\n\nexport type WaterfallLayoutOptions = {\n width: number;\n rowHeight: number;\n marginLeft: number;\n marginRight: number;\n marginTop: number;\n /** Decimal places for the bar labels. Display only; omit for 3 significant figures. */\n decimals?: ValuePrecision;\n};\n\nexport type Point = { x: number; y: number };\n\n/** Where a bar's numeric label goes, and roughly how wide it is. */\nexport type WaterfallValueLabel = {\n x: number;\n anchor: \"start\" | \"middle\" | \"end\";\n /** Estimated from the glyph count — enough to decide whether it fits. */\n estimatedWidth: number;\n /** Drawn over the bar, so the renderer must paint it in a contrasting colour. */\n inside: boolean;\n text: string;\n};\n\nexport type WaterfallArrowGeometry = WaterfallRow & {\n points: Point[];\n startX: number;\n endX: number;\n y: number;\n centerY: number;\n height: number;\n headLength: number;\n valueLabel: WaterfallValueLabel;\n};\n\nexport type WaterfallAxisMark = {\n kind: \"base\" | \"output\";\n value: number;\n x: number;\n /** Vertical extent of the dashed rule. The two do not match — see below. */\n y1: number;\n y2: number;\n label: string;\n};\n\n/**\n * The dashed vertical joining one bar's start to the next bar's end.\n *\n * shap/plots/_waterfall.py:130-137 draws these inside the loop that walks the\n * contributions, at `loc` immediately after `loc -= sval` — so the line sits\n * exactly where the two bars meet, which is what shows the reader that the\n * steps really do join up.\n */\nexport type WaterfallConnector = {\n x: number;\n y1: number;\n y2: number;\n};\n\nexport type WaterfallTick = {\n value: number;\n x: number;\n label: string;\n};\n\nexport type WaterfallSeparator = {\n y: number;\n x1: number;\n x2: number;\n};\n\nexport type WaterfallLayout = {\n arrows: WaterfallArrowGeometry[];\n axisMarks: WaterfallAxisMark[];\n connectors: WaterfallConnector[];\n xTicks: WaterfallTick[];\n separators: WaterfallSeparator[];\n xDomain: [number, number];\n plotWidth: number;\n /** Left and right edge of the plot area, where the axis is drawn. */\n plotLeft: number;\n plotRight: number;\n plotBottom: number;\n height: number;\n};\n\nconst colorFor = (value: number) => value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR;\n\n/** Gap between a bar's tip and its number. */\nconst VALUE_LABEL_GAP = 6;\nconst VALUE_LABEL_FONT_SIZE = 12;\n/** Mean glyph width as a fraction of font size, for this digit-heavy text. */\nconst GLYPH_WIDTH_RATIO = 0.6;\n/** Clearance the text keeps from the bar's own edges when it sits inside. */\nconst INSIDE_PADDING = 4;\n\n/**\n * Place a bar's numeric label, following shap/plots/_waterfall.py lines 230-245.\n *\n * SHAP draws the number inside the arrow, measures it, and moves it out only\n * when the text is wider than the arrow. Inside is the better default: it reads\n * as part of the bar and it cannot collide with anything.\n *\n * When the text does not fit, it goes just past the tip on the side the bar\n * points to. For a negative bar that is leftward, and a short negative bar sits\n * close to the feature names, so the label would land on top of them. There it\n * flips to the inner side instead, where the plot area always has room.\n * matplotlib hits the same conflict and resolves it with a wide left margin;\n * flipping is the better answer when the margin is a fixed gutter.\n *\n * Widths are estimated from the glyph count rather than measured. Measuring\n * would mean a DOM round-trip per row and a layout that cannot be computed or\n * tested outside a browser; over digits, which vary little in width, the\n * estimate is close enough to decide \"does this fit\".\n */\nfunction placeValueLabel(\n value: number,\n startX: number,\n endX: number,\n gutterX: number,\n decimals: ValuePrecision | undefined,\n): WaterfallValueLabel {\n const text = formatShapValue(value, decimals);\n const estimatedWidth = text.length * VALUE_LABEL_FONT_SIZE * GLYPH_WIDTH_RATIO;\n const base = { estimatedWidth, text };\n\n if (estimatedWidth + 2 * INSIDE_PADDING <= Math.abs(endX - startX)) {\n return { ...base, x: (startX + endX) / 2, anchor: \"middle\", inside: true };\n }\n\n if (value >= 0) {\n return { ...base, x: endX + VALUE_LABEL_GAP, anchor: \"start\", inside: false };\n }\n\n // A negative bar points left, so its label belongs off the left tip.\n const outsideLeftEdge = endX - VALUE_LABEL_GAP - estimatedWidth;\n if (outsideLeftEdge >= gutterX) {\n return { ...base, x: endX - VALUE_LABEL_GAP, anchor: \"end\", inside: false };\n }\n // No room there, so flip to the far side of the bar and read rightward from\n // its tail. That is startX, not endX: endX is the arrowhead, and starting\n // there would lay the text across the bar in the bar's own colour.\n return { ...base, x: startX + VALUE_LABEL_GAP, anchor: \"start\", inside: false };\n}\n\n/**\n * The value-space part of shap/plots/_waterfall.py::waterfall_legacy.\n * It walks backward from f(x), leaving pixel scaling and arrowheads to waterfallLayout.\n */\nexport function waterfallRows(\n explanation: ParsedExplanation,\n sampleIndex: number,\n maxDisplay: number,\n faithfulOtherRow: boolean,\n): WaterfallRows {\n if (!Number.isInteger(sampleIndex) || sampleIndex < 0 || sampleIndex >= explanation.nSamples) {\n throw new RangeError(\n `sampleIndex must identify a Sample from 0 to ${explanation.nSamples - 1}, received ${sampleIndex}`,\n );\n }\n if (!Number.isInteger(maxDisplay) || maxDisplay <= 0) {\n throw new RangeError(`maxDisplay must be a positive integer, received ${maxDisplay}`);\n }\n\n const values = explanation.values[sampleIndex];\n const baseValue = explanation.baseValues[sampleIndex];\n const modelOutput = baseValue + values.reduce((sum, value) => sum + value, 0);\n const order = orderFeatures(values.map(Math.abs));\n const visibleLimit = Math.min(maxDisplay, explanation.nFeatures);\n const hasOtherRow = visibleLimit < explanation.nFeatures;\n const realCount = hasOtherRow && faithfulOtherRow ? visibleLimit - 1 : visibleLimit;\n const rowCount = realCount + (hasOtherRow ? 1 : 0);\n\n let location = modelOutput;\n const rows: WaterfallRow[] = [];\n for (let rank = 0; rank < realCount; rank++) {\n const featureIndex = order[rank];\n const value = values[featureIndex];\n location -= value;\n rows.push({\n label: formatFeatureLabel(explanation.featureNames[featureIndex]),\n featureIndex,\n isOtherRow: false,\n value,\n left: location,\n width: value,\n row: rowCount - 1 - rank,\n color: colorFor(value),\n });\n }\n\n const collapsed = order.slice(realCount);\n if (hasOtherRow) {\n const value = collapsed.reduce((sum, featureIndex) => sum + values[featureIndex], 0);\n rows.push({\n label: `${collapsed.length} other features`,\n featureIndex: null,\n isOtherRow: true,\n value,\n left: baseValue,\n width: value,\n row: 0,\n color: colorFor(value),\n });\n }\n\n return {\n rows,\n baseValue,\n modelOutput,\n collapsedCount: hasOtherRow ? collapsed.length : 0,\n };\n}\n\nexport function waterfallLayout(\n valueRows: WaterfallRows,\n opts: WaterfallLayoutOptions,\n): WaterfallLayout {\n const { width, rowHeight, marginLeft, marginRight, marginTop } = opts;\n const plotWidth = width - marginLeft - marginRight;\n const coordinates = [valueRows.baseValue, valueRows.modelOutput];\n for (const row of valueRows.rows) coordinates.push(row.left, row.left + row.width);\n const min = Math.min(...coordinates);\n const max = Math.max(...coordinates);\n const span = max - min || 1;\n const toX = (value: number) => marginLeft + ((value - min) / span) * plotWidth;\n\n const barHeight = rowHeight * BAR_THICKNESS_RATIO;\n const inset = (rowHeight - barHeight) / 2;\n const arrows = valueRows.rows.map((row, index): WaterfallArrowGeometry => {\n const y = marginTop + index * rowHeight + inset;\n const centerY = marginTop + index * rowHeight + rowHeight / 2;\n const startX = toX(row.left);\n const endX = toX(row.left + row.width);\n const headLength = Math.min(Math.abs(endX - startX), WATERFALL_HEAD_LENGTH_PX);\n const neckX = row.width < 0 ? endX + headLength : endX - headLength;\n const points = [\n { x: startX, y },\n { x: neckX, y },\n { x: endX, y: centerY },\n { x: neckX, y: y + barHeight },\n { x: startX, y: y + barHeight },\n ];\n return {\n ...row,\n points,\n startX,\n endX,\n y,\n centerY,\n height: barHeight,\n headLength,\n valueLabel: placeValueLabel(row.value, startX, endX, marginLeft, opts.decimals),\n };\n });\n\n const plotBottom = marginTop + valueRows.rows.length * rowHeight;\n const { ticks, step } = niceTicks(min, max, tickSpace(plotWidth, TICK_LABEL_PT));\n\n // SHAP draws a connector per *individually* plotted Feature, so the Other row\n // never gets one below it. Without an Other row the condition becomes\n // `i + 4 < num_individual`, dropping the last four: one would be enough to\n // keep the line off the axis, and the 4 is unexplained in SHAP. Reproduced\n // as found rather than corrected, because this is a faithful detail.\n const individualCount = valueRows.rows.filter((row) => !row.isOtherRow).length;\n const hasOtherRow = individualCount < valueRows.rows.length;\n const connectors: WaterfallConnector[] = [];\n for (let index = 0; index < individualCount; index++) {\n if (!hasOtherRow && index + 4 >= individualCount) continue;\n connectors.push({\n x: arrows[index].startX,\n y1: marginTop + index * rowHeight + inset,\n y2: marginTop + (index + 1) * rowHeight + inset + barHeight,\n });\n }\n return {\n arrows,\n connectors,\n xTicks: ticks.map((value) => ({\n value,\n x: toX(value),\n label: tickLabel(value, step, opts.decimals === \"percent\"),\n })),\n axisMarks: [\n {\n kind: \"base\",\n value: valueRows.baseValue,\n x: toX(valueRows.baseValue),\n // axvline(base_values, 0, 1 / num_features) — one row tall, not full\n // height. Drawn the full height it reads as a y axis the chart has not\n // got, which is the whole reason SHAP hides the left spine.\n y1: plotBottom - rowHeight,\n y2: plotBottom,\n label: `E[f(X)] = ${formatLevel(valueRows.baseValue, opts.decimals)}`,\n },\n {\n kind: \"output\",\n value: valueRows.modelOutput,\n x: toX(valueRows.modelOutput),\n // axvline(fx, 0, 1) — the full height.\n y1: marginTop,\n y2: plotBottom,\n label: `f(x) = ${formatLevel(valueRows.modelOutput, opts.decimals)}`,\n },\n ],\n separators: valueRows.rows.map((_, index) => ({\n y: marginTop + index * rowHeight + rowHeight / 2,\n x1: marginLeft,\n x2: marginLeft + plotWidth,\n })),\n xDomain: [min, max],\n plotWidth,\n plotLeft: marginLeft,\n plotRight: marginLeft + plotWidth,\n plotBottom,\n height: plotBottom + AXIS_HEIGHT,\n };\n}\n","{\n \"note\": \"256-entry sRGB lookup tables dumped from shap.plots.colors. Interpolate linearly between entries; do not re-derive from Lch.\",\n \"red_blue\": [\n \"#008bfb\",\n \"#008afb\",\n \"#0089fa\",\n \"#0089fa\",\n \"#0088fa\",\n \"#0088fa\",\n \"#0087fa\",\n \"#0087fa\",\n \"#0086fa\",\n \"#0086f9\",\n \"#0085f9\",\n \"#0085f9\",\n \"#0084f9\",\n \"#0083f8\",\n \"#0083f8\",\n \"#0082f8\",\n \"#0082f8\",\n \"#0081f8\",\n \"#0081f7\",\n \"#0080f7\",\n \"#0080f7\",\n \"#007ff6\",\n \"#007ef6\",\n \"#007ef6\",\n \"#007df6\",\n \"#007df5\",\n \"#007cf5\",\n \"#007bf5\",\n \"#007bf4\",\n \"#007af4\",\n \"#007af4\",\n \"#0079f3\",\n \"#0078f3\",\n \"#0078f2\",\n \"#0077f2\",\n \"#0076f2\",\n \"#0076f1\",\n \"#0075f1\",\n \"#0075f0\",\n \"#0074f0\",\n \"#0073f0\",\n \"#0073ef\",\n \"#0072ef\",\n \"#0071ee\",\n \"#0071ee\",\n \"#0070ed\",\n \"#006fed\",\n \"#066fec\",\n \"#0f6eec\",\n \"#196deb\",\n \"#1e6deb\",\n \"#236cea\",\n \"#286bea\",\n \"#2c6be9\",\n \"#306ae9\",\n \"#3369e8\",\n \"#3769e8\",\n \"#3a68e7\",\n \"#3c67e6\",\n \"#3f66e6\",\n \"#4266e5\",\n \"#4465e5\",\n \"#4764e4\",\n \"#4964e3\",\n \"#4b63e3\",\n \"#4d62e2\",\n \"#5061e2\",\n \"#5261e1\",\n \"#5460e0\",\n \"#565fe0\",\n \"#575edf\",\n \"#595ede\",\n \"#5b5dde\",\n \"#5d5cdd\",\n \"#5f5bdc\",\n \"#605bdb\",\n \"#625adb\",\n \"#6359da\",\n \"#6558d9\",\n \"#6757d9\",\n \"#6856d8\",\n \"#6a56d7\",\n \"#6b55d6\",\n \"#6d54d6\",\n \"#6e53d5\",\n \"#6f52d4\",\n \"#7151d3\",\n \"#7251d2\",\n \"#7350d2\",\n \"#754fd1\",\n \"#764ed0\",\n \"#774dcf\",\n \"#794cce\",\n \"#7a4bce\",\n \"#7b4acd\",\n \"#7c49cc\",\n \"#7d48cb\",\n \"#7e48ca\",\n \"#8047c9\",\n \"#8146c8\",\n \"#8245c7\",\n \"#8344c7\",\n \"#8443c6\",\n \"#8542c5\",\n \"#8641c4\",\n \"#8740c3\",\n \"#883fc2\",\n \"#893ec1\",\n \"#8a3cc0\",\n \"#8b3bbf\",\n \"#8c3abe\",\n \"#8d39bd\",\n \"#8e38bc\",\n \"#8f37bb\",\n \"#9036ba\",\n \"#9134b9\",\n \"#9233b8\",\n \"#9232b8\",\n \"#9331b7\",\n \"#942fb6\",\n \"#952eb4\",\n \"#962db3\",\n \"#972bb2\",\n \"#972ab1\",\n \"#9828b0\",\n \"#9927af\",\n \"#9a25ae\",\n \"#9b24ae\",\n \"#9c23ad\",\n \"#9d22ac\",\n \"#9e21ac\",\n \"#a020ab\",\n \"#a11fab\",\n \"#a21eaa\",\n \"#a41daa\",\n \"#a51ca9\",\n \"#a61ba9\",\n \"#a719a8\",\n \"#a918a8\",\n \"#aa17a7\",\n \"#ab16a7\",\n \"#ac14a6\",\n \"#ae13a6\",\n \"#af11a5\",\n \"#b010a5\",\n \"#b10ea4\",\n \"#b20ca4\",\n \"#b40aa3\",\n \"#b507a2\",\n \"#b605a2\",\n \"#b703a1\",\n \"#b802a1\",\n \"#b900a0\",\n \"#bb00a0\",\n \"#bc009f\",\n \"#bd009e\",\n \"#be009e\",\n \"#bf009d\",\n \"#c0009d\",\n \"#c1009c\",\n \"#c2009b\",\n \"#c3009b\",\n \"#c4009a\",\n \"#c50099\",\n \"#c60099\",\n \"#c70098\",\n \"#c90097\",\n \"#ca0097\",\n \"#cb0096\",\n \"#cc0096\",\n \"#cd0095\",\n \"#cd0094\",\n \"#ce0093\",\n \"#cf0093\",\n \"#d00092\",\n \"#d10091\",\n \"#d20091\",\n \"#d30090\",\n \"#d4008f\",\n \"#d5008f\",\n \"#d6008e\",\n \"#d7008d\",\n \"#d8008d\",\n \"#d9008c\",\n \"#da008b\",\n \"#da008a\",\n \"#db008a\",\n \"#dc0089\",\n \"#dd0088\",\n \"#de0087\",\n \"#df0087\",\n \"#df0086\",\n \"#e00085\",\n \"#e10084\",\n \"#e20084\",\n \"#e30083\",\n \"#e30082\",\n \"#e40081\",\n \"#e50081\",\n \"#e60080\",\n \"#e6007f\",\n \"#e7007e\",\n \"#e8007d\",\n \"#e9007d\",\n \"#e9007c\",\n \"#ea007b\",\n \"#eb007a\",\n \"#eb007a\",\n \"#ec0079\",\n \"#ed0078\",\n \"#ed0077\",\n \"#ee0076\",\n \"#ef0076\",\n \"#ef0075\",\n \"#f00074\",\n \"#f10073\",\n \"#f10072\",\n \"#f20071\",\n \"#f20071\",\n \"#f30070\",\n \"#f4006f\",\n \"#f4006e\",\n \"#f5006d\",\n \"#f5006d\",\n \"#f6006c\",\n \"#f6006b\",\n \"#f7006a\",\n \"#f70069\",\n \"#f80068\",\n \"#f80068\",\n \"#f90067\",\n \"#f90066\",\n \"#fa0065\",\n \"#fa0064\",\n \"#fb0063\",\n \"#fb0062\",\n \"#fc0062\",\n \"#fc0061\",\n \"#fd0060\",\n \"#fd005f\",\n \"#fd005e\",\n \"#fe005d\",\n \"#fe005c\",\n \"#fe005c\",\n \"#ff005b\",\n \"#ff005a\",\n \"#ff0059\",\n \"#ff0058\",\n \"#ff0057\",\n \"#ff0056\",\n \"#ff0055\",\n \"#ff0055\",\n \"#ff0054\",\n \"#ff0053\",\n \"#ff0052\",\n \"#ff0051\"\n ],\n \"red_white_blue\": [\n \"#008bfb\",\n \"#028bfb\",\n \"#048cfb\",\n \"#068dfb\",\n \"#088efb\",\n \"#0a8ffb\",\n \"#0c90fb\",\n \"#0e91fb\",\n \"#1092fb\",\n \"#1293fb\",\n \"#1494fb\",\n \"#1695fb\",\n \"#1896fb\",\n \"#1a96fb\",\n \"#1c97fb\",\n \"#1e98fb\",\n \"#2099fb\",\n \"#229afb\",\n \"#249bfb\",\n \"#269cfb\",\n \"#289dfb\",\n \"#2a9efb\",\n \"#2c9ffb\",\n \"#2ea0fc\",\n \"#30a1fc\",\n \"#32a2fc\",\n \"#34a2fc\",\n \"#36a3fc\",\n \"#38a4fc\",\n \"#3aa5fc\",\n \"#3ca6fc\",\n \"#3ea7fc\",\n \"#40a8fc\",\n \"#42a9fc\",\n \"#44aafc\",\n \"#46abfc\",\n \"#48acfc\",\n \"#4aadfc\",\n \"#4cadfc\",\n \"#4eaefc\",\n \"#50affc\",\n \"#52b0fc\",\n \"#54b1fc\",\n \"#56b2fc\",\n \"#58b3fc\",\n \"#5ab4fc\",\n \"#5cb5fc\",\n \"#5eb6fc\",\n \"#60b7fc\",\n \"#62b8fc\",\n \"#65b8fc\",\n \"#67b9fc\",\n \"#69bafc\",\n \"#6bbbfd\",\n \"#6dbcfd\",\n \"#6fbdfd\",\n \"#71befd\",\n \"#73bffd\",\n \"#75c0fd\",\n \"#77c1fd\",\n \"#79c2fd\",\n \"#7bc3fd\",\n \"#7dc3fd\",\n \"#7fc4fd\",\n \"#81c5fd\",\n \"#83c6fd\",\n \"#85c7fd\",\n \"#87c8fd\",\n \"#89c9fd\",\n \"#8bcafd\",\n \"#8dcbfd\",\n \"#8fccfd\",\n \"#91cdfd\",\n \"#93cefd\",\n \"#95cefd\",\n \"#97cffd\",\n \"#99d0fd\",\n \"#9bd1fd\",\n \"#9dd2fd\",\n \"#9fd3fd\",\n \"#a1d4fd\",\n \"#a3d5fd\",\n \"#a5d6fe\",\n \"#a7d7fe\",\n \"#a9d8fe\",\n \"#abd9fe\",\n \"#add9fe\",\n \"#afdafe\",\n \"#b1dbfe\",\n \"#b3dcfe\",\n \"#b5ddfe\",\n \"#b7defe\",\n \"#b9dffe\",\n \"#bbe0fe\",\n \"#bde1fe\",\n \"#bfe2fe\",\n \"#c1e3fe\",\n \"#c3e4fe\",\n \"#c5e5fe\",\n \"#c7e5fe\",\n \"#c9e6fe\",\n \"#cbe7fe\",\n \"#cde8fe\",\n \"#cfe9fe\",\n \"#d1eafe\",\n \"#d3ebfe\",\n \"#d5ecfe\",\n \"#d7edfe\",\n \"#d9eefe\",\n \"#dbeffe\",\n \"#ddf0fe\",\n \"#dff0fe\",\n \"#e1f1ff\",\n \"#e3f2ff\",\n \"#e5f3ff\",\n \"#e7f4ff\",\n \"#e9f5ff\",\n \"#ebf6ff\",\n \"#edf7ff\",\n \"#eff8ff\",\n \"#f1f9ff\",\n \"#f3faff\",\n \"#f5fbff\",\n \"#f7fbff\",\n \"#f9fcff\",\n \"#fbfdff\",\n \"#fdfeff\",\n \"#ffffff\",\n \"#ffffff\",\n \"#fffdfe\",\n \"#fffbfc\",\n \"#fff9fb\",\n \"#fff7fa\",\n \"#fff5f8\",\n \"#fff3f7\",\n \"#fff1f6\",\n \"#ffeff4\",\n \"#ffedf3\",\n \"#ffebf1\",\n \"#ffe9f0\",\n \"#ffe7ef\",\n \"#ffe5ed\",\n \"#ffe3ec\",\n \"#ffe1eb\",\n \"#ffdfe9\",\n \"#ffdde8\",\n \"#ffdbe7\",\n \"#ffd9e5\",\n \"#ffd7e4\",\n \"#ffd5e2\",\n \"#ffd3e1\",\n \"#ffd1e0\",\n \"#ffcfde\",\n \"#ffcddd\",\n \"#ffcbdc\",\n \"#ffc9da\",\n \"#ffc7d9\",\n \"#ffc5d7\",\n \"#ffc3d6\",\n \"#ffc1d5\",\n \"#ffbfd3\",\n \"#ffbdd2\",\n \"#ffbbd1\",\n \"#ffb9cf\",\n \"#ffb7ce\",\n \"#ffb5cc\",\n \"#ffb3cb\",\n \"#ffb1ca\",\n \"#ffafc8\",\n \"#ffadc7\",\n \"#ffabc6\",\n \"#ffa9c4\",\n \"#ffa7c3\",\n \"#ffa5c1\",\n \"#ffa3c0\",\n \"#ffa1bf\",\n \"#ff9fbd\",\n \"#ff9dbc\",\n \"#ff9bbb\",\n \"#ff99b9\",\n \"#ff97b8\",\n \"#ff95b7\",\n \"#ff93b5\",\n \"#ff91b4\",\n \"#ff8fb2\",\n \"#ff8db1\",\n \"#ff8bb0\",\n \"#ff89ae\",\n \"#ff87ad\",\n \"#ff85ac\",\n \"#ff83aa\",\n \"#ff81a9\",\n \"#ff7fa7\",\n \"#ff7da6\",\n \"#ff7ba5\",\n \"#ff79a3\",\n \"#ff77a2\",\n \"#ff75a1\",\n \"#ff739f\",\n \"#ff719e\",\n \"#ff6f9c\",\n \"#ff6d9b\",\n \"#ff6b9a\",\n \"#ff6998\",\n \"#ff6797\",\n \"#ff6596\",\n \"#ff6294\",\n \"#ff6093\",\n \"#ff5e92\",\n \"#ff5c90\",\n \"#ff5a8f\",\n \"#ff588d\",\n \"#ff568c\",\n \"#ff548b\",\n \"#ff5289\",\n \"#ff5088\",\n \"#ff4e87\",\n \"#ff4c85\",\n \"#ff4a84\",\n \"#ff4882\",\n \"#ff4681\",\n \"#ff4480\",\n \"#ff427e\",\n \"#ff407d\",\n \"#ff3e7c\",\n \"#ff3c7a\",\n \"#ff3a79\",\n \"#ff3877\",\n \"#ff3676\",\n \"#ff3475\",\n \"#ff3273\",\n \"#ff3072\",\n \"#ff2e71\",\n \"#ff2c6f\",\n \"#ff2a6e\",\n \"#ff286d\",\n \"#ff266b\",\n \"#ff246a\",\n \"#ff2268\",\n \"#ff2067\",\n \"#ff1e66\",\n \"#ff1c64\",\n \"#ff1a63\",\n \"#ff1862\",\n \"#ff1660\",\n \"#ff145f\",\n \"#ff125d\",\n \"#ff105c\",\n \"#ff0e5b\",\n \"#ff0c59\",\n \"#ff0a58\",\n \"#ff0857\",\n \"#ff0655\",\n \"#ff0454\",\n \"#ff0252\",\n \"#ff0051\"\n ],\n \"nan_grey\": \"#848484\"\n}","// Runtime data, not a test fixture: the lookup tables SHAP's own colour maps\n// resolve to, captured by running shap 0.49.1. Kept beside the code so the\n// published package does not depend on the fixtures directory.\nimport colormaps from \"./colormaps.json\";\n\nexport type ColormapName = \"red_blue\" | \"red_white_blue\";\n\nconst tables = colormaps as Record<ColormapName, string[]>;\n\nfunction channel(hex: string, offset: number): number {\n return Number.parseInt(hex.slice(offset, offset + 2), 16);\n}\n\nfunction hexByte(value: number): string {\n return Math.round(value).toString(16).padStart(2, \"0\");\n}\n\n/** Samples a captured SHAP colour map, interpolating adjacent LUT entries in sRGB. */\nexport function sampleColormap(name: ColormapName, t: number): string {\n if (!Number.isFinite(t)) {\n throw new RangeError(`colormap position must be finite, received ${String(t)}`);\n }\n\n const table = tables[name];\n if (!table) throw new RangeError(`unknown colormap ${String(name)}`);\n\n const position = Math.max(0, Math.min(1, t)) * (table.length - 1);\n const lowerIndex = Math.floor(position);\n const upperIndex = Math.ceil(position);\n const fraction = position - lowerIndex;\n const lower = table[lowerIndex];\n const upper = table[upperIndex];\n\n const red = channel(lower, 1) + (channel(upper, 1) - channel(lower, 1)) * fraction;\n const green = channel(lower, 3) + (channel(upper, 3) - channel(lower, 3)) * fraction;\n const blue = channel(lower, 5) + (channel(upper, 5) - channel(lower, 5)) * fraction;\n return `#${hexByte(red)}${hexByte(green)}${hexByte(blue)}`;\n}\n","import { collapseToDisplay } from \"./collapse\";\nimport { sampleColormap } from \"./colormap\";\nimport { RowSort, sortDisplayRows } from \"./rowSort\";\nimport {\n AXIS_TITLE_DY,\n AxisSpine,\n AxisTick,\n AxisTitle,\n niceTicks,\n tickLabel,\n tickSpace,\n} from \"./ticks\";\nimport { globalImportance, orderFeatures } from \"./order\";\nimport { ParsedExplanation } from \"./types\";\n\nexport const BEESWARM_MISSING_COLOR = \"#777777\";\nexport const BEESWARM_ROW_HEIGHT = 0.4;\nconst NBINS = 100;\n/**\n * Room below the rows for ticks, their labels and the axis title — and below\n * those, a band for the hover colour legend, which would otherwise sit on the\n * title's right end.\n */\nconst AXIS_HEIGHT = 74;\n/** _beeswarm.py:499 tick_params(\"x\", labelsize=11). */\nconst TICK_LABEL_PT = 11;\n/** _beeswarm.py:501 set_xlabel(..., fontsize=13). */\nconst TITLE_PT = 13;\n/** matplotlib's default axes.xmargin; _beeswarm.py never sets xlim itself. */\nconst X_MARGIN = 0.05;\n\nexport type BeeswarmPoint = {\n sampleIndex: number;\n /** SHAP value in value space. */\n x: number;\n /** SHAP-compatible row coordinate, including signed vertical jitter. */\n y: number;\n /** Original Feature value before percentile clipping. */\n featureValue: number;\n /** Value sent through the colour map, or null when the Feature value is missing. */\n colorValue: number | null;\n color: string;\n};\n\nexport type BeeswarmRow = {\n label: string;\n featureIndex: number | null;\n isOtherRow: boolean;\n /** SHAP row number, counted upward from the bottom. */\n rowIndex: number;\n vmin: number;\n vmax: number;\n points: BeeswarmPoint[];\n};\n\nexport type BeeswarmRows = {\n /** Rows are ordered top-to-bottom. */\n rows: BeeswarmRow[];\n collapsedCount: number;\n};\n\nexport type BeeswarmLayoutOptions = {\n width: number;\n rowHeight: number;\n marginLeft: number;\n marginRight: number;\n marginTop: number;\n dotRadius: number;\n};\n\nexport type BeeswarmPointGeometry = Omit<BeeswarmPoint, \"x\" | \"y\"> & {\n x: number;\n y: number;\n valueX: number;\n valueY: number;\n radius: number;\n};\n\nexport type BeeswarmRowGeometry = Omit<BeeswarmRow, \"points\"> & {\n centerY: number;\n points: BeeswarmPointGeometry[];\n};\n\nexport type BeeswarmLayout = {\n rows: BeeswarmRowGeometry[];\n xDomain: [number, number];\n xZero: number;\n plotWidth: number;\n plotBottom: number;\n xTicks: AxisTick[];\n /** The bottom spine, which _beeswarm.py:493-495 leaves visible. */\n xSpine: AxisSpine | null;\n xTitle: AxisTitle;\n height: number;\n};\n\nfunction percentile(values: number[], percent: number): number {\n const finite = values.filter(Number.isFinite).sort((a, b) => a - b);\n if (finite.length === 0) return 0;\n const position = (finite.length - 1) * percent / 100;\n const lower = Math.floor(position);\n const upper = Math.ceil(position);\n const fraction = position - lower;\n return finite[lower] + (finite[upper] - finite[lower]) * fraction;\n}\n\nfunction colorDomain(featureValues: number[]): [number, number] {\n let vmin = percentile(featureValues, 5);\n let vmax = percentile(featureValues, 95);\n if (vmin === vmax) {\n vmin = percentile(featureValues, 1);\n vmax = percentile(featureValues, 99);\n }\n if (vmin === vmax) {\n const finite = featureValues.filter(Number.isFinite);\n if (finite.length > 0) {\n vmin = Math.min(...finite);\n vmax = Math.max(...finite);\n }\n }\n if (vmin > vmax) vmin = vmax;\n return [vmin, vmax];\n}\n\n/** Small deterministic PRNG used only to resolve points tied in the same x bin. */\nfunction seededRandom(seed: number): () => number {\n let state = Math.trunc(seed) >>> 0;\n return () => {\n state = (state + 0x6d2b79f5) >>> 0;\n let value = state;\n value = Math.imul(value ^ (value >>> 15), value | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return ((value ^ (value >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** JavaScript rounds .5 upward; NumPy rounds an exact .5 to the nearest even integer. */\nfunction numpyRound(value: number): number {\n const lower = Math.floor(value);\n if (value - lower === 0.5) return lower % 2 === 0 ? lower : lower + 1;\n return Math.round(value);\n}\n\nfunction spreadPoints(xs: number[], rowIndex: number, seed: number): number[] {\n const min = Math.min(...xs);\n const max = Math.max(...xs);\n const quantized = xs.map((x) => numpyRound(NBINS * (x - min) / (max - min + 1e-8)));\n const random = seededRandom(seed);\n const order = quantized\n .map((bin, index) => ({ bin, index, tieBreak: random() }))\n .sort((a, b) => (a.bin - b.bin) || (a.tieBreak - b.tieBreak))\n .map((entry) => entry.index);\n\n const offsets = new Array<number>(xs.length).fill(0);\n let layer = 0;\n let lastBin = -1;\n for (const index of order) {\n const bin = quantized[index];\n if (bin !== lastBin) layer = 0;\n offsets[index] = Math.ceil(layer / 2) * ((layer % 2) * 2 - 1);\n layer += 1;\n lastBin = bin;\n }\n\n const maxPositiveOffset = Math.max(0, ...offsets);\n const scale = 0.9 * (BEESWARM_ROW_HEIGHT / (maxPositiveOffset + 1));\n return offsets.map((offset) => rowIndex + offset * scale);\n}\n\n/**\n * Computes SHAP-compatible beeswarm rows entirely in value space. Pixel scaling belongs in\n * beeswarmLayout so golden values can be compared without a viewport.\n */\nexport function beeswarmRows(\n explanation: ParsedExplanation,\n maxDisplay: number,\n faithfulOtherRow: boolean,\n seed = 0,\n rowSort: RowSort = \"importance\",\n): BeeswarmRows {\n if (!Number.isInteger(maxDisplay) || maxDisplay <= 0) {\n throw new RangeError(`maxDisplay must be a positive integer, received ${maxDisplay}`);\n }\n if (!Number.isFinite(seed)) {\n throw new RangeError(`seed must be finite, received ${String(seed)}`);\n }\n\n const importance = globalImportance(explanation);\n const order = orderFeatures(importance);\n // Which rows are shown is decided by importance; rowSort only reorders them.\n const display = sortDisplayRows(\n collapseToDisplay(\n explanation.featureNames,\n importance,\n order,\n maxDisplay,\n faithfulOtherRow,\n ),\n rowSort,\n explanation.data,\n );\n const visibleFeatures = new Set(\n display.rows.flatMap((row) => row.featureIndex === null ? [] : [row.featureIndex]),\n );\n const collapsedFeatures = order.filter((featureIndex) => !visibleFeatures.has(featureIndex));\n\n const rows = display.rows.map((displayRow, displayIndex): BeeswarmRow => {\n const featureIndices = displayRow.featureIndex === null\n ? collapsedFeatures\n : [displayRow.featureIndex];\n // SHAP's faithful collapse mutates only the SHAP-value matrix. Its colour values remain those\n // of the Feature at the absorbed rank. Corrected mode has a genuinely separate Other row and\n // therefore sums the hidden Feature values as specified for that mode.\n const colorFeatureIndices = displayRow.featureIndex === null && faithfulOtherRow\n ? collapsedFeatures.slice(0, 1)\n : featureIndices;\n const xs = explanation.values.map((sample) =>\n featureIndices.reduce((sum, featureIndex) => sum + sample[featureIndex], 0));\n const featureValues = explanation.data.map((sample) =>\n colorFeatureIndices.reduce((sum, featureIndex) => sum + sample[featureIndex], 0));\n const rowIndex = display.rows.length - 1 - displayIndex;\n const ys = spreadPoints(xs, rowIndex, seed + Math.imul(displayIndex + 1, 0x9e3779b1));\n const [vmin, vmax] = colorDomain(featureValues);\n const colorSpan = vmax - vmin;\n\n return {\n label: displayRow.label,\n featureIndex: displayRow.featureIndex,\n isOtherRow: displayRow.isOtherRow,\n rowIndex,\n vmin,\n vmax,\n points: xs.map((x, sampleIndex): BeeswarmPoint => {\n const featureValue = featureValues[sampleIndex];\n if (!Number.isFinite(featureValue)) {\n return {\n sampleIndex,\n x,\n y: ys[sampleIndex],\n featureValue,\n colorValue: null,\n color: BEESWARM_MISSING_COLOR,\n };\n }\n const colorValue = Math.max(vmin, Math.min(vmax, featureValue));\n const normalized = colorSpan === 0 ? 0 : (colorValue - vmin) / colorSpan;\n return {\n sampleIndex,\n x,\n y: ys[sampleIndex],\n featureValue,\n colorValue,\n color: sampleColormap(\"red_blue\", normalized),\n };\n }),\n };\n });\n\n return { rows, collapsedCount: display.collapsedCount };\n}\n\nfunction beeswarmXAxis(\n min: number,\n max: number,\n toX: (value: number) => number,\n marginLeft: number,\n plotWidth: number,\n plotBottom: number,\n): Pick<BeeswarmLayout, \"xTicks\" | \"xSpine\" | \"xTitle\"> {\n const { ticks, step } = niceTicks(min, max, tickSpace(plotWidth, TICK_LABEL_PT));\n return {\n xTicks: ticks.map((value) => ({ value, x: toX(value), label: tickLabel(value, step) })),\n xSpine: { x1: marginLeft, x2: marginLeft + plotWidth, y: plotBottom },\n xTitle: {\n // _labels.py:5, labels[\"VALUE\"].\n text: \"SHAP value (impact on model output)\",\n x: marginLeft + plotWidth / 2,\n y: plotBottom + AXIS_TITLE_DY,\n fontSize: TITLE_PT,\n },\n };\n}\n\n/** Projects beeswarm value-space rows into SVG coordinates. */\nexport function beeswarmLayout(\n valueRows: BeeswarmRows,\n opts: BeeswarmLayoutOptions,\n): BeeswarmLayout {\n const { width, rowHeight, marginLeft, marginRight, marginTop, dotRadius } = opts;\n const plotWidth = width - marginLeft - marginRight;\n const values = valueRows.rows.flatMap((row) => row.points.map((point) => point.x));\n const dataMin = Math.min(0, ...values);\n const dataMax = Math.max(0, ...values);\n // SHAP never sets the beeswarm's xlim, so matplotlib's default 5% margin\n // applies on both sides. That is what lets the axis reach a round tick just\n // past the outermost dot, as SHAP's own figures do.\n const margin = (dataMax - dataMin) * X_MARGIN;\n const min = dataMin - margin;\n const max = dataMax + margin;\n const span = max - min || 1;\n const toX = (value: number) => marginLeft + (value - min) / span * plotWidth;\n const highestRowIndex = Math.max(0, valueRows.rows.length - 1);\n\n const rows = valueRows.rows.map((row): BeeswarmRowGeometry => {\n const centerY = marginTop + (highestRowIndex - row.rowIndex + 0.5) * rowHeight;\n return {\n ...row,\n centerY,\n points: row.points.map((point): BeeswarmPointGeometry => ({\n ...point,\n valueX: point.x,\n valueY: point.y,\n x: toX(point.x),\n y: centerY - (point.y - row.rowIndex) * rowHeight,\n radius: dotRadius,\n })),\n };\n });\n\n const plotBottom = marginTop + valueRows.rows.length * rowHeight;\n return {\n rows,\n xDomain: [min, max],\n xZero: toX(0),\n ...beeswarmXAxis(min, max, toX, marginLeft, plotWidth, plotBottom),\n plotWidth,\n plotBottom,\n height: plotBottom + AXIS_HEIGHT,\n };\n}\n","import { collapseToDisplay } from \"./collapse\";\nimport { sampleColormap } from \"./colormap\";\nimport { formatShapValue } from \"./format\";\nimport { RowSort, sortDisplayRows } from \"./rowSort\";\nimport {\n AXIS_TITLE_DY,\n AxisSpine,\n AxisTick,\n AxisTitle,\n niceTicks,\n tickLabel,\n tickSpace,\n} from \"./ticks\";\nimport { globalImportance, orderFeatures } from \"./order\";\nimport { ParsedExplanation } from \"./types\";\n\nconst FX_TOP = 8;\nconst FX_BOTTOM_GAP = 12;\nconst SEPARATOR_GAP = 4;\nconst SIDE_BAR_GAP = 10;\nconst SIDE_BAR_RIGHT_INSET = 40;\nconst SIDE_BAR_HEIGHT_RATIO = 0.6;\n/** Room below the grid for ticks, their labels and the Instances title. */\nconst AXIS_HEIGHT = 52;\n/** _heatmap.py leaves x tick labels at matplotlib's default \"medium\", 10 pt. */\nconst TICK_LABEL_PT = 10;\n\nexport type HeatmapCell = {\n /** Original index in the Explanation, before Sample ordering. */\n sampleIndex: number;\n /** Full-precision SHAP value represented by this cell. */\n value: number;\n /** Value after clipping to the shared symmetric colour domain. */\n colorValue: number;\n color: string;\n};\n\nexport type HeatmapColumn = {\n sampleIndex: number;\n /** The record UUID: the key for click-through, never shown to a person. */\n sampleId?: string;\n /** What a person reads for this Sample, when the payload carries labels. */\n sampleLabel?: string;\n /** Sum of all SHAP values for this Sample. */\n total: number;\n};\n\nexport type HeatmapRow = {\n label: string;\n featureIndex: number | null;\n isOtherRow: boolean;\n importance: number;\n /** Importance divided by the largest displayed-row importance. */\n sideBarValue: number;\n cells: HeatmapCell[];\n};\n\nexport type HeatmapRows = {\n /** Header of the uploaded column the labels came from, when it had one. */\n sampleLabelColumn?: string;\n /** Feature rows in top-to-bottom display order. */\n rows: HeatmapRow[];\n /** Sample columns in descending total-attribution order. */\n columns: HeatmapColumn[];\n /** Sample totals in the same order as columns. */\n fxLine: number[];\n vmin: number;\n vmax: number;\n collapsedCount: number;\n};\n\nexport type HeatmapLayoutOptions = {\n width: number;\n rowHeight: number;\n marginLeft: number;\n marginRight: number;\n /** Top of the matrix; the f(x) line occupies the space above it. */\n marginTop: number;\n};\n\nexport type HeatmapCellGeometry = HeatmapCell & {\n x: number;\n y: number;\n width: number;\n height: number;\n};\n\nexport type HeatmapSideBarGeometry = {\n value: number;\n x: number;\n y: number;\n width: number;\n height: number;\n};\n\nexport type HeatmapRowGeometry = Omit<HeatmapRow, \"cells\"> & {\n centerY: number;\n cells: HeatmapCellGeometry[];\n sideBar: HeatmapSideBarGeometry;\n};\n\nexport type HeatmapColumnGeometry = HeatmapColumn & {\n x: number;\n centerX: number;\n width: number;\n};\n\nexport type HeatmapLinePoint = {\n sampleIndex: number;\n sampleId?: string;\n value: number;\n x: number;\n y: number;\n};\n\nexport type HeatmapAxisMark = {\n value: number;\n y: number;\n label: string;\n};\n\nexport type HeatmapSpine = { x: number; y1: number; y2: number };\n\n/** An outward tick on the left edge, one per Feature row. */\nexport type HeatmapYTick = { y: number; x1: number; x2: number };\n\n/** matplotlib's default major tick, 3.5 pt, at the 100 dpi SHAP renders at. */\nconst Y_TICK_LENGTH = 5;\n\nexport type HeatmapLayout = {\n sampleLabelColumn?: string;\n rows: HeatmapRowGeometry[];\n columns: HeatmapColumnGeometry[];\n fxLine: HeatmapLinePoint[];\n fxAxisMarks: HeatmapAxisMark[];\n fxDomain: [number, number];\n separatorY: number;\n gridLeft: number;\n gridRight: number;\n gridTop: number;\n plotBottom: number;\n /**\n * _heatmap.py:135 shows the left and right spines (and hides top and bottom),\n * and :136 bounds them with set_bounds(n - row_height, -row_height). With\n * row_height = 0.5 (:116) that is exactly the outer edge of the first and last\n * row — so they frame the grid and stop short of the f(x) chart above it. The\n * side bars are drawn with clip_on=False (:173), outside the right spine.\n */\n spines: { left: HeatmapSpine; right: HeatmapSpine };\n /** yaxis.set_ticks_position(\"left\") with tick_params(direction=\"out\"), :134,:138. */\n yTicks: HeatmapYTick[];\n /** Ticks along the Sample axis, at the centre of each ticked column. */\n xTicks: AxisTick[];\n /** Always null: _heatmap.py:137 hides the bottom spine. */\n xSpine: AxisSpine | null;\n xTitle: AxisTitle;\n plotWidth: number;\n cellWidth: number;\n height: number;\n};\n\nfunction percentile(values: number[], fraction: number): number {\n if (values.length === 0) return 0;\n const sorted = [...values].sort((a, b) => a - b);\n const position = (sorted.length - 1) * fraction;\n const lower = Math.floor(position);\n const upper = Math.ceil(position);\n const weight = position - lower;\n return sorted[lower] + (sorted[upper] - sorted[lower]) * weight;\n}\n\n/** Computes the SHAP-compatible orderings, collapse, colours, line, and side bars in value space. */\nexport function heatmapRows(\n explanation: ParsedExplanation,\n maxDisplay: number,\n faithfulOtherRow: boolean,\n rowSort: RowSort = \"importance\",\n): HeatmapRows {\n if (!Number.isInteger(maxDisplay) || maxDisplay <= 0) {\n throw new RangeError(`maxDisplay must be a positive integer, received ${maxDisplay}`);\n }\n\n const importance = globalImportance(explanation);\n const featureOrder = orderFeatures(importance);\n // Which rows are shown is decided by importance; rowSort only reorders them.\n // Sample columns keep SHAP's order either way.\n const display = sortDisplayRows(\n collapseToDisplay(\n explanation.featureNames,\n importance,\n featureOrder,\n maxDisplay,\n faithfulOtherRow,\n ),\n rowSort,\n explanation.data,\n );\n const displayedFeatures = new Set(\n display.rows.flatMap((row) => row.featureIndex === null ? [] : [row.featureIndex]),\n );\n const collapsedFeatures = featureOrder.filter((index) => !displayedFeatures.has(index));\n\n const columns = explanation.values\n .map((sample, sampleIndex): HeatmapColumn => ({\n sampleIndex,\n sampleId: explanation.sampleIds?.[sampleIndex],\n sampleLabel: explanation.sampleLabels?.[sampleIndex],\n total: sample.reduce((sum, value) => sum + value, 0),\n }))\n .sort((a, b) => (b.total - a.total) || (a.sampleIndex - b.sampleIndex));\n\n const uncolouredRows = display.rows.map((displayRow) => {\n const featureIndices = displayRow.featureIndex === null\n ? collapsedFeatures\n : [displayRow.featureIndex];\n return {\n displayRow,\n cells: columns.map((column) => ({\n sampleIndex: column.sampleIndex,\n value: featureIndices.reduce(\n (sum, featureIndex) => sum + explanation.values[column.sampleIndex][featureIndex],\n 0,\n ),\n })),\n };\n });\n\n const allCells = uncolouredRows.flatMap((row) => row.cells.map((cell) => cell.value));\n const lower = percentile(allCells, 0.01);\n const upper = percentile(allCells, 0.99);\n const limit = Math.max(-lower, upper, 0);\n const vmin = limit === 0 ? 0 : -limit;\n const vmax = limit;\n const largestImportance = Math.max(0, ...display.rows.map((row) => row.value));\n\n const rows = uncolouredRows.map(({ displayRow, cells }): HeatmapRow => ({\n label: displayRow.label,\n featureIndex: displayRow.featureIndex,\n isOtherRow: displayRow.isOtherRow,\n importance: displayRow.value,\n sideBarValue: largestImportance === 0 ? 0 : displayRow.value / largestImportance,\n cells: cells.map((cell): HeatmapCell => {\n const colorValue = limit === 0\n ? 0\n : Math.max(-limit, Math.min(limit, cell.value));\n const normalized = limit === 0 ? 0.5 : (colorValue + limit) / (2 * limit);\n return {\n ...cell,\n colorValue,\n color: sampleColormap(\"red_white_blue\", normalized),\n };\n }),\n }));\n\n return {\n rows,\n columns,\n fxLine: columns.map((column) => column.total),\n vmin,\n vmax,\n collapsedCount: display.collapsedCount,\n sampleLabelColumn: explanation.sampleLabelColumn,\n };\n}\n\n/**\n * The Sample axis. xlim(-0.5, n - 0.5) at _heatmap.py:151 puts integer i at the\n * centre of column i, so a tick's position is its column's centre. Integer steps\n * only: the axis counts Samples, and for a handful of them there is no Sample 0.5.\n */\nfunction heatmapXAxis(\n sampleCount: number,\n marginLeft: number,\n cellWidth: number,\n plotWidth: number,\n plotBottom: number,\n): Pick<HeatmapLayout, \"xTicks\" | \"xSpine\" | \"xTitle\"> {\n const { ticks, step } = niceTicks(\n -0.5,\n sampleCount - 0.5,\n tickSpace(plotWidth, TICK_LABEL_PT),\n { integer: true },\n );\n return {\n xTicks: ticks.map((value) => ({\n value,\n x: marginLeft + (value + 0.5) * cellWidth,\n label: tickLabel(value, step),\n })),\n xSpine: null,\n xTitle: {\n text: \"Instances\",\n x: marginLeft + plotWidth / 2,\n y: plotBottom + AXIS_TITLE_DY,\n fontSize: TICK_LABEL_PT,\n },\n };\n}\n\n/** Projects heatmap value-space rows into SVG geometry. */\nexport function heatmapLayout(\n valueRows: HeatmapRows,\n opts: HeatmapLayoutOptions,\n): HeatmapLayout {\n const { width, rowHeight, marginLeft, marginRight, marginTop } = opts;\n const plotWidth = width - marginLeft - marginRight;\n const cellWidth = valueRows.columns.length === 0 ? 0 : plotWidth / valueRows.columns.length;\n const gridRight = marginLeft + plotWidth;\n const plotBottom = marginTop + valueRows.rows.length * rowHeight;\n const sideBarWidth = Math.max(0, marginRight - SIDE_BAR_RIGHT_INSET - SIDE_BAR_GAP);\n\n const columns = valueRows.columns.map((column, index): HeatmapColumnGeometry => ({\n ...column,\n x: marginLeft + index * cellWidth,\n centerX: marginLeft + (index + 0.5) * cellWidth,\n width: cellWidth,\n }));\n\n const rows = valueRows.rows.map((row, rowIndex): HeatmapRowGeometry => {\n const y = marginTop + rowIndex * rowHeight;\n const centerY = y + rowHeight / 2;\n const barHeight = rowHeight * SIDE_BAR_HEIGHT_RATIO;\n return {\n ...row,\n centerY,\n cells: row.cells.map((cell, columnIndex): HeatmapCellGeometry => ({\n ...cell,\n x: marginLeft + columnIndex * cellWidth,\n y,\n width: cellWidth,\n height: rowHeight,\n })),\n sideBar: {\n value: row.sideBarValue,\n x: gridRight + SIDE_BAR_GAP,\n y: centerY - barHeight / 2,\n width: row.sideBarValue * sideBarWidth,\n height: barHeight,\n },\n };\n });\n\n const fxMin = Math.min(0, ...valueRows.fxLine);\n const fxMax = Math.max(0, ...valueRows.fxLine);\n const fxSpan = fxMax - fxMin || 1;\n const fxBottom = Math.max(FX_TOP, marginTop - FX_BOTTOM_GAP);\n const toY = (value: number) => FX_TOP + (fxMax - value) / fxSpan * (fxBottom - FX_TOP);\n const fxLine = columns.map((column, index): HeatmapLinePoint => ({\n sampleIndex: column.sampleIndex,\n sampleId: column.sampleId,\n value: valueRows.fxLine[index],\n x: column.centerX,\n y: toY(valueRows.fxLine[index]),\n }));\n const axisValues = [fxMax, 0, fxMin].filter(\n (value, index, values) => values.indexOf(value) === index,\n );\n\n return {\n rows,\n columns,\n fxLine,\n fxAxisMarks: axisValues.map((value) => ({\n value,\n y: toY(value),\n label: formatShapValue(value),\n })),\n fxDomain: [fxMin, fxMax],\n separatorY: marginTop - SEPARATOR_GAP,\n gridLeft: marginLeft,\n gridRight,\n gridTop: marginTop,\n plotBottom,\n spines: {\n left: { x: marginLeft, y1: marginTop, y2: plotBottom },\n right: { x: gridRight, y1: marginTop, y2: plotBottom },\n },\n yTicks: rows.map((row) => ({\n y: row.centerY,\n x1: marginLeft - Y_TICK_LENGTH,\n x2: marginLeft,\n })),\n ...heatmapXAxis(valueRows.columns.length, marginLeft, cellWidth, plotWidth, plotBottom),\n sampleLabelColumn: valueRows.sampleLabelColumn,\n plotWidth,\n cellWidth,\n height: plotBottom + AXIS_HEIGHT,\n };\n}\n"],"mappings":"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ACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAAA,EACA,gBAAkB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IAC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","names":["MINUS","BAR_THICKNESS_RATIO","AXIS_HEIGHT","TICK_LABEL_PT","BAR_THICKNESS_RATIO","AXIS_HEIGHT","AXIS_HEIGHT","TICK_LABEL_PT","TITLE_PT","X_MARGIN","AXIS_HEIGHT","TICK_LABEL_PT","percentile"]}
@@ -0,0 +1,111 @@
1
+ /** The wire payload. Core fields are named exactly as shap.Explanation names them. */
2
+ type Explanation = {
3
+ contract_version: number;
4
+ values: number[][] | number[][][];
5
+ base_values: number | number[] | number[][];
6
+ data: number[][];
7
+ feature_names: string[];
8
+ sample_ids?: string[];
9
+ /** n — what a person reads for each Sample; never a join key. */
10
+ sample_labels?: string[];
11
+ /** Header of the uploaded column the labels came from, when it had one. */
12
+ sample_label_column?: string;
13
+ output_names?: string[];
14
+ model_name?: string;
15
+ model_version?: string;
16
+ };
17
+ /** A validated Explanation with the class axis resolved away. */
18
+ type ParsedExplanation = {
19
+ /** n x p, class already selected */
20
+ values: number[][];
21
+ /** n x p */
22
+ data: number[][];
23
+ /** length n — always per Sample, even when the wire form was a scalar */
24
+ baseValues: number[];
25
+ /** length p */
26
+ featureNames: string[];
27
+ /** length n, undefined when the payload omitted it */
28
+ sampleIds?: string[];
29
+ /** length n — display names. sampleIds stays the key for joins and click-through. */
30
+ sampleLabels?: string[];
31
+ sampleLabelColumn?: string;
32
+ outputName?: string;
33
+ nSamples: number;
34
+ nFeatures: number;
35
+ };
36
+ /** One row of a chart: either a real Feature or the collapsed Other features row. */
37
+ type DisplayRow = {
38
+ label: string;
39
+ /** index into ParsedExplanation.featureNames, or null for the Other features row */
40
+ featureIndex: number | null;
41
+ /** the value this row draws — for the bar chart, mean(|phi|) */
42
+ value: number;
43
+ isOtherRow: boolean;
44
+ };
45
+ type DisplayRows = {
46
+ rows: DisplayRow[];
47
+ /** how many Features were folded into the Other features row; 0 when none */
48
+ collapsedCount: number;
49
+ };
50
+ declare class UnsupportedContractVersionError extends Error {
51
+ readonly received: unknown;
52
+ constructor(received: unknown);
53
+ }
54
+ declare class InvalidExplanationError extends Error {
55
+ constructor(message: string);
56
+ }
57
+
58
+ /**
59
+ * The order the displayed Feature rows are drawn in.
60
+ *
61
+ * `importance` is SHAP's own order and the default. The other two only
62
+ * *reorder* the rows importance already chose — they never change which
63
+ * Features are shown. Sorting every Feature by name and taking the first N
64
+ * would show whatever happens to begin with A, not what the model relies on.
65
+ */
66
+ type RowSort = "importance" | "name" | "featureValue";
67
+ /**
68
+ * Reorder a collapsed set of rows.
69
+ *
70
+ * * `name` — alphabetical, ignoring case.
71
+ * * `featureValue` — mean feature value across Samples, highest first. For
72
+ * this platform that is mean relative abundance, so the most abundant taxa
73
+ * sit on top.
74
+ *
75
+ * Ties keep importance order, so a sort is stable and repeatable. The Other
76
+ * row is not a Feature and always stays last.
77
+ */
78
+ declare function sortDisplayRows(display: DisplayRows, sort: RowSort, data: number[][]): DisplayRows;
79
+
80
+ /** How the value labels are written. Display only. */
81
+ type ValuePrecision = 2 | 3 | 4 | "percent";
82
+ /**
83
+ * Spec 3.5 V1. SHAP's "%0.03f" renders most relative-abundance-scale values as "0" or "-0";
84
+ * this keeps them readable and always signs the value so a bar's direction is unambiguous.
85
+ *
86
+ * `decimals` fixes how the value is written for display. It does not change any
87
+ * value that is computed from — or compared against — the payload; it only
88
+ * changes the glyphs. Values too small to survive at that precision still fall
89
+ * back to an exponent rather than collapsing to a signed zero, which is the
90
+ * whole point of V1: "+0.00" hides both the magnitude and the direction.
91
+ *
92
+ * "percent" moves the decimal point two places and adds a sign. It reaches a
93
+ * range the fixed-decimal settings cannot: a contribution of 0.0003 is an
94
+ * exponent at two decimals but an ordinary 0.03%, which is most of this data.
95
+ */
96
+ declare function formatShapValue(v: number, decimals?: ValuePrecision): string;
97
+ /**
98
+ * A value on the model's output scale: `E[f(X)]` and `f(x)`.
99
+ *
100
+ * Unsigned, unlike a contribution. SHAP draws the same distinction —
101
+ * `_waterfall.py:327,339` format these with `"%0.03f"` and only the bar
102
+ * contributions with `"%+0.02f"` — and it matters: a leading "+" on a model
103
+ * output reads as "went up by", when the number is where the prediction landed,
104
+ * not how far it moved. A minus is still kept, because nothing guarantees a
105
+ * model output is a probability.
106
+ */
107
+ declare function formatLevel(v: number, decimals?: ValuePrecision): string;
108
+ /** Spec 3.5 V3. The italic styling is applied by the renderer, not here. */
109
+ declare function formatFeatureLabel(name: string): string;
110
+
111
+ export { type DisplayRows as D, type Explanation as E, InvalidExplanationError as I, type ParsedExplanation as P, type RowSort as R, UnsupportedContractVersionError as U, type ValuePrecision as V, type DisplayRow as a, formatLevel as b, formatShapValue as c, formatFeatureLabel as f, sortDisplayRows as s };
@@ -0,0 +1,111 @@
1
+ /** The wire payload. Core fields are named exactly as shap.Explanation names them. */
2
+ type Explanation = {
3
+ contract_version: number;
4
+ values: number[][] | number[][][];
5
+ base_values: number | number[] | number[][];
6
+ data: number[][];
7
+ feature_names: string[];
8
+ sample_ids?: string[];
9
+ /** n — what a person reads for each Sample; never a join key. */
10
+ sample_labels?: string[];
11
+ /** Header of the uploaded column the labels came from, when it had one. */
12
+ sample_label_column?: string;
13
+ output_names?: string[];
14
+ model_name?: string;
15
+ model_version?: string;
16
+ };
17
+ /** A validated Explanation with the class axis resolved away. */
18
+ type ParsedExplanation = {
19
+ /** n x p, class already selected */
20
+ values: number[][];
21
+ /** n x p */
22
+ data: number[][];
23
+ /** length n — always per Sample, even when the wire form was a scalar */
24
+ baseValues: number[];
25
+ /** length p */
26
+ featureNames: string[];
27
+ /** length n, undefined when the payload omitted it */
28
+ sampleIds?: string[];
29
+ /** length n — display names. sampleIds stays the key for joins and click-through. */
30
+ sampleLabels?: string[];
31
+ sampleLabelColumn?: string;
32
+ outputName?: string;
33
+ nSamples: number;
34
+ nFeatures: number;
35
+ };
36
+ /** One row of a chart: either a real Feature or the collapsed Other features row. */
37
+ type DisplayRow = {
38
+ label: string;
39
+ /** index into ParsedExplanation.featureNames, or null for the Other features row */
40
+ featureIndex: number | null;
41
+ /** the value this row draws — for the bar chart, mean(|phi|) */
42
+ value: number;
43
+ isOtherRow: boolean;
44
+ };
45
+ type DisplayRows = {
46
+ rows: DisplayRow[];
47
+ /** how many Features were folded into the Other features row; 0 when none */
48
+ collapsedCount: number;
49
+ };
50
+ declare class UnsupportedContractVersionError extends Error {
51
+ readonly received: unknown;
52
+ constructor(received: unknown);
53
+ }
54
+ declare class InvalidExplanationError extends Error {
55
+ constructor(message: string);
56
+ }
57
+
58
+ /**
59
+ * The order the displayed Feature rows are drawn in.
60
+ *
61
+ * `importance` is SHAP's own order and the default. The other two only
62
+ * *reorder* the rows importance already chose — they never change which
63
+ * Features are shown. Sorting every Feature by name and taking the first N
64
+ * would show whatever happens to begin with A, not what the model relies on.
65
+ */
66
+ type RowSort = "importance" | "name" | "featureValue";
67
+ /**
68
+ * Reorder a collapsed set of rows.
69
+ *
70
+ * * `name` — alphabetical, ignoring case.
71
+ * * `featureValue` — mean feature value across Samples, highest first. For
72
+ * this platform that is mean relative abundance, so the most abundant taxa
73
+ * sit on top.
74
+ *
75
+ * Ties keep importance order, so a sort is stable and repeatable. The Other
76
+ * row is not a Feature and always stays last.
77
+ */
78
+ declare function sortDisplayRows(display: DisplayRows, sort: RowSort, data: number[][]): DisplayRows;
79
+
80
+ /** How the value labels are written. Display only. */
81
+ type ValuePrecision = 2 | 3 | 4 | "percent";
82
+ /**
83
+ * Spec 3.5 V1. SHAP's "%0.03f" renders most relative-abundance-scale values as "0" or "-0";
84
+ * this keeps them readable and always signs the value so a bar's direction is unambiguous.
85
+ *
86
+ * `decimals` fixes how the value is written for display. It does not change any
87
+ * value that is computed from — or compared against — the payload; it only
88
+ * changes the glyphs. Values too small to survive at that precision still fall
89
+ * back to an exponent rather than collapsing to a signed zero, which is the
90
+ * whole point of V1: "+0.00" hides both the magnitude and the direction.
91
+ *
92
+ * "percent" moves the decimal point two places and adds a sign. It reaches a
93
+ * range the fixed-decimal settings cannot: a contribution of 0.0003 is an
94
+ * exponent at two decimals but an ordinary 0.03%, which is most of this data.
95
+ */
96
+ declare function formatShapValue(v: number, decimals?: ValuePrecision): string;
97
+ /**
98
+ * A value on the model's output scale: `E[f(X)]` and `f(x)`.
99
+ *
100
+ * Unsigned, unlike a contribution. SHAP draws the same distinction —
101
+ * `_waterfall.py:327,339` format these with `"%0.03f"` and only the bar
102
+ * contributions with `"%+0.02f"` — and it matters: a leading "+" on a model
103
+ * output reads as "went up by", when the number is where the prediction landed,
104
+ * not how far it moved. A minus is still kept, because nothing guarantees a
105
+ * model output is a probability.
106
+ */
107
+ declare function formatLevel(v: number, decimals?: ValuePrecision): string;
108
+ /** Spec 3.5 V3. The italic styling is applied by the renderer, not here. */
109
+ declare function formatFeatureLabel(name: string): string;
110
+
111
+ export { type DisplayRows as D, type Explanation as E, InvalidExplanationError as I, type ParsedExplanation as P, type RowSort as R, UnsupportedContractVersionError as U, type ValuePrecision as V, type DisplayRow as a, formatLevel as b, formatShapValue as c, formatFeatureLabel as f, sortDisplayRows as s };