Causal Data Science for Business Analytics
Hamburg University of Technology
Monday, 22. June 2026
Last week DML handed you one number: the average effect, with an honest interval.
But “on average the coupon adds €1.50” is the answer to a question a manager rarely asks.
The real question is whom should we send it to?
S08 closed with an ATE for the coupon: a tidy point with a \(\sqrt{n}\) interval. Useful — but it hides three managerial questions:
The S08 → S09 move, in one line
S08 averaged the per-unit score \(\psi_i\) into the ATE. S09 projects \(\psi_i\) on covariates. Stop averaging the score — start projecting it on \(X\).

One object — the per-unit score \(\psi_i\) — powers everything today. Average it → ATE. Project / rank / weight it → GATE, \(\tau(x)\), RATE, policy.
| Symbol | ShopSphere | Role |
| \(D\) |
coupon (0/1)
|
targeted discount |
| \(Y\) |
profit_30d (€)
|
30-day profit |
| \(X\) | recency, frequency, monetary, tenure, age, channel, email opens, loyalty | customer covariates |
A naive treated-minus-control difference says the coupon destroys profit: −€1.38. But the firm sends coupons to lapsing customers — who would have spent less anyway. The sign is confounded.
Run the S08 AIPW estimator (cross-fitted \(\hat\mu_1,\hat\mu_0,\hat e\)) and the sign flips:
| Estimator | Effect (€) | Verdict |
| Naive difference | −1.38 | sign-flipped by confounding |
| Linear OLS (controls) | 1.65 | closer, still parametric |
| AIPW (DML) | ≈ 1.5 | CI covers truth (1.69) |
So the coupon adds about €1.5 on average — “meh”. A blanket campaign barely breaks even. The average hides the action.
Define the conditional average treatment effect — the effect for customers like \(x\):
\[\tau(x) \;=\; \mathbb{E}\!\left[\,Y(1) - Y(0) \;\middle|\; X = x\,\right]\]
Same potential-outcome contrast as the ATE — just not averaged away. The ATE is \(\tau(x)\) collapsed to a point.
A natural worry: does conditioning on \(x\) require more assumptions? No.
By the law of iterated expectations, the ATE is just the average of the inner effects:
\[\text{ATE} \;=\; \mathbb{E}\big[\,\tau(X)\,\big] \;=\; \mathbb{E}\big[\,\mathbb{E}[Y(1)-Y(0)\mid X]\,\big]\]
If unconfoundedness \(\{Y(1),Y(0)\}\perp D \mid X\) identifies the outer average, it already identifies the inner effect \(\tau(x)\) — the same assumption, not yet averaged.
The reassurance
Heterogeneity is an estimation challenge (a function is harder than a number), not an identification one. The S08 assumptions carry over verbatim.
A variable in \(X\) can play two distinct roles — and they are independent:
| Role | What it affects | If ignored… |
| Confounder \(C\) | both \(D\) and \(Y\) | bias — must adjust (identification) |
| Effect-modifier \(H\) | the size of \(\tau(x)\) | you miss heterogeneity (description) |
A variable can be both, either, or neither. On ShopSphere frequency is both: it drives who gets a coupon (confounder) and how much the coupon helps (modifier).
Crucial: you condition on \(C\) to remove bias; you condition on \(H\) to describe variation. Different jobs, often the same columns.
How finely should we resolve the effect? It is a bias–variance trade-off:
A useful division of labour
GATE is what you report (stable, interpretable, defensible SEs). \(\tau(x)\) is the engine that ranks and targets — even when no single \(\tau(\hat x)\) is individually precise.
Recall the S08 AIPW pseudo-outcome — the per-unit doubly-robust signal:
\[\psi_i = \big[\hat\mu_1(X_i) - \hat\mu_0(X_i)\big] + \frac{D_i\,(Y_i-\hat\mu_1(X_i))}{\hat e(X_i)} - \frac{(1-D_i)(Y_i-\hat\mu_0(X_i))}{1-\hat e(X_i)}\]
Its defining property — the spine of the whole session:
\[\boxed{\;\mathbb{E}\big[\psi \mid X = x\big] \;=\; \tau(x)\;}\]
So \(\psi_i\) is a noisy unbiased label for the unseen \(\tau(X_i)\). Averaging it gave the ATE; regressing it on \(X\) gives \(\tau(x)\). Every method today is a different regression of \(\psi\).
The discipline that makes this trustworthy: a flexible learner will always hand you a wiggly \(\hat\tau(x)\) — even from pure noise. Estimation is easy; earning belief in the estimate is the work.
The simplest heterogeneity analysis: split customers into pre-specified groups and estimate the effect within each.
Two rules for honest stratification
Coarse: few groups, chosen before seeing outcomes — not data-dredged. Pre-specified: subgroups picked after looking at \(\hat\tau\) are how false discoveries are born.
Tempting shortcut: one OLS with a treatment × modifier interaction — \[Y = \alpha + \tau\,D + \gamma\,H + \delta\,(D\times H) + \varepsilon\] and read \(\delta\) as “how the effect varies with \(H\)”.
Why this breaks
If \(H\) is also a confounder, the interaction \(\delta\) is biased by the same confounding that biases \(\tau\) — and the controls enter linearly, so any nonlinear confounding leaks straight into \(\hat\delta\).
On ShopSphere frequency confounds and modifies — exactly the case where the raw interaction misleads. We need a method that de-confounds first, then describes variation.
The spine, second appearance. We already have a de-confounded per-unit label \(\psi_i\) with \(\mathbb{E}[\psi\mid X]=\tau(x)\). So regress \(\psi\) on group dummies:
\[\widehat{\text{GATE}}(g) \;=\; \frac{1}{n_g}\sum_{i \,\in\, g} \psi_i \;=\; \bar\psi_{\,g}\]
That is just the group mean of the score — and an OLS of \(\psi\) on group indicators returns each GATE as a coefficient with a valid standard error.
The confounding was already removed when we built \(\psi\) (cross-fitted \(\hat\mu_1,\hat\mu_0,\hat e\)). No interaction trap: de-confound first (in \(\psi\)), then describe (by group).
Two reasons the GATE standard errors are trustworthy:
The free lunch
Group-level heterogeneity comes with honest confidence intervals — because it is just an average of an orthogonal score over fixed groups.

