Skill Tracer
Skills › Scientific Agent Skills › shap › migration.md

migration.md

424 lines · skills/shap/references/migration.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# SHAP Migration and Compatibility
2
3This guide covers migration to SHAP 0.52.0 and the modern `shap.Explanation` API.
4

SHAP Migration and Compatibility

This guide covers migration to SHAP 0.52.0 and the modern shap.Explanation API.

Raw lines 1 to 4
5## Version Baseline
6
7| SHAP release | Python requirement | Important compatibility note |
8|---|---|---|
9| 0.52.0 | `>=3.12` | Current release; NumPy `>=2`; native bindings moved to nanobind/scikit-build-core |
10| 0.51.0 | `>=3.11` | Latest release suitable for Python 3.11 |
11| 0.50.0 | `>=3.11` | First release after Python 3.9/3.10 support ended |
12| 0.49.1 | `>=3.9` | Last release line supporting Python 3.9 and 3.10; fixes the broken 0.49.0 publication |
14For a new environment:
16```bash
17uv venv --python 3.12
18source .venv/bin/activate
19uv pip install "shap[plots]==0.52.0"
20```
22For a project that cannot move off Python 3.11:
24```bash
25uv pip install "shap[plots]==0.51.0"
26```
28For Python 3.9 or 3.10, upgrade Python if possible. If temporarily constrained:
30```bash
31uv pip install "shap[plots]==0.49.1"
32```
34Do not claim 0.49.1 behavior is identical to 0.52. Read the release notes and test model-library compatibility.

Version Baseline

SHAP releasePython requirementImportant compatibility note
0.52.0>=3.12Current release; NumPy >=2; native bindings moved to nanobind/scikit-build-core
0.51.0>=3.11Latest release suitable for Python 3.11
0.50.0>=3.11First release after Python 3.9/3.10 support ended
0.49.1>=3.9Last release line supporting Python 3.9 and 3.10; fixes the broken 0.49.0 publication

For a new environment:

uv venv --python 3.12
source .venv/bin/activate
uv pip install "shap[plots]==0.52.0"

For a project that cannot move off Python 3.11:

uv pip install "shap[plots]==0.51.0"

For Python 3.9 or 3.10, upgrade Python if possible. If temporarily constrained:

uv pip install "shap[plots]==0.49.1"

Do not claim 0.49.1 behavior is identical to 0.52. Read the release notes and test model-library compatibility.

Raw lines 5 to 35
Runs code bin/activate :18
36## Major Changes Affecting Existing Code

Major Changes Affecting Existing Code

Raw lines 36 to 37
38### 0.45.0
40- Python 3.8 support ended.
41- Multi-output SHAP values changed from a list to a NumPy array with the output dimension last.
42- Deprecated `feature_dependence` parameters were removed from `TreeExplainer` and `LinearExplainer`.
43- Python 3.12 support was added.

0.45.0

  • Python 3.8 support ended.
  • Multi-output SHAP values changed from a list to a NumPy array with the output dimension last.
  • Deprecated feature_dependence parameters were removed from TreeExplainer and LinearExplainer.
  • Python 3.12 support was added.
Raw lines 38 to 44
45### 0.46.0
47- NumPy 2, Keras 3, and TensorFlow 2.16 support was added.
48- The deprecated `auto_size_plot` argument to `summary_plot` was removed.

0.46.0

  • NumPy 2, Keras 3, and TensorFlow 2.16 support was added.
  • The deprecated auto_size_plot argument to summary_plot was removed.
Raw lines 45 to 49
50### 0.47.0
52- `TreeExplainer(feature_perturbation="auto")` became the default behavior: interventional when background data are supplied, tree-path-dependent otherwise.
53- Passing `approximate` to the `TreeExplainer` constructor was deprecated; pass it when calling the explainer.
54- Plotting APIs continued moving toward `Explanation` inputs and returned axes.
55- Legacy bar plotting gained deprecation guidance.

0.47.0

  • TreeExplainer(feature_perturbation="auto") became the default behavior: interventional when background data are supplied, tree-path-dependent otherwise.
  • Passing approximate to the TreeExplainer constructor was deprecated; pass it when calling the explainer.
  • Plotting APIs continued moving toward Explanation inputs and returned axes.
  • Legacy bar plotting gained deprecation guidance.
