This guide covers migration to SHAP 0.52.0 and the modern shap.Explanation API.
| SHAP release | Python requirement | Important compatibility note |
|---|---|---|
| 0.52.0 | >=3.12 |
Current release; NumPy >=2; native bindings moved to nanobind/scikit-build-core |
| 0.51.0 | >=3.11 |
Latest release suitable for Python 3.11 |
| 0.50.0 | >=3.11 |
First release after Python 3.9/3.10 support ended |
| 0.49.1 | >=3.9 |
Last 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.
- Python 3.8 support ended.
- Multi-output SHAP values changed from a list to a NumPy array with the output dimension last.
- Deprecated
feature_dependenceparameters were removed fromTreeExplainerandLinearExplainer. - Python 3.12 support was added.
- NumPy 2, Keras 3, and TensorFlow 2.16 support was added.
- The deprecated
auto_size_plotargument tosummary_plotwas removed.
TreeExplainer(feature_perturbation="auto")became the default behavior: interventional when background data are supplied, tree-path-dependent otherwise.- Passing
approximateto theTreeExplainerconstructor was deprecated; pass it when calling the explainer. - Plotting APIs continued moving toward
Explanationinputs and returned axes. - Legacy bar plotting gained deprecation guidance.
- 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.
- Python 3.11 became the minimum.
- Type coverage and tree/path-dependent behavior continued to improve.
- Python 3.12 became the minimum.
- NumPy 2 became a core minimum requirement.
- Native bindings moved from Cython/setup.py to nanobind with scikit-build-core and CMake.
- Tree/GPU fixes improved missing-value routing, vector-valued XGBoost base scores, and multiclass additivity.
TreeExplainergained a pandas nullable-dtype fix.- Plot and documentation examples continued moving to the modern API.
Legacy:
values = explainer.shap_values(X)Modern:
explanation = explainer(X)
values = explanation.values
base_values = explanation.base_valuesKeep the Explanation object instead of immediately extracting .values; plots and slicing use its metadata.
Legacy:
base_value = explainer.expected_valueModern:
base_values = explanation.base_valuesbase_values can be scalar, per-row, per-output, or per-row/per-output. Inspect shape.
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.
Legacy:
shap.summary_plot(values, X)Modern:
shap.plots.beeswarm(explanation)Legacy:
shap.summary_plot(values, X, plot_type="bar")Modern:
shap.plots.bar(explanation)Legacy:
shap.dependence_plot("age", values, X, interaction_index="bmi")Modern:
shap.plots.scatter(
explanation[:, "age"],
color=explanation[:, "bmi"],
)Legacy:
shap.force_plot(
explainer.expected_value,
values[row_index],
X.iloc[row_index],
)Modern:
shap.plots.force(explanation[row_index])Legacy code often manually built an Explanation from arrays. Modern:
shap.plots.waterfall(explanation[row_index])Legacy:
shap.image_plot(values, images)Modern:
shap.plots.image(explanation)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.
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.
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",
)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.
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.
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.
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.
- Pin old and new environments.
- Capture model predictions and existing SHAP outputs on a small immutable fixture.
- Replace
.shap_values(X)withexplainer(X). - Print every result shape.
- Replace list-based output selection with final-axis slicing.
- Replace legacy plotting calls.
- Make tree perturbation and output explicit.
- Validate additive reconstruction against the exact selected output.
- Compare baselines and local values; do not require numerical identity if the game/default changed.
- Run model-library compatibility tests, especially for XGBoost, LightGBM, CatBoost, TensorFlow, Keras, and PyTorch.
- Update stored report metadata and migration notes.
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:
passAttach this output to reproducible bug reports, without environment variables or credentials.
- 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