SHAP (SHapley Additive exPlanations): Practical Guide for Data-Driven Decisions
By MCP Analytics Team | Published , updated
Your credit scoring model just denied a $50,000 small business loan application. The applicant calls asking why. Your model says "0.73 probability of default" but that doesn't explain anything. Which feature tipped the decision? Was it the debt-to-income ratio, the short credit history, or the recent bankruptcy filing? SHAP can show how a model score is split across its features; the values depend on the model and its data. You can use that to explain the decision, but only if you avoid the three interpretation mistakes that turn attribution into false causation.
Before we dive into SHAP mechanics, here are the ground rules: SHAP explains what your model learned, not what causes outcomes in the real world. That distinction matters when you're making decisions based on these explanations.
Worked Example (Simulated)
Question: How does a model split one score across its inputs?
Data: Simulated, seed 17. The code makes one row and a set of 200 background rows with random values for debt ratio, credit score, and time in business. These are not real loan records.
Method: The score is a set formula. The code finds the mean score over the background set, then checks each order of the fields. It shares each change in score across the orders. This gives exact Shapley values for this formula and this set.
import itertools
import numpy as np
SEED = 17
rng = np.random.default_rng(SEED)
features = ["debt ratio", "credit score", "time in business"]
background = rng.normal(size=(200, 3))
x = rng.normal(size=3)
def model(row):
debt, score, time = row
return 0.20 + 0.40 * debt - 0.30 * score + 0.10 * time + 0.25 * debt * time
base = float(np.mean([model(row) for row in background]))
prediction = float(model(x))
values = {}
for size in range(4):
for subset in itertools.combinations(range(3), size):
masked = background.copy()
if subset:
masked[:, subset] = x[list(subset)]
values[frozenset(subset)] = float(np.mean([model(row) for row in masked]))
shap = np.zeros(3)
for order in itertools.permutations(range(3)):
subset = frozenset()
for feature in order:
next_subset = subset | {feature}
shap[feature] += values[next_subset] - values[subset]
subset = next_subset
shap /= 6
print(f"Seed: {SEED}")
print(f"Background rows: {len(background)}")
print("Input values:")
for name, value in zip(features, x):
print(f" {name}: {value:.6f}")
print(f"Base score: {base:.6f}")
for name, value in zip(features, shap):
print(f"SHAP {name}: {value:+.6f}")
print(f"Sum of SHAP values: {shap.sum():.6f}")
print(f"Base plus SHAP sum: {base + shap.sum():.6f}")
print(f"Model score: {prediction:.6f}")
Output from running the script:
Seed: 17
Background rows: 200
Input values:
debt ratio: -0.621629
credit score: -1.394457
time in business: -0.288297
Base score: 0.198505
SHAP debt ratio: -0.204493
SHAP credit score: +0.396197
SHAP time in business: -0.004550
Sum of SHAP values: 0.187154
Base plus SHAP sum: 0.385659
Model score: 0.385659
Result: The score is 0.385659, above the base score of 0.198505. Credit score adds +0.396197 to this score. Debt ratio takes away 0.204493, and time in business moves it by a small -0.004550. The SHAP values sum to 0.187154, the gap from the base score to the model score.
Limit: This is a small score formula with made-up data. It says nothing about real loans.
Sources
- L. S. Shapley, "A Value for n-Person Games," in Contributions to the Theory of Games, Volume II (Annals of Mathematics Studies 28), 1953, pp. 307-318. doi 10.1515/9781400881970-018. Cited for who introduced Shapley values and when.
- S. M. Lundberg and S.-I. Lee, "A Unified Approach to Interpreting Model Predictions," NeurIPS 2017. NeurIPS 2017 page. Cited for who introduced SHAP and when.
- S. M. Lundberg et al., "From local explanations to global understanding with explainable AI for trees," Nature Machine Intelligence 2(1):56-67, 2020. doi 10.1038/s42256-019-0138-9. Cited for who published TreeExplainer and when.
Why Your Black-Box Model Needs SHAP (And When It Doesn't)
Here's the situation: you built a gradient boosting model that predicts customer churn. Your stakeholders ask "which factors drive churn?" Feature importance rankings give you a global answer, but when the VP asks "why did we lose this enterprise account?", you need attribution for that specific prediction.
To see which of your own columns a model actually uses to explain an outcome, try the free feature importance tool.
SHAP (SHapley Additive exPlanations) solves this by decomposing any model's prediction into contributions from each feature. SHAP uses Shapley values from cooperative game theory, which share a payout among the players in a coalition, introduced by Shapley in 1953 (see Sources).
The business translation: if your model is a team making a prediction, SHAP calculates how much each feature "contributed" to the final score. But here's what SHAP is NOT:
- Not causal inference: High SHAP values mean the model used that feature heavily, not that changing the feature would change the outcome
- Not a model validator: SHAP faithfully explains garbage models just as well as good ones
- Not a substitute for proper experiments: If you want to know if reducing email frequency reduces churn, run an A/B test: don't rely on SHAP values from observational data
The Three Interpretation Mistakes That Break SHAP Analysis
- Mistaking attribution for causation: "SHAP says price has high impact, so lowering price will increase conversions" assumes the model captured causal relationships, not just correlations
- Ignoring feature correlations: When features correlate (income and credit score), SHAP may distribute their joint effect unpredictably across both features
- Over-interpreting individual predictions: One customer's SHAP values don't represent patterns. You need aggregate analysis across samples
Use SHAP when you need to:
- Explain individual predictions to stakeholders or customers (regulatory compliance, customer service)
- Debug model behavior by identifying which features drive unexpected predictions
- Compare feature contributions across segments (approved vs denied loans, churned vs retained customers)
- Generate hypotheses for controlled experiments (high SHAP features become test candidates)
Skip SHAP when:
- You're working with inherently interpretable models (linear regression, decision trees with depth ≤ 3)
- You need causal estimates and haven't controlled for confounders
- Computational cost exceeds the value of explanation (real-time systems with millisecond latency requirements)
- Your features are so correlated that attribution becomes arbitrary
The Mathematical Foundation (Without the PhD)
Shapley values answer this question: "What's the average marginal contribution of this feature across all possible orderings of features?" That sounds abstract, so let's make it concrete.
Imagine you're predicting loan default with three features: credit score, income, and debt-to-income ratio. Your model outputs a probability. To calculate the SHAP value for credit score, you need to know: "How much does adding credit score change the prediction, averaged over all possible combinations of the other features?"
The process:
- Start with the base prediction (what the model outputs when it knows nothing about this applicant, typically the average prediction across all training data)
- Consider every possible subset of features excluding credit score: {}, {income}, {debt-to-income}, {income, debt-to-income}
- For each subset, calculate the prediction with and without adding credit score
- For each order of the features, record how much the feature changes the score when it joins; average that change across all orders
- The average is the SHAP value for credit score
For a model with n features, exact Shapley values consider all 2n feature subsets. That count grows quickly, which is why many SHAP implementations use approximations.
SHAP's Three Guarantees
Local accuracy: The sum of SHAP values equals the difference between the model's prediction for this instance and the base prediction (typically the average prediction).
Consistency: If a model changes so that a feature's marginal contribution increases (or stays the same) across all subsets, that feature's SHAP value cannot decrease.
Missingness: Features not used by the model have zero SHAP values.
These properties describe how the method splits a score. They do not make an interpretation correct.
The key algorithmic variants:
TreeExplainer: For tree-based models (Random Forest, XGBoost, LightGBM), TreeExplainer is the SHAP tool for tree models.
KernelExplainer: Model-agnostic approximation using weighted linear regression. Works with any model but requires sampling. You specify a background dataset (a few hundred representative samples is a common starting point), and KernelExplainer approximates SHAP values by evaluating the model on feature perturbations.
DeepExplainer: Optimized for deep learning models, using a connection between SHAP and DeepLIFT. Faster than KernelExplainer for neural networks but requires TensorFlow or PyTorch integration.
If your model is tree-based, start with TreeExplainer. Run time depends on the model and data, and it grows when you explain many predictions or use many features.
Setting Up Your SHAP Analysis: The Pre-Flight Checklist
Before calculating a single SHAP value, verify your experimental setup. Here's what I check:
1. Model Quality Comes First
SHAP explains your model faithfully, including its mistakes. If your model has poor calibration, overfits to noise, or learned spurious correlations, SHAP will dutifully explain those problems. Validate model performance first:
- Check AUC, precision, recall on a held-out test set
- Verify calibration (predicted probabilities match actual frequencies)
- Test for fairness across demographic groups if applicable
- Look for data leakage (features that wouldn't be available at prediction time)
Only explain models you trust to make good predictions.
2. Choose Your Background Dataset
For KernelExplainer (non-tree models), you need a background dataset representing the distribution of your features. This dataset defines "what does it mean to remove a feature?" Common approaches:
- Random sample from training data: a few hundred instances, stratified by outcome if imbalanced
- K-means clustering: Use cluster centroids to represent diverse data patterns with fewer samples
- Entire training set: Most accurate but computationally expensive
For TreeExplainer, the background dataset is less critical: the algorithm uses the tree structure to handle missing features.
3. Decide Your Explanation Scope
What are you trying to explain?
- Individual predictions: "Why did the model reject this application?" Calculate SHAP for that single instance
- Segment comparisons: "How do feature contributions differ between churned and retained customers?" Calculate SHAP for representative samples from each segment
- Global feature importance: "Which features matter most overall?" Calculate mean absolute SHAP values across your validation set
Computational cost scales with the number of instances you explain. For global patterns, use enough samples to represent your population.
4. Handle Correlated Features
When features correlate strongly (income and credit score, page views and session duration), SHAP may distribute their joint effect unpredictably. Check feature correlations before interpretation:
import pandas as pd
import seaborn as sns
# Check correlation matrix
corr_matrix = X_train.corr()
high_corr = (corr_matrix.abs() > 0.7) & (corr_matrix.abs() < 1.0)
# Identify problematic pairs
for i in range(len(high_corr)):
for j in range(i+1, len(high_corr)):
if high_corr.iloc[i, j]:
print(f"High correlation: {high_corr.index[i]} and {high_corr.columns[j]}")
If you find high correlations (|r| > 0.7), consider creating composite features or using one as a proxy for both. Document these correlations in your interpretation. They limit causal claims.
Calculating SHAP Values: The Step-by-Step Implementation
The example above uses simulated data to show how a model score can be split across features. The steps below show the same idea in code for a LightGBM churn model on your own data, with no results shown.
Step 1: Train Your Model
import lightgbm as lgb
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
# Load data (assuming preprocessed features)
X = df[['monthly_charges', 'tenure_months', 'support_tickets',
'feature_usage_score', 'days_since_last_login', 'contract_type']]
y = df['churned']
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Train LightGBM model
model = lgb.LGBMClassifier(
n_estimators=100,
max_depth=5,
learning_rate=0.1,
random_state=42
)
model.fit(X_train, y_train)
# Verify performance
from sklearn.metrics import roc_auc_score, classification_report
y_pred_proba = model.predict_proba(X_test)[:, 1]
print(f"AUC on held-out data: {roc_auc_score(y_test, y_pred_proba):.3f}")
Step 2: Initialize SHAP Explainer
import shap
# For tree-based models, use TreeExplainer
explainer = shap.TreeExplainer(model)
def churn_shap(explainer, X):
"""Log-odds SHAP values for the churn class, shape (n_samples, n_features).
The output shape depends on the SHAP version: older versions return a list
[class_0_values, class_1_values] for LightGBM binary models, newer versions
return a single array for the positive class."""
sv = explainer.shap_values(X)
if isinstance(sv, list):
sv = sv[1]
elif sv.ndim == 3:
sv = sv[:, :, 1]
return sv
# Calculate SHAP values for the test set (positive class: churn = 1)
shap_values_churn = churn_shap(explainer, X_test)
# Base value for the churn class, in log-odds
base_value = np.atleast_1d(explainer.expected_value)[-1]
# Verify local accuracy property
instance_idx = 0
predicted_proba = model.predict_proba(X_test.iloc[[instance_idx]])[:, 1][0]
# Convert to log-odds for SHAP (works in log-odds space for classifiers)
predicted_log_odds = np.log(predicted_proba / (1 - predicted_proba))
base_log_odds = base_value
shap_sum = shap_values_churn[instance_idx].sum()
print(f"Base log-odds: {base_log_odds:.4f}")
print(f"SHAP sum: {shap_sum:.4f}")
print(f"Predicted log-odds: {predicted_log_odds:.4f}")
print(f"Base + SHAP sum: {base_log_odds + shap_sum:.4f}")
# These should match (within rounding error)
Step 3: Explain Individual Predictions
# Select a high-risk customer
high_risk_idx = y_pred_proba.argsort()[-1] # Highest predicted churn probability
customer = X_test.iloc[high_risk_idx]
print(f"Customer features:")
print(customer)
print(f"\nPredicted churn probability: {y_pred_proba[high_risk_idx]:.2%}")
# Create waterfall plot for this prediction
shap.waterfall_plot(
shap.Explanation(
values=shap_values_churn[high_risk_idx],
base_values=base_value,
data=customer,
feature_names=X_test.columns.tolist()
)
)
The waterfall plot shows how each feature pushes the prediction up or down from the base value. For a TreeExplainer on a LightGBM classifier, the contributions are in log-odds, so they add up on the log-odds scale and are converted to a probability only at the end. Read the plot from the base value to the final prediction: each bar is one feature's contribution for that customer, and the bars add up to the gap between the base value and the prediction. Your own values will depend on your model and data.
Step 4: Global Feature Importance
# Calculate mean absolute SHAP values (global importance)
shap_importance = pd.DataFrame({
'feature': X_test.columns,
'importance': np.abs(shap_values_churn).mean(axis=0)
}).sort_values('importance', ascending=False)
print(shap_importance)
# Visualize with summary plot
shap.summary_plot(shap_values_churn, X_test, plot_type="bar")
# Detailed summary plot showing value distributions
shap.summary_plot(shap_values_churn, X_test)
The summary plot shows patterns across all predictions. Things to look for:
- Which features have the largest mean |SHAP|, the strongest drivers of the model's output
- Whether high feature values (red dots) push the prediction up and low values (blue dots) push it down, or the reverse
- Features whose effect changes sharply at some value, which can point to interaction effects
Step 5: Analyze Feature Interactions
# Calculate SHAP interaction values (computationally expensive)
shap_interaction = explainer.shap_interaction_values(X_test.iloc[:500])
# Interaction between days_since_last_login and feature_usage_score
shap.dependence_plot(
'days_since_last_login',
shap_values_churn[:500],
X_test.iloc[:500],
interaction_index='feature_usage_score'
)
A dependence plot can show whether the effect of one feature changes with the value of another. If it does, the model has learned an interaction. That is a pattern in the model, not a finding about the world.
Interpreting SHAP Values: What They Mean (And Don't Mean)
Here's where most analysts make the jump from correlation to causation. Let's establish boundaries.
What SHAP Values Tell You
Attribution in the model's logic: "For this prediction, the model used days_since_last_login to raise the log-odds of churn." This is descriptive. It explains what the model did.
Feature importance for this instance: SHAP values show which features were most influential for this specific prediction, which may differ from global feature importance.
Directional relationship as learned: If days_since_last_login has positive SHAP values across most instances, the model learned that higher values associate with higher churn probability.
What SHAP Values DON'T Tell You
Causal effects: "Reducing days_since_last_login by 10 days would reduce churn probability by 0.15" is not a valid inference from SHAP. The model learned correlations from observational data. Maybe engaged customers naturally log in more frequently. The relationship could be reversed.
Intervention recommendations: High SHAP values identify correlates, not levers. Before recommending interventions based on SHAP analysis, run controlled experiments.
Ground truth: SHAP explains the model, not reality. If your model learned that "customers who submit support tickets churn more," SHAP will highlight support_tickets, even if the real causal story is "unhappy customers both submit tickets and churn."
The Hypothesis Generation Framework
Here's how I use SHAP rigorously:
- Calculate SHAP values on a validation set representing your population
- Identify high-impact features using mean absolute SHAP values
- Examine SHAP distributions across segments (churned vs retained, high vs low value)
- Generate hypotheses: "Based on SHAP analysis, we hypothesize that reducing time-to-first-login reduces churn"
- Design experiments to test hypotheses causally (A/B tests, natural experiments, instrumental variables)
- Validate or refute based on experimental results, not SHAP values
SHAP accelerates hypothesis generation by surfacing what your model learned from millions of observations. But correlation, no matter how strong or well-explained, doesn't imply causation without experimental evidence.
Red Flags in SHAP Interpretation
- "SHAP proves that X causes Y": No. SHAP proves your model uses X to predict Y.
- "We should intervene on the highest SHAP features": Maybe. Test first.
- "This customer's SHAP values represent all customers like them": Not necessarily. Calculate SHAP for the segment.
- "SHAP values are stable across model retraining": Only if the underlying patterns are stable. Check.
Implementation Best Practices: Running SHAP Analysis That Ships
1. Calculate SHAP Incrementally for Large Datasets
For production systems processing thousands of predictions daily, batch your SHAP calculations:
import shap
import numpy as np
# Don't do this with 100K+ predictions
# shap_values = explainer.shap_values(X_all) # Memory explosion
# Do this instead
batch_size = 1000
shap_values_list = []
for i in range(0, len(X_all), batch_size):
batch = X_all.iloc[i:i+batch_size]
shap_values_list.append(churn_shap(explainer, batch)) # class-1 values, any SHAP output shape
shap_values_all = np.vstack(shap_values_list)
2. Cache Background Data for KernelExplainer
If you're explaining multiple models on the same dataset, reuse the background sample:
# Create background dataset once
background = shap.sample(X_train, 100) # K-means sampling
# Reuse for multiple models
explainer_model1 = shap.KernelExplainer(model1.predict_proba, background)
explainer_model2 = shap.KernelExplainer(model2.predict_proba, background)
3. Set Computational Budgets
For KernelExplainer with high-dimensional data, control runtime with the nsamples parameter:
# Fast approximation (lower accuracy)
explainer = shap.KernelExplainer(model.predict_proba, background)
shap_values = explainer.shap_values(X_test, nsamples=100)
# Higher accuracy (slower)
shap_values = explainer.shap_values(X_test, nsamples=1000)
# Adaptive sampling (default)
shap_values = explainer.shap_values(X_test) # Automatically determines nsamples
Benchmark on a small subset first to estimate runtime. Run time grows with the number of samples, features and background rows.
4. Validate SHAP Stability Across Model Updates
When you retrain models on new data, check if SHAP patterns shift:
# Calculate SHAP for same test set with old and new models
shap_old = churn_shap(explainer_old, X_test)
shap_new = churn_shap(explainer_new, X_test)
# Compare feature importance rankings
importance_old = np.abs(shap_old).mean(axis=0)
importance_new = np.abs(shap_new).mean(axis=0)
feature_comparison = pd.DataFrame({
'feature': X_test.columns,
'old_importance': importance_old,
'new_importance': importance_new,
'change': importance_new - importance_old,
'pct_change': (importance_new - importance_old) / importance_old * 100
}).sort_values('change', ascending=False)
print(feature_comparison)
# Flag large shifts for investigation
large_shifts = feature_comparison[abs(feature_comparison['pct_change']) > 50]
if not large_shifts.empty:
print("\nWARNING: Large SHAP importance shifts detected:")
print(large_shifts)
5. Store SHAP Values for Reproducibility
Save SHAP values alongside predictions for audit trails:
import pickle
# Save SHAP analysis
shap_data = {
'shap_values': shap_values_churn,
'base_value': base_value,
'feature_names': X_test.columns.tolist(),
'data': X_test,
'predictions': y_pred_proba,
'model_version': 'v2.3.1',
'calculation_date': '2026-03-01'
}
with open('shap_analysis_20260301.pkl', 'wb') as f:
pickle.dump(shap_data, f)
# Load for later analysis
with open('shap_analysis_20260301.pkl', 'rb') as f:
loaded_shap = pickle.load(f)
6. Create Explanation Templates for Stakeholders
Standardize how you communicate SHAP insights:
def generate_explanation(shap_values, features, feature_names, base_value,
predicted_proba, top_n=3):
"""Generate human-readable explanation from SHAP values."""
# Get top contributing features
feature_impact = list(zip(feature_names, shap_values, features))
sorted_impact = sorted(feature_impact, key=lambda x: abs(x[1]), reverse=True)
explanation = f"Predicted probability: {predicted_proba:.1%}\n\n"
explanation += f"Base rate (average): {1 / (1 + np.exp(-base_value)):.1%}\n\n"
explanation += f"Top {top_n} factors:\n"
for name, shap_val, feature_val in sorted_impact[:top_n]:
direction = "increased" if shap_val > 0 else "decreased"
# A SHAP value is additive in log-odds, so exp(shap_val) is the odds multiplier.
# (A probability change would depend on the base value and the other features.)
odds_multiplier = np.exp(shap_val)
explanation += (f"- {name} = {feature_val}: {direction} the odds of churn "
f"(log-odds {shap_val:+.2f}, odds x{odds_multiplier:.2f})\n")
return explanation
# Use for customer communication
customer_idx = 0
explanation = generate_explanation(
shap_values_churn[customer_idx],
X_test.iloc[customer_idx].values,
X_test.columns.tolist(),
base_value,
y_pred_proba[customer_idx]
)
print(explanation)
7. Monitor Feature Attribution Drift
Track SHAP distributions over time to detect data drift:
# Calculate weekly SHAP statistics
weekly_shap_stats = []
for week in prediction_data['week'].unique():
week_data = prediction_data[prediction_data['week'] == week]
week_shap = churn_shap(explainer, week_data[features])
stats = {
'week': week,
'n_predictions': len(week_data),
**{f'{feat}_mean_shap': np.abs(week_shap[:, i]).mean()
for i, feat in enumerate(features)}
}
weekly_shap_stats.append(stats)
shap_trends = pd.DataFrame(weekly_shap_stats)
# Alert if feature importance shifts > 50%
for feature in features:
col = f'{feature}_mean_shap'
baseline = shap_trends[col].iloc[:4].mean() # First month
recent = shap_trends[col].iloc[-4:].mean() # Last month
if abs(recent - baseline) / baseline > 0.5:
print(f"ALERT: {feature} importance changed {(recent-baseline)/baseline:.1%}")
Common Pitfalls and How to Avoid Them
Pitfall 1: Explaining Models You Don't Trust
The mistake: Running SHAP on a model with poor validation performance or known data quality issues.
Why it fails: SHAP will faithfully explain a bad model's bad predictions. You'll spend hours analyzing patterns that don't generalize.
The fix: Validate model quality first. Check AUC, calibration, and fairness metrics. Only explain models that pass your quality bar.
Pitfall 2: Ignoring Sample Size for Segment Comparisons
The mistake: "High-value customers have 2x higher SHAP values for feature X" based on 15 high-value customers.
Why it fails: Small sample SHAP distributions are noisy. Statistical significance requires adequate samples.
The fix: Calculate required sample size before comparing segments:
from scipy import stats
def required_sample_size(effect_size, alpha=0.05, power=0.8):
"""Calculate sample size for two-sample t-test."""
z_alpha = stats.norm.ppf(1 - alpha/2)
z_beta = stats.norm.ppf(power)
n = 2 * ((z_alpha + z_beta) / effect_size) ** 2
return int(np.ceil(n))
# To detect medium effect (Cohen's d = 0.5) with 80% power
n_needed = required_sample_size(0.5)
print(f"Need {n_needed} samples per group")
Pitfall 3: Over-Interpreting Feature Interactions
The mistake: "SHAP interaction values show that feature A and B interact, so we should combine them."
Why it fails: SHAP interactions measure how the model allocates credit, not necessarily real-world interactions. The model might split correlated effects arbitrarily.
The fix: Verify interactions with domain knowledge and controlled experiments before acting on them.
Pitfall 4: Using SHAP Values as Feature Selection
The mistake: "Features with low mean SHAP values don't matter, so let's remove them."
Why it fails: Low SHAP values might indicate the model hasn't learned to use a feature yet, or the feature only matters for rare cases.
The fix: Use proper feature selection methods (permutation importance, recursive feature elimination) that test predictive value, not just current model usage.
Pitfall 5: Mistaking Individual SHAP for Segment Patterns
The mistake: Showing one customer's waterfall plot as evidence of how the model treats all similar customers.
Why it fails: Individual instances can be outliers. Patterns emerge from distributions, not examples.
The fix: Use summary plots and aggregate statistics. Show individual examples as illustrations, not evidence.
Try SHAP Analysis Yourself
When to Choose SHAP vs Alternative Explainability Methods
SHAP isn't the only explainability tool. Here's when to use alternatives:
Use SHAP When:
- You need mathematically consistent explanations (local accuracy guarantee)
- You're explaining tree-based models (TreeExplainer is the SHAP tool for tree models)
- You need both local (per-prediction) and global (overall) explanations
- Regulatory compliance requires detailed prediction justifications
- You have the computing resources for the method you pick
Use LIME When:
- You need fast approximations for high-dimensional data (thousands of features)
- You're explaining image or text models where SHAP is prohibitively expensive
- You only need local explanations (individual predictions)
- You're working with models where SHAP implementations aren't available
Learn more about LIME for model explainability.
Use Permutation Importance When:
- You only need global feature importance, not individual explanations
- You want to measure actual predictive value (not just model usage)
- You're doing feature selection before model training
Use Partial Dependence Plots When:
- You want to visualize how features affect predictions on average
- You're checking for non-linear relationships
- You need to communicate effects to non-technical stakeholders
Use Inherently Interpretable Models When:
- Linear regression or logistic regression provides adequate performance
- Shallow decision trees (depth ≤ 3) meet your accuracy requirements
- Interpretability is more important than marginal accuracy gains
- You need to make causal claims and can control for confounders
The best approach often combines methods: use inherently interpretable models when possible, boost with ensemble methods when needed, and apply SHAP for detailed explanations.
What SHAP Reveals About Your Model (And Your Data)
Here are patterns to look for when you run SHAP on a model:
When SHAP Shows High Importance for Unexpected Features
If low-importance features in your training metrics show high SHAP values, investigate:
- Data leakage: The feature contains information not available at prediction time
- Proxy variables: The feature correlates with the true driver you didn't measure
- Interaction effects: The feature only matters in combination with others
When SHAP Values Vary Wildly Across Segments
If the same feature has opposite SHAP signs for different groups:
- Non-linear relationships: The feature has different effects at different values
- Model artifacts: The model learned segment-specific patterns from limited data
- Real heterogeneity: The relationship genuinely differs across segments (verify with domain experts)
When SHAP Distributions Are Unstable Across Model Versions
If SHAP patterns shift significantly when you retrain:
- Data drift: The underlying distribution changed
- Correlated features: The model reallocates credit arbitrarily
- Regularization effects: Different hyperparameters penalized features differently
SHAP analysis is a diagnostic tool for model behavior. Use it to surface questions, then investigate with proper statistical methods or experiments.
Your turn
Bring your own data and the question you actually need answered.
MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai
Frequently Asked Questions
What's the difference between SHAP values and feature importance?
Feature importance tells you which variables matter most globally across all predictions. SHAP values show how much each feature contributed to this specific prediction. Feature importance might say "income is the most important variable" while SHAP shows how income moved the score for one applicant. You need both: feature importance for overall model understanding, SHAP for individual prediction explanations.
Can I use SHAP values to make causal claims?
No. SHAP values measure feature attribution in your model's predictions, not causal effects in the real world. If your model learned spurious correlations, SHAP will faithfully explain those correlations. The statement "this feature has high SHAP value" means "the model used this feature heavily in its prediction," not "changing this feature would cause the outcome to change." For causal claims, you need proper experimental design or causal inference methods.
How do I calculate SHAP values for a model with 50+ features?
Use TreeExplainer for tree-based models (XGBoost, LightGBM, Random Forest) which is the SHAP tool for tree models. For deep learning or other model types, use KernelExplainer with sampling. Start with a small background sample for speed, then increase it if you need more precision. For very large datasets, compute SHAP on a representative sample of instances rather than your entire dataset. The calculation time grows with features × samples × background data, so budget accordingly.
What sample size do I need for reliable SHAP analysis?
For individual prediction explanations, a single instance is enough, SHAP tells you about that one prediction. For global feature importance using mean absolute SHAP values, you need enough samples to represent your population. Use enough instances from your validation set to represent your population. If you're comparing SHAP distributions across segments (e.g., approved vs denied loans), run a sample size calculation for the effect size you care about.
Should I use SHAP or LIME for model explainability?
Use SHAP when you need mathematically consistent explanations and are willing to pay the computational cost. SHAP guarantees that feature contributions sum to the model output and provides both local and global explanations. Use LIME when you need fast approximations for high-dimensional data or when explaining models where SHAP is computationally prohibitive. LIME is faster but can give inconsistent results across repeated runs. For production systems requiring regulatory compliance, prefer SHAP's consistency guarantees.
The Bottom Line: Explanation Enables Better Questions
SHAP shows how your model splits each prediction across its features. That transparency is powerful, but it's not causal inference.
Here's my framework for using SHAP rigorously:
- Build a model you trust: Validate performance, calibration, and fairness before explaining
- Calculate SHAP systematically: Use TreeExplainer for tree models, batch processing for scale, appropriate sample sizes for segments
- Interpret conservatively: SHAP shows correlation, not causation. Document feature correlations and model limitations
- Generate hypotheses: High SHAP features become candidates for intervention testing
- Test experimentally: Run A/B tests, natural experiments, or quasi-experimental designs to validate causal claims
- Monitor continuously: Track SHAP distributions over time to detect data drift and model degradation
The real value of SHAP isn't the explanations themselves. It's the questions they surface. When you see that "days_since_last_login" dominates churn predictions, that's your signal to run an engagement experiment. When SHAP shows a pattern that differs across segments, that's your hypothesis to test.
Correlation is interesting. Causation requires an experiment. SHAP helps you ask the right questions, then you design the experiments to answer them.
Before we draw conclusions from SHAP values, let's remember what we're actually measuring: how the model allocates credit, not how the world works. Use SHAP to understand your model, then use experiments to understand reality.
For an overall ranking of which inputs matter, including impurity and permutation methods, see feature importance.