Raw lines 50 to 56
57### 0.49.x
59- 0.49.1 repaired the 0.49.0 release publication.
60- 0.49.x was the final line supporting Python 3.9 and 3.10.
61- C++ categorical-split support expanded.

0.49.x

  • 0.49.1 repaired the 0.49.0 release publication.
  • 0.49.x was the final line supporting Python 3.9 and 3.10.
  • C++ categorical-split support expanded.
Raw lines 57 to 62
63### 0.50.0–0.51.0
65- Python 3.11 became the minimum.
66- Type coverage and tree/path-dependent behavior continued to improve.

0.50.0–0.51.0

  • Python 3.11 became the minimum.
  • Type coverage and tree/path-dependent behavior continued to improve.
Raw lines 63 to 67
68### 0.52.0
70- Python 3.12 became the minimum.
71- NumPy 2 became a core minimum requirement.
72- Native bindings moved from legacy extension/setup.py machinery to nanobind with scikit-build-core and CMake.
73- Tree/GPU fixes improved missing-value routing, vector-valued XGBoost base scores, and multiclass additivity.
74- `TreeExplainer` gained a pandas nullable-dtype fix.
75- Plot and documentation examples continued moving to the modern API.

0.52.0

  • Python 3.12 became the minimum.
  • NumPy 2 became a core minimum requirement.
  • Native bindings moved from legacy extension/setup.py machinery to nanobind with scikit-build-core and CMake.
  • Tree/GPU fixes improved missing-value routing, vector-valued XGBoost base scores, and multiclass additivity.
  • TreeExplainer gained a pandas nullable-dtype fix.
  • Plot and documentation examples continued moving to the modern API.
Raw lines 68 to 76
77### Verified 0.52.0 traps
79- Tabular backgrounds are capped at 100 by default; set the masker's `max_samples` explicitly for larger experiments.
80- `Explainer(algorithm="deep")` fails through unsupported dispatch keywords; use `DeepExplainer` directly.
81- Kernel SHAP defaults to `l1_reg="num_features(10)"`; declare regularization and sampling budgets.
82- Tree interactions require the raw, tree-path-dependent path; do not reuse a probability/interventional explainer.
83- Loss baselines may be callable and label-dependent, and classifier loss transforms require model-specific verification.
84- A one-output deep array can retain a final singleton output axis; `ranked_outputs` belongs on `.shap_values`, not the constructor.

Verified 0.52.0 traps

  • Tabular backgrounds are capped at 100 by default; set the masker's max_samples explicitly for larger experiments.
  • Explainer(algorithm="deep") fails through unsupported dispatch keywords; use DeepExplainer directly.
  • Kernel SHAP defaults to l1_reg="num_features(10)"; declare regularization and sampling budgets.
  • Tree interactions require the raw, tree-path-dependent path; do not reuse a probability/interventional explainer.
  • Loss baselines may be callable and label-dependent, and classifier loss transforms require model-specific verification.
  • A one-output deep array can retain a final singleton output axis; ranked_outputs belongs on .shap_values, not the constructor.
Raw lines 77 to 85
Starts an agent dispatch :80
86## Explanation API Migration

Explanation API Migration

Raw lines 86 to 87
88### Compute explanations
90Legacy:
92```python
93values = explainer.shap_values(X)
94```
96Modern:
98```python
99explanation = explainer(X)
100values = explanation.values
101base_values = explanation.base_values
102```
104Keep the `Explanation` object instead of immediately extracting `.values`; plots and slicing use its metadata.

Compute explanations

Legacy:

values = explainer.shap_values(X)

Modern:

explanation = explainer(X)
values = explanation.values
base_values = explanation.base_values

Keep the Explanation object instead of immediately extracting .values; plots and slicing use its metadata.

Raw lines 88 to 105
106### Base values
108Legacy:
110```python
111base_value = explainer.expected_value
112```
114Modern:
116```python
117base_values = explanation.base_values
118```
120`base_values` can be scalar, per-row, per-output, or per-row/per-output. Inspect shape.

Base values

Legacy:

base_value = explainer.expected_value

Modern:

base_values = explanation.base_values

base_values can be scalar, per-row, per-output, or per-row/per-output. Inspect shape.

Raw lines 106 to 121
122### Multi-output values
124Pre-0.45 code often assumed a list:
126```python
127positive_values = values[1]
128```
130Modern tabular code uses the final output axis:
132```python
133positive_exp = explanation[..., 1]
134positive_values = explanation.values[..., 1]
135```
137`explanation[1]` selects the second sample, not the second class.
139For scikit-learn `RandomForestClassifier`, a typical shape is:
141```text
142(samples, features, classes)
143```
145For XGBoost binary classification with default raw output, a typical shape is:
147```text
148(samples, features)
149```
151Do not hard-code a binary shape across model families.

Multi-output values

Pre-0.45 code often assumed a list:

positive_values = values[1]

Modern tabular code uses the final output axis:

positive_exp = explanation[..., 1]
positive_values = explanation.values[..., 1]

explanation[1] selects the second sample, not the second class.

For scikit-learn RandomForestClassifier, a typical shape is:

(samples, features, classes)

For XGBoost binary classification with default raw output, a typical shape is:

(samples, features)

Do not hard-code a binary shape across model families.

Raw lines 122 to 152
153## Plot Migration

Plot Migration

Raw lines 153 to 154
155### Summary plot to beeswarm
157Legacy:
159```python
160shap.summary_plot(values, X)
161```
163Modern:
165```python
166shap.plots.beeswarm(explanation)
167```

Summary plot to beeswarm

Legacy:

shap.summary_plot(values, X)

Modern:

shap.plots.beeswarm(explanation)
Raw lines 155 to 168
169### Summary bar to bar
171Legacy:
173```python
174shap.summary_plot(values, X, plot_type="bar")
175```
177Modern:
179```python
180shap.plots.bar(explanation)
181```

Summary bar to bar

Legacy:

shap.summary_plot(values, X, plot_type="bar")

Modern:

shap.plots.bar(explanation)
Raw lines 169 to 182
183### Dependence plot to scatter
185Legacy:
187```python
188shap.dependence_plot("age", values, X, interaction_index="bmi")
189```
191Modern:
193```python
194shap.plots.scatter(
195 explanation[:, "age"],
196 color=explanation[:, "bmi"],
198```

Dependence plot to scatter

Legacy:

shap.dependence_plot("age", values, X, interaction_index="bmi")

Modern:

shap.plots.scatter(
    explanation[:, "age"],
    color=explanation[:, "bmi"],
)
Raw lines 183 to 199
200### Force plot
202Legacy:
204```python
205shap.force_plot(
206 explainer.expected_value,
207 values[row_index],
208 X.iloc[row_index],
210```
212Modern:
214```python
215shap.plots.force(explanation[row_index])
216```

Force plot

Legacy:

shap.force_plot(
    explainer.expected_value,
    values[row_index],
    X.iloc[row_index],
)

Modern:

shap.plots.force(explanation[row_index])
Raw lines 200 to 217
218### Local waterfall
220Legacy code often manually built an `Explanation` from arrays. Modern:
222```python
223shap.plots.waterfall(explanation[row_index])
224```

Local waterfall

Legacy code often manually built an Explanation from arrays. Modern:

shap.plots.waterfall(explanation[row_index])
Raw lines 218 to 225
226### Image plot
228Legacy:
230```python
231shap.image_plot(values, images)
232```
234Modern:
236```python
237shap.plots.image(explanation)
238```

Image plot

Legacy:

shap.image_plot(values, images)

Modern:

shap.plots.image(explanation)
Raw lines 226 to 239
240### Decision plot
242`shap.plots.decision` remains largely array-oriented:
244```python
245shap.plots.decision(
246 base_value,
247 values,
248 features=X_display,
249 feature_names=feature_names,
251```
253Select one output and compatible base value before calling it.

Decision plot

shap.plots.decision remains largely array-oriented:

shap.plots.decision(
    base_value,
    values,
    features=X_display,
    feature_names=feature_names,
)

Select one output and compatible base value before calling it.

Raw lines 240 to 254
255## TreeExplainer Migration

