Back to Claude Scientific Skills

SHAP Plotting

skills/shap/references/plots.md

2.55.011.6 KB
Original Source

SHAP Plotting

This reference uses the SHAP 0.52 plotting API. Prefer shap.plots.* functions that consume shap.Explanation objects. Legacy functions such as summary_plot, dependence_plot, and top-level force_plot remain relevant to old code but lose metadata and should not anchor a new workflow.

Validate Before Plotting

For every plot:

python
print("values:", explanation.values.shape)
print("base_values:", np.shape(explanation.base_values))
print("data:", np.shape(explanation.data))
print("features:", explanation.feature_names)
print("outputs:", explanation.output_names)

For multi-output tabular data, select one output:

python
class_exp = explanation[..., class_index]
# or, when output names are populated:
class_exp = explanation[..., "class_name"]

Most tabular plots expect either:

  • one local row: (features,); or
  • many rows: (samples, features).

Passing (samples, features, outputs) without selecting an output is a common source of misleading or failed plots.

Plot Selection

QuestionPlotInput
Which features have the greatest average absolute attribution?barmulti-row Explanation
What are the direction, spread, and observed values of attributions?beeswarmmulti-row Explanation
How does one row move from baseline to output?waterfallone-row Explanation
How does attribution vary with a feature value?scatterone feature column
Are there sample-level explanation patterns?heatmapmulti-row Explanation
How do cohorts compare descriptively?barCohorts or dictionary
Which tokens or regions contribute?text, imagemodality-specific Explanation
Is an interactive additive layout useful?forceone or multiple rows
How do cumulative paths differ?decisionarrays plus base value

No plot proves causality or fairness. A plot is an aggregation of attributions under a model, output, background, and masker.

Global Bar Plot