GATE by frequency band: 0–1 buys: +€8.0 · 2–3: +€2.0 · 4–6: −€1.1 · 7+ buys: −€4.2. The “meh” €1.4 average was masking a sign change.
Bands are reportable but blunt. To resolve \(\tau\) along a continuous modifier, replace group dummies with a flexible basis:
\[\widehat{\tau}(h) \;=\; \text{smooth of } \psi_i \text{ on } h_i \qquad (\text{kernel or spline regression of the score})\]
Same spine — regress \(\psi\) on the modifier — but the “groups” become a continuum. This recovers a curve instead of bars.
But once the modifier is multivariate (frequency and recency and tenure …), hand-picked bases break down. We need a learner that finds the structure itself — Act 3.
The division of labour, restated
GATE — coarse, honest CIs, board-ready. The number you put in a slide deck for stakeholders. \(\tau(x)\) — fine, individual-level, the engine that drives ranking and targeting.
You can report GATEs and still act on \(\tau(x)\) — the two are complementary, not competing.
Next: estimate the continuous \(\tau(x)\) — and confront the fact that flexible learners invent heterogeneity if you let them.
The meta-learner recipe: build \(\tau(x)\) out of off-the-shelf regressions.
Both are intuitive and use any regressor you like. But both optimize the wrong objective — next slide.
The meta-learner critique
S- and T-learners minimize outcome prediction error (MSE on \(Y\)). But \(\tau(x)\) is a difference of two large surfaces — a learner tuned to predict \(Y\) can be badly tuned to estimate their difference. (Curth and Schaar 2023)
Two failure modes:
The cure: target the causal quantity directly — split on effect heterogeneity (forests) or regress the score (DR-learner). (Lechner 2018)
A causal tree changes the splitting rule. A regression tree splits to make \(Y\) homogeneous; a causal tree splits to make the treatment effect differ across leaves.
The danger: choosing and estimating on the same data
Pick the split and estimate the leaf effect from the same rows, and you bias the effect toward the split — the leaf looks more extreme than it is. (Overfitting the heterogeneity.)