TreeExplainer Migration

Raw lines 255 to 256
257### `feature_dependence`
259Removed:
261```python
262shap.TreeExplainer(model, feature_dependence="independent")
263```
265Current:
267```python
268shap.TreeExplainer(
269 model,
270 data=background,
271 feature_perturbation="interventional",
273```
275Or:
277```python
278shap.TreeExplainer(
279 model,
280 feature_perturbation="tree_path_dependent",
282```
284These are not mechanical synonyms for every old setting. Reconfirm the intended game and baseline.

feature_dependence

Removed:

shap.TreeExplainer(model, feature_dependence="independent")

Current:

shap.TreeExplainer(
    model,
    data=background,
    feature_perturbation="interventional",
)

Or:

shap.TreeExplainer(
    model,
    feature_perturbation="tree_path_dependent",
)

These are not mechanical synonyms for every old setting. Reconfirm the intended game and baseline.

Raw lines 257 to 285
286### Default feature perturbation
288Old code could rely on an interventional default. Current `"auto"` behavior depends on whether data are passed.
290Make reproducibility explicit:
292```python
293explainer = shap.TreeExplainer(
294 model,
295 data=background,
296 feature_perturbation="interventional",
297 model_output="raw",
299```

Default feature perturbation

Old code could rely on an interventional default. Current "auto" behavior depends on whether data are passed.

Make reproducibility explicit:

explainer = shap.TreeExplainer(
    model,
    data=background,
    feature_perturbation="interventional",
    model_output="raw",
)
Raw lines 286 to 300
301### Approximation
303Deprecated constructor use:
305```python
306explainer = shap.TreeExplainer(model, approximate=True)
307values = explainer(X)
308```
310Current:
312```python
313explainer = shap.TreeExplainer(model)
314explanation = explainer(X, approximate=True)
315```
317Label approximate values and do not treat them as ordinary Tree SHAP.

Approximation

Deprecated constructor use:

explainer = shap.TreeExplainer(model, approximate=True)
values = explainer(X)

Current:

explainer = shap.TreeExplainer(model)
explanation = explainer(X, approximate=True)

Label approximate values and do not treat them as ordinary Tree SHAP.

Raw lines 301 to 318
319### Probability output
321Use:
323```python
324explainer = shap.TreeExplainer(
325 model,
326 data=background,
327 feature_perturbation="interventional",
328 model_output="probability",
330explanation = explainer(X)
331```
333Probability and log-loss output modes are currently supported only under interventional feature perturbation.

Probability output

Use:

explainer = shap.TreeExplainer(
    model,
    data=background,
    feature_perturbation="interventional",
    model_output="probability",
)
explanation = explainer(X)

Probability and log-loss output modes are currently supported only under interventional feature perturbation.

Raw lines 319 to 334
335## DeepExplainer Output Migration
337Pre-0.45 code:
339```python
340values_by_class = explainer.shap_values(X)
341class_values = values_by_class[class_index]
342```
344Modern one-input, multi-output result:
346```python
347values = explainer.shap_values(X)
348class_values = values[..., class_index]
349```
351Multiple model inputs still produce a list, one array per input. `ranked_outputs=k` returns `(values, indexes)`; preserve the indexes because selected outputs can differ per row.

DeepExplainer Output Migration

Pre-0.45 code:

values_by_class = explainer.shap_values(X)
class_values = values_by_class[class_index]

Modern one-input, multi-output result:

values = explainer.shap_values(X)
class_values = values[..., class_index]

Multiple model inputs still produce a list, one array per input. ranked_outputs=k returns (values, indexes); preserve the indexes because selected outputs can differ per row.

Raw lines 335 to 352
353## Link and Output Migration
355Do not use a display link to pretend an explanation was computed in another space.
357```python
358shap.plots.force(raw_margin_exp[row], link="logit")
359```
361This labels raw margins as probabilities for display. It is not equivalent to:
363```python
364shap.TreeExplainer(
365 model,
366 data=background,
367 feature_perturbation="interventional",
368 model_output="probability",
369)(X)
370```
372The two additive decompositions can differ.

Link and Output Migration

Do not use a display link to pretend an explanation was computed in another space.

