← All posts
Machine Learning · TreeSHAP · Part 3 of 5

How TreeSHAP Avoids Listing Every Feature Group

We are still explaining Maya: A = Returning, B = Premium, C = High usage. The tree predicts 90, its baseline is 52, and the feature contributions from the previous chapter are 24⅓, 3, and 10⅔. We will derive those same numbers using the leaf paths.

Calculate one leaf at a time

When some inputs are hidden, the prediction is a weighted average of the four leaves. This table records each leaf's part of the baseline:

LeafConditionsCustomersBaseline part
10New, Basic3 of 103
30New, Premium1 of 103
50Returning, Low2 of 1010
90Returning, High4 of 1036

Revealing an input changes these weights. The change in the whole prediction is the sum of the changes from the leaves. Averaging over reveal orders preserves that sum, so we can calculate a feature's contribution at each leaf and then add them:

$$\phi_j=\sum_{\text{leaves }\ell}\phi_{j,\ell}.$$

Follow the weight reaching the 90 leaf

The path first asks whether Maya is Returning, then whether her usage is High. Six of the ten customers are Returning; four of those six have High usage.

10 customers → 6 Returning → 4 High usage
Hidden route weight = (6/10) × (4/6) = 2/5.
Baseline part = (2/5) × 90 = 36.

At an unknown split, use the child-to-parent count ratio. At a known split, follow Maya's value: the branch she takes gets multiplier 1, and the other branch gets 0. This gives all four cases for the 90 leaf:

Known detailsRoute weightWeight × 90
None(6/10) × (4/6)36
Only A1 × (4/6)60
Only C(6/10) × 154
A and C1 × 190

Calculate A’s share of the 90 leaf

Plan B is absent from this path and has no effect on this leaf. In the six reveal orders, A comes before C three times and after C three times. The two cases therefore have equal weight:

Subtract the leaf values before and after revealing each feature.
Order on this pathA’s changeC’s change
A, then CNone → Only A
60 − 36 = 24
Only A → A and C
90 − 60 = 30
C, then AOnly C → A and C
90 − 54 = 36
None → Only C
54 − 36 = 18
Average(24 + 36) / 2
= 30
(30 + 18) / 2
= 24

This leaf is fully accounted for: 36 + 30 + 24 = 90.

Add the four leaf explanations

LeafBaselineABC
103−1.5−1.50
303−7.54.50
50103⅓0−13⅓
903630024
Total5224⅓310⅔

Negative entries account for leaves that lose weight. Revealing that Maya is Returning rules out the 30 leaf, for example. Adding the columns gives 52 + 24⅓ + 3 + 10⅔ = 90.

Separate the feature being revealed from the others

To extend the calculation to longer paths, write $z$ for a feature's multiplier while hidden and $o$ for its multiplier when revealed. On our 90-leaf path:

FeatureHidden: zRevealed: o
A: Returning3/51
C: High usage2/31

Revealing A changes only A's multiplier, from 3/5 to 1. The leaf value and C's current multiplier stay fixed:

$$\begin{aligned} \text{C hidden: }&90(1-3/5)(2/3)=24,\\ \text{C revealed: }&90(1-3/5)(1)=36. \end{aligned}$$

The same reasoning applies to any feature j on any leaf path: multiply the leaf value $u$ by j's change $o_j-z_j$, then by the other features' multipliers. We only need an efficient way to average that last product over reveal orders.

Group the products by how many features came before j

Let $d$ be the number of distinct features on the path. For each of the other $d-1$ features, choose $z_i$ when hidden or $o_i$ when revealed. Groups with the same number of revealed features have the same Shapley weight, so we can add their products first.

Store those sums as the coefficients of a polynomial:

$$Q_j(t)=\prod_{i\ne j}(z_i+o_it).$$

The product includes only the other features on this path. Selecting $z_i$ leaves feature i hidden; selecting $o_it$ reveals it. The power of $t$ counts the revealed features. Consequently, the coefficient $q_k$ of $t^k$ adds all products with exactly $k$ revealed features.

Feature j is equally likely to occupy any of the d positions. Given that k features precede it, each of the $\binom{d-1}{k}$ possible groups is equally likely. Each such group therefore gets weight $1/[d\binom{d-1}{k}]$. The weighted average of the other features' product is:

