Variance attribution (OLS + Shapley/LMG) — v1.0.0
What question it answers
Descriptive coverage says "la consommation varie de X % entre régions". This method
answers the harder question: **"de cette variation, quelle part chaque facteur
explique-t-il ?"* — e.g. "35 % s'explique par le climat, 8 % par la part résidentielle,
57 % reste inexpliqué (bâti, chauffage, revenus non mesurés)."*
How it works
y = β₀ + Σ βⱼ·Xⱼ + ε over the cross-section.
Var(y) the factors explain together.
incremental R² of each factor over all orderings of the regressors. This is the
order-independent, fair attribution when factors are correlated — a plain "add factors
one by one" split would depend on the (arbitrary) order. The shares plus
residual_share = 1 − R² sum to 1.
statistically distinguishable from zero (|t| > 1.96 ≈ 95 %, large-n normal).
Worked shape (illustrative)
Input: y = conso_moyenne_kwh per commune; factors dju (climate), part_residentielle.
Output: `{ r_squared: 0.41, residual_share: 0.59, components: [
{ factor: "dju", share: 0.35, coefficient: …, t_stat: 6.2, significant: true },
{ factor: "part_residentielle", share: 0.06, coefficient: …, t_stat: 1.1, significant: false } ] }`.
Reading: climate explains ~35 % of the inter-commune spread and is highly significant; the
residential share adds little and isn't distinguishable from zero; 59 % is unexplained —
the missing confounders (building age, heating type, income) are named, not hidden.
Honesty / limits (read before publishing any number from this)
computed fact. OLS shares measure association
controlling for the supplied factors only. An omitted confounder inflates the share of
whatever it correlates with — so the share is an upper bound on a factor's true import when
confounders are missing.
both. A high residual means "we mostly don't know" — say so.
on wholesale prices is meaningless (no exogenous variation) — such pairs are not whitelisted.
break determinism/replay. We give deterministic t-based significance instead.
Determinism
Closed-form OLS (Gauss–Jordan inverse) + exhaustive subset enumeration for the Shapley shares
(k ≤ 6 → ≤ 64 subsets). No Math.random, no Date, no I/O. Same inputs → byte-identical
output → replayable from (method_id, version, input_hash).