shap.plots.force(raw_margin_exp[row], link="logit")

This labels raw margins as probabilities for display. It is not equivalent to:

shap.TreeExplainer(
    model,
    data=background,
    feature_perturbation="interventional",
    model_output="probability",
)(X)

The two additive decompositions can differ.

Raw lines 353 to 373
374## End-to-End Migration Recipe
3761. Pin old and new environments.
3772. Capture model predictions and existing SHAP outputs on a small immutable fixture.
3783. Prefer `explainer(X)` where it exposes the required controls. Keep specialized `.shap_values` calls for Kernel `nsamples`, deep ranked outputs, and gradient sampling budgets.
3794. Print every result shape.
3805. Replace list-based output selection with final-axis slicing.
3816. Replace legacy plotting calls.
3827. Make tree perturbation and output explicit.
3838. Validate additive reconstruction against the exact selected output.
3849. Compare baselines and local values; do not require numerical identity if the game/default changed.
38510. Run model-library compatibility tests, especially for XGBoost, LightGBM, CatBoost, TensorFlow, Keras, and PyTorch.
38611. Update stored report metadata and migration notes.

End-to-End Migration Recipe

  1. Pin old and new environments.
  2. Capture model predictions and existing SHAP outputs on a small immutable fixture.
  3. Prefer explainer(X) where it exposes the required controls. Keep specialized .shap_values calls for Kernel nsamples, deep ranked outputs, and gradient sampling budgets.
  4. Print every result shape.
  5. Replace list-based output selection with final-axis slicing.
  6. Replace legacy plotting calls.
  7. Make tree perturbation and output explicit.
  8. Validate additive reconstruction against the exact selected output.
  9. Compare baselines and local values; do not require numerical identity if the game/default changed.
  10. Run model-library compatibility tests, especially for XGBoost, LightGBM, CatBoost, TensorFlow, Keras, and PyTorch.
  11. Update stored report metadata and migration notes.
Raw lines 374 to 387
388## Compatibility Diagnostic
390```python
391import importlib.metadata
392import platform
394packages = [
395 "shap",
396 "numpy",
397 "pandas",
398 "scikit-learn",
399 "xgboost",
400 "lightgbm",
401 "catboost",
402 "tensorflow",
403 "keras",
404 "torch",
407print("Python", platform.python_version())
408for package in packages:
409 try:
410 print(package, importlib.metadata.version(package))
411 except importlib.metadata.PackageNotFoundError:
412 pass
413```
415Attach this output to reproducible bug reports, without environment variables or credentials.

Compatibility Diagnostic

import importlib.metadata
import platform

packages = [
    "shap",
    "numpy",
    "pandas",
    "scikit-learn",
    "xgboost",
    "lightgbm",
    "catboost",
    "tensorflow",
    "keras",
    "torch",
]

print("Python", platform.python_version())
for package in packages:
    try:
        print(package, importlib.metadata.version(package))
    except importlib.metadata.PackageNotFoundError:
        pass

Attach this output to reproducible bug reports, without environment variables or credentials.

Raw lines 388 to 416
417## Sources
419- SHAP release notes: https://shap.readthedocs.io/en/latest/release_notes.html
420- SHAP 0.52.0 release: https://github.com/shap/shap/releases/tag/v0.52.0
421- PyPI metadata: https://pypi.org/project/shap/0.52.0/
422- Explanation migration guide: https://shap.readthedocs.io/en/latest/example_notebooks/api_examples/migrating-to-new-api.html
423- TreeExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.TreeExplainer.html
424- Plot API: https://shap.readthedocs.io/en/latest/api.html#plots

Sources

  • SHAP release notes: https://shap.readthedocs.io/en/latest/release_notes.html
  • SHAP 0.52.0 release: https://github.com/shap/shap/releases/tag/v0.52.0
  • PyPI metadata: https://pypi.org/project/shap/0.52.0/
  • Explanation migration guide: https://shap.readthedocs.io/en/latest/example_notebooks/api_examples/migrating-to-new-api.html
  • TreeExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.TreeExplainer.html
  • Plot API: https://shap.readthedocs.io/en/latest/api.html#plots
Raw lines 417 to 424