$$W_j=\sum_{k=0}^{d-1}\frac{q_k}{d\binom{d-1}{k}}.$$

Multiply this average by the leaf value and j's multiplier change:

$$\phi_{j,\mathrm{leaf}}=u(o_j-z_j)W_j.$$

Apply the equation to the same 90 leaf

For A, the only other feature is C. Its factor is $2/3+t$, so $q_0=2/3$ and $q_1=1$. The two positions for A each have weight 1/2:

$$W_A=\frac{2/3}{2}+\frac12=\frac56.$$
$$\phi_{A,\mathrm{leaf}}=90(1-3/5)\frac56=30.$$

This is exactly the table's average, $(24+36)/2$. The polynomial has organized the same calculation by how many other features are already known.

Update the coefficient list without listing the groups

Start with the coefficient list [1]. Multiplying by one more factor $(z+ot)$ updates entry k as follows:

$$c'_k=zc_k+oc_{k-1}.$$

There are two ways to finish with k revealed features. Keep the new feature hidden and use an old group of size k, or reveal it and use an old group of size k−1. The equation adds both possibilities. Entries outside the old list are zero.

Build the full product for the 90 leaf, adding A and then C.
Added factorCoefficient list
None[1]
A: 3/5 + t[3/5, 1]
C: 2/3 + t[2/5, 19/15, 1]

The middle entry is $(3/5)\times1+1\times(2/3)=19/15$. It combines “only C revealed” and “only A revealed.” Every path update combines products this way, keeping one number for each group size.

Build the full product $P$ once. For a particular j, remove its factor to obtain $Q_j$. From $P=(z_j+o_jt)Q_j$, matching coefficients gives:

$$q_0=\frac{p_0}{z_j},\qquad q_k=\frac{p_k-o_jq_{k-1}}{z_j}.$$

Here $p_k$ is a full-product coefficient. Removing A gives $q_0=(2/5)/(3/5)=2/3$ and $q_1=(19/15-2/3)/(3/5)=1$. We have recovered C's factor without rebuilding the product. This division assumes positive hidden fractions, as in our tree.

If a path tests the same feature twice, combine its multipliers

Knowing one feature's value answers every split that tests it. If two splits on the path both test j, combine their hidden fractions and their revealed fractions:

$$z_j=z_{j,1}z_{j,2},\qquad o_j=o_{j,1}o_{j,2}.$$

The combined revealed multiplier is 1 only if Maya's value satisfies both conditions. Otherwise it is 0. Use this combined pair as one factor in the polynomial. Counting the two splits as separate features would allow one occurrence to be known while the other remains hidden, which cannot represent knowing the input.

This is why d counts distinct features. In the recursive TreeSHAP implementation, an unwind removes the feature's earlier path entry and an extend inserts the combined entry.

Why this takes less work

A path's coefficient list grows one entry at a time. Building it takes $1+2+\cdots+d=O(d^2)$ work. Removing one factor and computing its weighted sum takes $O(d)$; repeating this for all d features also takes $O(d^2)$.

For T trees, at most L leaves per tree, and maximum path depth D, this gives $O(TLD^2)$ arithmetic operations for one explained row. The calculation stores sums by group size instead of evaluating every feature group separately.

Add tree contributions in the same way as tree predictions

The same addition rule used for leaves also applies to trees. If a model predicts $F(x)=b+\sum_t a_tf_t(x)$, then:

$$\begin{aligned} \phi_{0,F}&=b+\sum_t a_t\phi_{0,t},\\ \phi_{j,F}&=\sum_t a_t\phi_{j,t}. \end{aligned}$$

The constant b goes into the baseline. A forest average uses $a_t=1/T$; a boosted sum uses the weights in that sum. These equations hold on the additive output scale being explained.

We have now obtained the contribution formula and its efficient calculation. Both depend on the rule used when a feature is hidden. The next chapter changes that rule while keeping Maya, the ten customers, and the tree fixed.

The exact Python calculation implements the polynomial form above. The TreeSHAP paper and reference implementation express the path updates through extend and unwind operations.