Honest estimation: use one subsample to choose the splits, a different subsample to estimate the leaf effects. The effect estimate never sees the data that chose its split → unbiased within-leaf. (Athey and Imbens 2016)
A causal forest averages many honest causal trees — but it is more than an average. (Wager and Athey 2018; Athey, Tibshirani, and Wager 2019)
In effect: a forest is adaptive nearest-neighbour matching, where the metric is learned to track effect heterogeneity — not outcome level.
At each target point \(x\), the forest estimates \(\hat\tau(x)\) by solving a locally-weighted version of the same moment we have used all along — units near \(x\) count more:
\[\sum_i \alpha_i(x)\,\psi\big(W_i;\,\tau(x)\big) \;=\; 0\]
So the forest is the local cousin of DML: globally average the score → ATE; locally weight it → \(\tau(x)\). (Splitting algebra lives in the notes.)

Estimated vs known true \(\tau(x)\): causal forest tracks truth at corr 0.97; the T-learner is visibly noisier at corr 0.93. Targeting the causal quantity pays off.

The estimated \(\hat\tau(x)\) ranges from −€5 to +€17 (SD ≈ 5.0), centred near the €1.4 ATE. About 54% of customers have a positive estimated effect. The average was the tip of an iceberg.
| Learner | What it regresses | Targets \(\tau\) directly? | Watch out for |
| S | \(Y\) on \((D,X)\), one model | no (difference of preds) | shrinks \(D\) away |
| T | \(Y\) on \(X\) per arm, two models | no (difference of preds) | mismatched smoothness; halves data |
| X | imputed unit effects on \(X\), propensity-blended | closer | helps under treatment imbalance |
| R | \(\tilde Y\) on \(\tilde D\) (Robinson residuals), \(\tau(x)\)-weighted | yes | needs good nuisances |
| DR | the AIPW score \(\psi\) on \(X\) | yes (orthogonal) | the spine — our default |
The pattern: S/T predict \(Y\) and subtract; X/R/DR target \(\tau\) itself. Causal forests can fit the R- or DR-style objective internally. (Künzel et al. 2019; Nie and Wager 2021; Kennedy 2023)
When one arm is much larger (few treated, many controls — common in observational data), the X-learner uses both arms efficiently: (Künzel et al. 2019)
\[\hat\tau(x)\;=\;\big(1-\hat e(x)\big)\,\hat\tau_1(x)\;+\;\hat e(x)\,\hat\tau_0(x)\]
The blend leans on the larger arm’s (better-estimated) model where data is thin — visibly better than the plain T-learner when treatment is rare.
The R-learner targets \(\tau(x)\) through the Robinson partialling-out you know from DML — now with a slope that varies in \(x\): (Nie and Wager 2021)
\[\hat\tau(\cdot) \;=\; \arg\min_{\tau}\ \sum_i \Big[\underbrace{(Y_i-\hat\ell(X_i))}_{\tilde Y_i} - \tau(X_i)\,\underbrace{(D_i-\hat e(X_i))}_{\tilde D_i}\Big]^2\]
\(\hat\ell(X)=\mathbb{E}[Y\mid X]\), \(\hat e(X)=\mathbb{E}[D\mid X]\) are the S08 cross-fitted nuisances; built on \(\tilde Y,\tilde D\) it inherits DML’s orthogonality.
Factor out \(\tilde D_i^2\) → an ordinary weighted regression:
\[\hat\tau(\cdot) \;=\; \arg\min_{\tau}\ \sum_i \underbrace{(D_i-\hat e(X_i))^2}_{\text{weight } w_i}\,\Big(\underbrace{\tfrac{Y_i-\hat\ell(X_i)}{D_i-\hat e(X_i)}}_{\text{pseudo-outcome } \tilde Z_i}\;-\;\tau(X_i)\Big)^2\]
Regress \(\tilde Z_i=\tilde Y_i/\tilde D_i\) on \(X\), weighted by \(w_i\) — any sample-weight learner (forest, boosting, NN) fits it; the weight vanishes where \(\tilde D_i\to0\), so \(\tilde Z_i\) can’t explode.
The third appearance of the spine — and our default. Take the per-unit AIPW score and regress it on the covariates: (Kennedy 2023)
\[\hat\tau(x) \;=\; \widehat{\mathbb{E}}\big[\,\psi \,\big|\, X = x\,\big], \qquad \mathbb{E}[\psi\mid X=x]=\tau(x)\]
Doubly robust and orthogonal, so the second-stage learner can be a black box without contaminating \(\tau(x)\) to first order. This is why a causal forest fit on \(\psi\) recovers truth so well.
Practical guidance
Default to DR-learner / causal forest (orthogonal, targets \(\tau\)). Reach for the X-learner under heavy treatment imbalance. Use S/T only as quick baselines. (Curth and Schaar 2023)
But there is no universally best learner — the winner depends on the true \(\tau(x)\) surface (smooth vs sparse vs sharp), the sample size, and overlap.
The unifying lesson
What makes all the good ones good is the same thing: an orthogonal score that targets \(\tau\) directly, not the outcome \(Y\).
Step back. Across forests, R-learner, and DR-learner, the engine is the same:
So heterogeneity estimation is not a new paradigm — it is DML, refusing to take the final average.
The uncomfortable truth
Fit a causal forest on data with zero true heterogeneity (\(\tau(x)\equiv\text{const}\)) and you still get a varying \(\hat\tau(x)\) — a wiggly histogram of pure noise.
The estimate looks the same whether heterogeneity is real or invented. So before acting, we must answer two questions with inference, not eyeballing:
Regress the orthogonal score \(\psi\) on the (out-of-fold) estimate \(\hat\tau(x)\) — the spine doing validation:
\[\psi_i \;=\; \beta_1 + \beta_2\,\big(\hat\tau(X_i) - \overline{\hat\tau}\big) + \varepsilon_i\]
The decisive coefficient
\(\beta_2 = 1\): \(\hat\tau\) is perfectly calibrated. \(\beta_2 = 0\): \(\hat\tau\) is noise — it does not track the truth. Test \(H_0\!:\beta_2=0\). (Chernozhukov et al. 2018)
| Quantity | Estimate | Reading |
Is the ranking real? test_calibration(cf) — project the score on the forest’s own \(\hat\tau\)
|
||
| Differential prediction (\(\beta_2\)) | 1.08 (\(t = 36.6\)) | \(\hat\tau\) tracks truth, slope ≈ 1 |
| Mean-forest calibration (\(\beta_1\)) | 0.955 | level ≈ correct (ATE) |
What drives it? best_linear_projection(cf, ·) — project on named covariates
|
||
Slope on frequency
|
−1.19 (\(p<2\!\times\!10^{-16}\)) | effect falls as frequency rises |
\(\beta_2 = 1.08\), \(p < 2\times10^{-16}\): we decisively reject \(H_0\!:\beta_2=0\) — the wide histogram was signal, not noise. A separate projection onto covariates then pins the gradient — the effect falls €1.19 per purchase — confirming the negative driver the GATE bars already ranked dominant.
Real heterogeneity is necessary but not sufficient — we need to know targeting pays. The RATE (Rank-Weighted Average Treatment Effect) measures the gain from prioritizing by \(\hat\tau\): (Yadlowsky et al. 2025)
RATE = area under the TOC
If \(\hat\tau\) ranks well, the top customers gain much more than treating everyone — the TOC stays above the flat random-targeting line at 0. RATE > 0 (a QINI cousin) certifies the ranking has value.

