Calculate Tree SHAP Values by Hand
We are still explaining Maya from the ten-customer example. She is Returning, has a Premium plan, and has High usage. The tree predicts $90 for her, compared with the starting average of $52. Let’s share the extra $38 among those three details.
We will calculate the average prediction for every combination of details we might know about Maya. With three inputs, there are just eight combinations.
We use A for customer group, B for plan, and C for usage. Maya’s values are all 1 in the code. Keep the same tree and the same ten customers throughout.
We will write $v(S)$ for the average prediction when the details in $S$ are known. For example, $v(\{A\})$ means “we know Maya is Returning, but not her plan or usage.” The empty group $\varnothing$ means we know none of her details.
Calculate the eight group values
First, calculate the average for each customer group. On the New branch, Asha, Ben, and Cara receive prediction 10, while Dev receives 30. That average is $(3\times10+1\times30)/4=15$. On the Returning branch, Eli and Farah receive 50, and Gia, Hari, Isha, and Jay receive 90. That average is $(2\times50+4\times90)/6=76\frac23$.
With none of Maya’s details known, combine the two groups using their counts: four New customers and six Returning customers. This gives $(4/10)\times15+(6/10)\times76\frac23=52$.
Knowing Maya is Returning (A) sends us right, giving $76\frac23$.
Knowing only her Premium plan (B) leaves her customer group unknown. Inside the New branch, we now follow Premium directly to 30. The earlier average $(3/4)\times10+(1/4)\times30=15$ applied while the plan was unknown; knowing Premium replaces it with $0\times10+1\times30=30$.
At the top of the tree, the New/Returning question is still unanswered, so we use its original weights of 4/10 and 6/10. The Returning branch stays at $76\frac23$ because usage is still unknown. Thus $v(\{B\})=(4/10)\times30+(6/10)\times76\frac23=58$. The step-by-step plan example shows these two splits separately.
Knowing only her High usage (C) leaves the New branch at 15 and makes the right subtree return 90. This gives $v(\{C\})=0.4\times15+0.6\times90=60$. Notice that “only C known” still visits the left subtree. Knowing C does not tell us A.
| Revealed group | Calculation | Value |
|---|---|---|
| None | 0.4 × 15 + 0.6 × 76⅔ | 52 |
| A: Returning | Returning-customer average | 76⅔ |
| B: Premium | 0.4 × 30 + 0.6 × 76⅔ | 58 |
| C: High usage | 0.4 × 15 + 0.6 × 90 | 60 |
| A, B | A sends us right; C stays hidden | 76⅔ |
| A, C | A and C select the 90 leaf | 90 |
| B, C | 0.4 × 30 + 0.6 × 90 | 66 |
| A, B, C | Ordinary prediction | 90 |
The two-feature rows follow the same rule. In particular, B has no further effect after A is known to be 1. That does not mean B is irrelevant in every group. When A remains hidden, B raises the partial prediction by 6.
Reveal the features in all six orders
First try customer group → plan → usage, or A → B → C. For this order, the successive values are 52 → $76\frac23$ → $76\frac23$ → 90. Attribute each change to the feature just revealed: A gets $24\frac23$, B gets 0, and C gets $13\frac13$.
For B → C → A, the values are 52 → 58 → 66 → 90. B gets 6, C gets 8, and A gets 24. These contributions still sum to 38, but the allocation changed. Neither order gets to decide the final answer alone.
| Reveal order | Group (A) | Plan (B) | Usage (C) |
|---|---|---|---|
| A → B → C | 24⅔ | 0 | 13⅓ |
| A → C → B | 24⅔ | 0 | 13⅓ |
| B → A → C | 18⅔ | 6 | 13⅓ |
| B → C → A | 24 | 6 | 8 |
| C → A → B | 30 | 0 | 8 |
| C → B → A | 24 | 6 | 8 |
| Average | 24⅓ | 3 | 10⅔ |
Every row distributes 38. Averaging each column gives the Shapley allocation. B appears before A in three of the six orders, contributing 6 each time. It appears after A in the other three, contributing zero. Its final value is therefore $(6+6+6)/6=3$.
Group equivalent orders together
We can reuse calculations by grouping the reveal orders according to which features are already known before A joins. Here A is customer group, B is plan, and C is usage. There are four possible starting groups:
| Known before A | Reveal orders | Weight |
|---|---|---|
| None | A → B → C A → C → B | 2/6 = 1/3 |
| B only: plan | B → A → C | 1/6 |
| C only: usage | C → A → B | 1/6 |
| B and C: plan and usage | B → C → A C → B → A | 2/6 = 1/3 |
Each of the six orders is equally likely. A comes first in two orders, so the None group gets weight 1/3. A comes last in two orders, so the B and C group also gets weight 1/3. Each remaining group occurs in one order and gets weight 1/6.
Now calculate how much revealing A increases the prediction from each starting group, and multiply that change by its weight:
- Nothing known: revealing A changes 52 to 76⅔. Weighted change: (1/3) × (76⅔ − 52).
- Only B (plan) known: revealing A changes 58 to 76⅔. Weighted change: (1/6) × (76⅔ − 58).
- Only C (usage) known: revealing A changes 60 to 90. Weighted change: (1/6) × (90 − 60).
- B and C (plan and usage) known: revealing A changes 66 to 90. Weighted change: (1/3) × (90 − 66).
This is the same average we obtained from all six reveal orders. We have simply calculated each distinct change once and weighted it by how often it occurs.
Count what goes before and after A
Let $M$ be the total number of features, including A. Choose one particular group that must appear before A, and let $k$ be the number of features in that group. For example, with A, B, and C, choosing “B only” means $M=3$ and $k=1$.
↓
A
↓
The remaining M − k − 1 features
The final count is $M-k-1$ because we remove the $k$ features already placed before A and then remove A itself. Once the group is chosen, A must occupy position $k+1$.
- Arrange the features before A. Our chosen group has $k$ members, which can appear in $k!$ different orders.
- Arrange the features after A. The remaining $M-k-1$ members can appear in $(M-k-1)!$ different orders.
- Combine those choices. Every arrangement before A can be paired with every arrangement after A. Multiply the counts to get $k!(M-k-1)!$ matching reveal orders.
- Divide by all possible orders. The full set of $M$ features has $M!$ equally weighted reveal orders.
Putting those counts together gives the weight for this particular preceding group:
Check it against our four groups
We have three features, so the denominator is always $3!=6$. The numerator changes with the group placed before A:
| Known before A | k | Weight calculation |
|---|---|---|
| None | 0 | (0! × 2!) / 3! = 2/6 = 1/3 |
| B only: plan | 1 | (1! × 1!) / 3! = 1/6 |
| C only: usage | 1 | (1! × 1!) / 3! = 1/6 |
| B and C | 2 | (2! × 0!) / 3! = 2/6 = 1/3 |
For None, A is first and B and C can follow in either order: A → B → C or A → C → B. That is $0!\times2!=2$ matching orders. For B only, B must come first, A second, and C last. That is just $1!\times1!=1$ matching order. The formula reproduces the counts in our earlier table.
Another way to see the same weights
A is equally likely to be first, second, or third. Each position gets a total weight of 1/3. When A is second, there are two possible preceding groups—B only or C only—so each gets half of that weight: $(1/3)/2=1/6$.
For $M$ features, each position similarly gets weight $1/M$. If $k$ features precede A, there are $\binom{M-1}{k}$ possible groups: this symbol counts the ways to choose $k$ features from the other $M-1$. Splitting the position’s weight equally among those groups gives:
The exact lab uses this equivalent expression. Both formulas count the same reveal orders and give the same four weights: 1/3 for None, 1/6 for B only, 1/6 for C only, and 1/3 for B and C.
Why ordinary path differences give another answer
We can walk the customer’s actual path and attribute the change in the subtree average at each step. At A, the average changes from 52 to $76\frac23$. At C, it changes from $76\frac23$ to 90. That gives A $24\frac23$, B 0, and C $13\frac13$.
This path-difference explanation adds up correctly. It also matches the orders in which A is revealed before either relevant lower split. It does not average the other orders. In our example, it misses the situations in which B changes the partial prediction before A is revealed.
To get SHAP values, we need the average over all six orders. Following Maya’s route gives us just one way to share the $38. Averaging the orders lets each detail contribute both before and after the others are known.
Reproduce every number without an ML library
from tree_shap_lab import TREE, X, coalition_value, enumerate_shap
baseline, phi, groups = enumerate_shap(
lambda known: coalition_value(TREE, X, known), m=3
)
print(baseline, phi)
# 52 [Fraction(73, 3), Fraction(3, 1), Fraction(32, 3)]
assert baseline + sum(phi) == 90
The script uses rational numbers, so these are exact fractions rather than rounded floating-point approximations. It evaluates every group recursively and applies the Shapley weights. This makes it a useful small-example reference, but the number of groups doubles whenever we add a feature.
The Tree SHAP paper develops an efficient alternative to exhaustive groups and discusses the distinction from path-difference attributions. Next we will derive the efficient idea directly from our four leaves.