python
ax = shap.plots.bar(
    explanation,
    max_display=20,
    show=False,
)
ax.set_title("Mean absolute SHAP value")
ax.figure.tight_layout()
ax.figure.savefig("shap-bar.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

A multi-row explanation is aggregated by mean absolute attribution. A single row creates a local bar plot.

Cohort bar plot

python
cohorts = explanation.cohorts(group_labels)
ax = shap.plots.bar(cohorts.abs.mean(0), max_display=15, show=False)
ax.figure.savefig("shap-cohorts.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

Alternatively:

python
ax = shap.plots.bar(
    {
        "Group A": explanation[group_labels == "A"],
        "Group B": explanation[group_labels == "B"],
    },
    show=False,
)

Use one shared explainer and background for direct cohort comparison. Include sample counts and uncertainty; different mean magnitudes can reflect feature distributions, performance, or model behavior.

Feature clustering

python
clustering = shap.utils.hclust(X_background, y_background)
ax = shap.plots.bar(
    explanation,
    clustering=clustering,
    clustering_cutoff=0.5,
    show=False,
)

hclust(X, y) uses outcome-redundancy information and can be expensive. Do not compute it using held-out labels if that would contaminate the analysis.

Beeswarm Plot

python
ax = shap.plots.beeswarm(
    explanation,
    max_display=20,
    alpha=0.7,
    s=12,
    group_remaining_features=True,
    show=False,
)
ax.figure.savefig("shap-beeswarm.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

Current 0.52 controls include:

  • order: default explanation.abs.mean(0);
  • clustering and cluster_threshold;
  • ax;
  • log_scale;
  • color_bar;
  • s for marker size;
  • plot_size;
  • group_remaining_features.

Interpretation:

  • horizontal position: signed attribution in the explained output's units;
  • color: observed feature value when numeric display data are available;
  • vertical density/jitter: observations, not uncertainty;
  • row order: an aggregation rule, not causal importance.

Useful alternatives:

python
# Highlight features with rare large effects.
shap.plots.beeswarm(
    explanation,
    order=explanation.abs.max(0),
)

# Absolute magnitude only; direction is intentionally removed.
shap.plots.beeswarm(
    explanation.abs,
    color="shap_red",
)

Do not interpret red as "risk" or blue as "protective" without first defining output and units.

Waterfall Plot

python
ax = shap.plots.waterfall(
    explanation[row_index],
    max_display=15,
    show=False,
)
ax.figure.savefig("shap-waterfall.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

Input must be one-dimensional. A waterfall shows how values move from the selected background's base value to the selected output for one row.

Report:

  • why the row was selected;
  • baseline population and value;
  • output name and units;
  • final model output;
  • whether omitted features were grouped.

Never describe the arrows as what would happen if a person changed a feature. They decompose a predictive output; they are not recourse or intervention estimates.

Scatter Plot

python
ax = shap.plots.scatter(
    explanation[:, "age"],
    color=explanation[:, "bmi"],
    alpha=0.5,
    x_jitter="auto",
    show=False,
)
ax.figure.savefig("shap-age-scatter.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

If color=explanation is passed, SHAP selects a likely interaction color automatically:

python
shap.plots.scatter(explanation[:, "age"], color=explanation)

Vertical spread at one x-value can arise from:

  • interactions;
  • correlated features;
  • heterogeneous subgroups;
  • approximation noise;
  • model discontinuities.

It does not by itself prove an interaction or causal effect.

Current scatter features include categorical support, automatic jitter, percentile axis limits, a histogram, one-feature ax support, and overlay curves:

python
shap.plots.scatter(
    explanation[:, "age"],
    xmin="percentile(1)",
    xmax="percentile(99)",
    hist=True,
)

Heatmap

python
subset = explanation[:200]
ax = shap.plots.heatmap(
    subset,
    max_display=20,
    instance_order=subset.sum(1),
    show=False,
)
ax.figure.savefig("shap-heatmap.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

Heatmaps are useful for sample-level patterns but can invite over-interpretation of visual clusters. Record:

  • row sampling and ordering;
  • any clustering method;
  • output and baseline;
  • whether colors share a fixed scale across panels.

Use a subset for readability, but choose it before viewing explanations or document the selection rule.

Violin Plot

python
shap.plots.violin(
    explanation.values,
    features=explanation.data,
    feature_names=explanation.feature_names,
    max_display=20,
)

The violin API retains array-oriented parameters. Prefer beeswarm for the richest modern Explanation workflow; use violin when density is more important than individual points.

Force Plot

The current API recommends passing an Explanation:

python
shap.plots.force(explanation[row_index])

This returns a JavaScript visualization by default. Initialize notebook JavaScript if required:

python
shap.plots.initjs()

For a static local plot:

python
shap.plots.force(
    explanation[row_index],
    matplotlib=True,
    show=False,
)
plt.gcf().savefig("shap-force.png", dpi=200, bbox_inches="tight")
plt.close(plt.gcf())

Use link="logit" only as a display transform when the additive values are in log-odds. It does not recompute probability-space SHAP values.

Stacked force plots can accept multiple rows but become hard to audit. Prefer heatmaps or cohort summaries for large samples.

Decision Plot

Decision plots retain an array-oriented API:

python
shap.plots.decision(
    base_value,
    values,
    features=X_display,
    feature_names=feature_names,
    highlight=highlight_rows,
    show=False,
)

Before plotting, select one output and ensure a compatible scalar base value. Use decision plots for cumulative path comparison, not as a default multiclass plot.

Text Plot

python
shap.plots.text(text_explanation)

For multi-output text:

python
class_text = text_explanation[..., "anger"]
shap.plots.text(class_text)

Token merging and tokenizer special tokens affect presentation. Preserve the tokenizer and masker configuration.

Image Plot

python
shap.plots.image(image_explanation)

Legacy examples may use shap.image_plot; prefer shap.plots.image for current code. Select only the outputs that were actually explained and label them explicitly.

Image overlays show attribution under a masking method; they are not object localization or segmentation ground truth.

Group Difference and Monitoring

shap.plots.group_difference plots differences in mean SHAP values between two groups. It is descriptive and should be paired with confidence intervals or resampling when decisions depend on the difference.

shap.plots.monitoring visualizes attribution behavior over an ordered axis. For production drift, store numeric summaries and statistical checks as the source of truth; use the plot as a diagnostic.

Saving Figures Reliably

Most modern matplotlib-backed plots return an Axes when show=False:

python
def save_axes(ax, path):
    figure = ax.figure
    figure.tight_layout()
    figure.savefig(path, dpi=200, bbox_inches="tight")
    plt.close(figure)

If a plot does not return an axis, capture the current figure immediately:

python
shap.plots.heatmap(explanation, show=False)
figure = plt.gcf()
figure.savefig("plot.png", dpi=200, bbox_inches="tight")
plt.close(figure)

Do not call another plotting function before capturing the figure.

Publication and Accessibility

  • Put output units in the x-axis label or caption.
  • Define the baseline population in the caption.
  • Include sample size and aggregation.
  • Use direct labels in addition to color.
  • Avoid red/green-only encodings.
  • Keep consistent axis limits for panels intended for comparison.
  • Export vector formats (.svg or .pdf) when journal workflows allow.
  • Do not expose identifiers or sensitive raw values in local plot labels.

Common Failures

"Waterfall requires a scalar explanation"

Select one row and one output:

python
shap.plots.waterfall(explanation[row_index, ..., output_index])

For tabular data, the clearer sequence is:

python
class_exp = explanation[..., output_index]
shap.plots.waterfall(class_exp[row_index])

Missing feature names

Use a DataFrame through preprocessing where possible, or pass feature_names when creating the explainer. Verify order before assigning names manually.

No colors in beeswarm

Color requires usable feature data in explanation.data or display_data. An explanation built from values alone cannot recover original feature values.

Figures overlap in loops

Use show=False, save the returned axis/figure, and close it every iteration.

Plot and prediction disagree

Check output selection and units. For example, raw XGBoost margins are not probabilities. Reconstruct the exact explained output before changing plot settings.

Sources