\(\widehat{\mathrm{TOC}}(q)=\overline{\psi}\,\big|_{\text{top-}q\text{ by }\hat\tau}-\overline{\psi}_{\text{all}}\) — the benefit over treat-all, scored by the doubly-robust \(\psi\) (rank by \(\hat\tau\), score by \(\psi\))
Treat the top-ranked customers and you gain far more than treating everyone — about €12/customer extra for the top decile, shrinking to €0 once you treat the whole base. RATE (AUTOC) = 5.24 (\(t = 34\)) — the ranking has real, sizeable value.
Take the top- and bottom-\(\hat\tau\) groups and compare their covariate means — here, the profile across \(\hat\tau\) quintiles: (Chernozhukov et al. 2018)

High-effect = low-frequency, recent, non-loyal persuadables; low-effect = loyal regulars. A describable persona — and a sanity check: if the extremes made no business sense, distrust the model.
Before you act on \(\hat\tau(x)\), clear three gates
Only an estimate that clears all three earns a policy. A wiggly histogram alone earns nothing.
A manager cannot deploy a histogram. The deliverable is a policy — a function that says, for each customer, treat or not:
\[\pi : X \;\longrightarrow\; \{0, 1\}\]
We judge a policy by its value — the expected outcome if we deploy it:
\[V(\pi) \;=\; \mathbb{E}\big[\,Y(\pi(X))\,\big]\]
The goal of policy learning: find the \(\pi\) that maximizes value subject to constraints (budget, fairness, explainability) — not the \(\pi\) that best estimates \(\tau(x)\).
The naive policy: treat customer \(i\) iff \(\hat\tau(X_i) > 0\). Intuitive — but flawed.
CATE accuracy ≠ policy quality
A learner can have low CATE-MSE yet make bad decisions — what matters for a policy is getting the sign right near the threshold \(\tau(x)=0\), not the magnitude everywhere. (Qian and Murphy 2011)
So we should optimize the decision boundary directly, not plug a noisy point estimate into a threshold.
Since \(\pi\in\{0,1\}\), the deployed outcome is \(Y(\pi(X))=\pi(X)\,Y(1)+(1-\pi(X))\,Y(0)\). Take the expectation — the treat-none baseline drops:
\[V(\pi)=\mathbb E\big[Y(\pi(X))\big]=\underbrace{\mathbb E[Y(0)]}_{\text{treat-none baseline (constant)}}+\;\mathbb E\big[\pi(X)\,\tau(X)\big]\]
Only the second term depends on \(\pi\), so the value-maximizing rule collects the effect \(\tau\) where it treats:
\[\hat\pi=\arg\max_{\pi}\ \mathbb E\big[\pi(X)\,\tau(X)\big]\]
Treat where \(\tau>0\). Two refinements follow — both still in \(\tau\): recenter on a coin flip (the symmetric \(2\pi-1\) form), then — only once the formula is derived — replace the unseen \(\tau\) by the score \(\psi\).
Subtract the coin-flip value \(V(\tfrac12)=\tfrac12\mathbb E[Y(1)]+\tfrac12\mathbb E[Y(0)]\) (treating at random — the best rule ignoring \(X\)) — a constant in \(\pi\), so \(\arg\max\) is unchanged:
\[\begin{aligned} \hat\pi=\arg\max_\pi V(\pi) &=\arg\max_\pi\ \mathbb E[Y(\pi)] =\arg\max_\pi\ \mathbb E[Y(\pi)]-\tfrac12\mathbb E[Y(1)]-\tfrac12\mathbb E[Y(0)]\\[2pt] &=\arg\max_\pi\ \mathbb E\big[\pi Y(1)+(1-\pi)Y(0)\big]-\tfrac12\mathbb E[Y(1)]-\tfrac12\mathbb E[Y(0)]\\[2pt] &=\arg\max_\pi\ \mathbb E\big[(\pi-\tfrac12)Y(1)\big]+\mathbb E\big[(\tfrac12-\pi)Y(0)\big] =\arg\max_\pi\ \mathbb E\big[(\pi-\tfrac12)(Y(1)-Y(0))\big]\\[2pt] &=\arg\max_\pi\ 2\,\mathbb E\big[(\pi-\tfrac12)(Y(1)-Y(0))\big] =\arg\max_\pi\ \mathbb E\big[(2\pi-1)(Y(1)-Y(0))\big]\\[2pt] &\overset{\text{LIE}}{=}\ \arg\max_\pi\ \mathbb E\big[(2\pi-1)\,\tau(X)\big] \end{aligned}\]
Sample version, with the score (\(\mathbb E[\psi\mid X]=\tau\)):
\[\boxed{\,\hat\pi=\arg\max_{\pi}\ \tfrac1n\sum_i\big(2\pi(X_i)-1\big)\psi_i\,}\]
Split each score into sign × size, \(\psi_i=\operatorname{sign}(\psi_i)\,|\psi_i|\), and three roles appear:
\[\tfrac1n\sum_i\big(2\pi(X_i)-1\big)\psi_i=\tfrac1n\sum_i\underbrace{|\psi_i|}_{\text{weight}}\;\underbrace{\operatorname{sign}(\psi_i)}_{\text{label }y_i}\;\underbrace{\big(2\pi(X_i)-1\big)}_{\text{prediction }\hat y_i}\]
Both \(y_i\) and \(\hat y_i\) live in \(\{-1,+1\}\), so their product is the classification margin: \(\,y_i\hat y_i=+1\) if the policy agrees with the sign (a right call), \(-1\) if it disagrees (a mistake). Maximizing weighted agreement is minimizing weighted misclassification:
\[\hat\pi=\arg\min_{\pi}\ \tfrac1n\sum_i|\psi_i|\,\cdot\,\mathbf 1\big\{\,2\pi(X_i)-1\neq\operatorname{sign}(\psi_i)\,\big\}\]
label \(=\operatorname{sign}(\psi_i)\) (treat the helped, withhold the hurt) · weight \(=|\psi_i|\) (the stakes) · classifier \(=\pi\). The sign/size split makes the weights \(\ge 0\), so any weighted classifier fits it — tree, logistic, SVM. (Athey and Wager 2021)
Restrict \(\pi\) to a shallow decision tree and the value-maximizing rule becomes human-readable: (Zhou, Athey, and Wager 2023)

The rule sends the coupon to recent, infrequent buyers (recency < 132 and frequency < 4) and to the lapsed near-one-timer (recency ≥ 132 and frequency < 1) — net-positive since near-zero frequency leaves no margin to cannibalize. Everyone else: withhold — exactly the GATE story.

Held-out value per customer: treat-none €21.18 · treat-all €22.80 · policy tree €24.04. The two-question rule beats a blanket campaign by +€1.24 / customer — and beats treating no one by far more.
Before deployment
Report the policy’s value with a confidence interval (is +€1.24 distinguishable from treat-all?), and run CLAN on the policy — who does it treat, who does it withhold from? Audit for fairness and sensibility.
The deployment standard: a rule a stakeholder can read, a value they can trust, and a population you can characterize.
Closing Part IV — Causal Machine Learning
S08 + S09 are one method: orthogonalize and cross-fit the score, then either average it (ATE) or project it (heterogeneity, targeting, policy).
The through-line of the whole course: identify the effect, estimate it honestly, then act on it.
| Thank you for your attention! | |
|
|
Causal Data Science for Business Analytics | 9. Treatment Effect Heterogeneity