Monte Carlo Shapley Effects
This guide explains how to estimate Shapley effects using the Monte Carlo approach, which correctly handles correlated inputs — a scenario where standard variance-based methods break down.
Overview
The MC Shapley method estimates Shapley effects via conditional Monte Carlo sampling. It supports:
- Two computation methods: exhaustive enumeration and random permutations
- Bootstrap confidence intervals for all estimates
- Three distribution families:
GaussianCopulaUniform,MultivariateNormal, andTruncatedMultivariateNormal - Flexible model functions: use the trained surrogate or any Python callable
Basic Usage with the Surrogate Model
After training an RS-HDMR surrogate, call get_mc_shapley() directly:
from shapleyx import rshdmr
# Train the RS-HDMR surrogate
model = rshdmr(data_file='data.csv', polys=[10, 5], method='ard_cv')
sobol, shapley, total = model.run_all()
# Compute MC Shapley effects (independent inputs, surrogate model)
mc_results = model.get_mc_shapley(N=5000, method='exhaustive', B=500)
print(mc_results)
# variable effect shapley_value total_variance lower upper
# 0 X1 0.435622 5.982737 13.7330 0.408571 0.466487
# 1 X2 0.424561 5.830838 13.7330 0.396925 0.453246
# 2 X3 0.139817 1.920069 13.7330 0.114170 0.166073
Correlated Inputs
Provide a correlation matrix via the corr parameter to model dependence:
import numpy as np
corr = np.array([
[1.0, 0.0, 0.8],
[0.0, 1.0, 0.0],
[0.8, 0.0, 1.0],
])
mc_corr = model.get_mc_shapley(corr=corr, N=5000, B=500)
This constructs a GaussianCopulaUniform distribution using the training data's min–max ranges and the supplied correlation matrix.
User-Defined Model Functions
You are not limited to the surrogate model. Pass any callable f(x) that takes a 1D numpy array and returns a scalar:
def my_model(x):
return np.sin(x[0]) + 7 * np.sin(x[1])**2 + 0.1 * x[2]**4 * np.sin(x[0])
mc_user = model.get_mc_shapley(f=my_model, N=5000, B=500)
Custom Distributions
For full control over the input distribution, provide a distribution object via the joint parameter.
Gaussian Copula with Uniform Marginals
from shapleyx.utilities.mc_shapley import GaussianCopulaUniform
joint = GaussianCopulaUniform(
lows=[0.0, -1.0, 0.5],
highs=[1.0, 1.0, 2.0],
corr=np.eye(3),
)
mc = model.get_mc_shapley(joint=joint, f=my_model, N=5000)
Multivariate Normal
from shapleyx.utilities.mc_shapley import MultivariateNormal
joint = MultivariateNormal(
mean=[0.0, 0.0, 0.0],
cov=[[1.0, 0.5, 0.0],
[0.5, 1.0, 0.0],
[0.0, 0.0, 1.0]],
)
mc = model.get_mc_shapley(joint=joint, f=my_model, N=5000)
Truncated Multivariate Normal
from shapleyx.utilities.mc_shapley import TruncatedMultivariateNormal
joint = TruncatedMultivariateNormal(
mean=[0.0, 0.0, 0.0],
cov=[[1.0, 0.5, 0.0],
[0.5, 1.0, 0.0],
[0.0, 0.0, 1.0]],
lower=[-1.0, -1.0, -1.0],
upper=[1.0, 1.0, 1.0],
)
mc = model.get_mc_shapley(joint=joint, f=my_model, N=5000)
Use -np.inf and np.inf for unbounded dimensions. The class uses
Gibbs sampling for both joint and conditional draws; tune
joint_burn_in (default 30) and cond_burn_in (default 5) to
balance speed versus convergence.
Choosing a Computation Method
| Method | When to Use |
|---|---|
'exhaustive' |
Small to moderate \(d\) (≤ 8). Enumerates all \(2^d-1\) subsets. Exact. |
'permutation' |
Larger \(d\). Uses random permutations with lazy caching. Approximate but scalable. |
# Exhaustive (default)
mc = model.get_mc_shapley(N=5000, method='exhaustive')
# Permutation
mc = model.get_mc_shapley(N=5000, method='permutation', n_perm=2000)
Coalition Truncation (k_max)
The exhaustive method evaluates all \(2^d - 1\) non-empty subsets, which becomes
prohibitively expensive for \(d > 8\). However, many models — especially those
built from RS-HDMR surrogates — have no interactions above a known order.
For example, an RS-HDMR model trained with polys=[10, 5] has only
first-order (main effects) and second-order (pairwise) interactions; third-order
and higher terms are absent by construction.
The k_max parameter limits coalition evaluation to subsets of size
\(\leq k_{\max}\) (plus the full set, always needed for total variance). This is
exact when the model truly has no interactions above order \(k_{\max}\),
dramatically reducing the number of subsets:
| \(d\) | All subsets (\(2^d-1\)) | \(k_{\max}=2\) | \(k_{\max}=3\) |
|---|---|---|---|
| 6 | 63 | 22 | 42 |
| 8 | 255 | 37 | 93 |
| 10 | 1,023 | 56 | 176 |
| 15 | 32,767 | 121 | 576 |
| 20 | 1,048,575 | 211 | 1,351 |
Auto-Detection
When using model.get_mc_shapley(), k_max defaults to False (a sentinel
meaning "not set"). In this state, it is auto-detected from the surrogate
model's polys parameter — len(polys) gives the highest interaction order.
No manual configuration is needed:
# polys=[10, 5] → k_max=2 auto-detected (exact for this surrogate)
mc = model.get_mc_shapley(N=5000, method='exhaustive')
To force full enumeration, pass k_max=None:
mc = model.get_mc_shapley(N=5000, method='exhaustive', k_max=None)
Explicit Control
Override the auto-detected value or use k_max with the low-level API:
# Explicit: limit to pairwise coalitions
mc = model.get_mc_shapley(N=5000, method='exhaustive', k_max=2)
# Full enumeration (all 2^d - 1 subsets)
mc = model.get_mc_shapley(N=5000, method='exhaustive', k_max=None)
# Low-level API
from shapleyx.utilities.mc_shapley import MCShapley
mc = MCShapley(f=my_model, joint=my_dist)
df = mc.compute(N=5000, method='exhaustive', k_max=2)
Note:
k_maxonly affects the exhaustive method. The permutation method evaluates subsets lazily through random permutations of all sizes and does not support coalition truncation.
Approximation Behaviour
When k_max < d-1, some coalitions are not evaluated. The estimator uses
a conservative fallback: missing \(v(u)\) values are approximated as the
total variance \(v(\text{full})\), meaning missing high-order interactions are
assumed to contribute proportionally. For models with sparse high-order
structure (the intended use case), this approximation is excellent.
Note on Sobol indices: When \(k_{\max} < d-1\), the complement sets
\(\{-i\}\) needed for total-order Sobol indices \(T_i\) are not evaluated,
so sobol_total is returned as NaN. First-order Sobol indices \(S_i\)
remain available (they only require singleton subsets, always evaluated
when \(k_{\max} \ge 1\)). Pass k_max=None to force full enumeration
and obtain \(T_i\).
Bootstrap Confidence Intervals
Set B > 0 to enable bootstrap CIs. The returned DataFrame gains 'lower' and 'upper' columns:
mc = model.get_mc_shapley(N=5000, B=500, alpha=0.05)
print(f"X1 effect: {mc.loc[0, 'effect']:.4f} "
f"[{mc.loc[0, 'lower']:.4f}, {mc.loc[0, 'upper']:.4f}]")
Sobol Indices from the Same Data
The Owen & Prieur covariance formulation \(v(u) = \text{Cov}[f(\mathbf{X}), f(\mathbf{X}_u)]\) equals the closed Sobol index \(\mathbb{V}[\mathbb{E}(f(\mathbf{X}) \mid \mathbf{X}_u)]\). This means first-order (\(S_i\)) and total-order (\(T_i\)) Sobol indices can be extracted from the same Monte Carlo data at zero additional cost:
When using method='exhaustive', the returned DataFrame includes:
| Column | Description |
|---|---|
sobol_first |
First-order Sobol index \(S_i\) |
sobol_total |
Total-order Sobol index \(T_i\) |
sobol_first_lower, sobol_first_upper |
Bootstrap CIs for \(S_i\) (if B > 0) |
sobol_total_lower, sobol_total_upper |
Bootstrap CIs for \(T_i\) (if B > 0) |
mc = model.get_mc_shapley(N=5000, method='exhaustive', B=500)
print(mc[['variable', 'effect', 'sobol_first', 'sobol_total']])
Sobol Availability with \(k_{\max}\)
When using coalition truncation, Sobol index availability depends on which subsets are evaluated:
| Index | Required subset | Available when |
|---|---|---|
| \(S_i\) | \(\{i\}\) (size 1) | \(k_{\max} \ge 1\) — always available |
| \(T_i\) | \(\{-i\}\) (size \(d-1\)) | \(k_{\max} \ge d-1\) or k_max=None |
When \(T_i\) cannot be computed (missing complement sets), the
sobol_total column contains NaN. Pass k_max=None to force
full enumeration and obtain both \(S_i\) and \(T_i\) regardless of
dimension.
For the permutation method, Sobol columns are returned as NaN — the lazy subset
evaluation may not include all singleton and \((d-1)\)-variable subsets needed.
Note on correlated inputs: Under correlation, the standard inequality \(S_i \leq T_i\) no longer holds. See the MC Shapley + Sobol tutorial for a detailed explanation.
Performance Tips
- Start with small N: Use
N=1000–2000for quick exploration, increase for final results. - Prefer the permutation method for \(d > 6\) to avoid exponential subset explosion.
- The
Bbootstrap iterations are independent of the \(2^d\) scaling — they re-use cached data. - Random state: Fix
random_statefor reproducible outputs.
See Also
- MC Shapley Reference — complete API documentation
- Theory: Shapley Effects with Correlated Inputs
- Distribution Classes — built-in and custom distributions
- Case Study Reports — detailed PDF reports for cantilever beam and wing weight examples
- Examples: MC Shapley Notebook