chc.lift¶
Stability tier: experimental.
Lift tests read as structure: a geo test's gap between its groups, period by period, identifies a channel's carryover, the shape of its curve and its size, over the spend the test covered. Experimental.
A geo test changes one channel's spend in one group of regions and leaves another group spending as planned. The groups share everything else -- the season, the other channels, promotions, every demand shock nobody observed -- so the difference of their outcomes is the channel's own::
d_t = M(s_treated)_t - M(s_control)_t + nu_t,
M the channel's return on a spend path (a chc.response.Channel) and nu_t the
groups' own noise (Heusch 2026a). Every parameter of M is in it: the gap's rise when the spend
changes and its decay after carry the carryover, tests at different spend levels carry the curve's
shape, and the gap's size carries the coefficient. A test reduced to one number, its total lift
over its total spend, keeps only the last.
fit_lift fits the equation by least squares over every period of every test, with one noise
scale across them, and gives each parameter a profile-likelihood interval with an F cutoff
(Bates and Watts 1988): exact for a model linear in its parameters and, unlike a Wald interval, it
follows a likelihood that curves. A parameter the tests do not pin runs to its bound, and its
interval says so: a curve the tests never bent has no upper scale.
The curve is identified over the adstock the tests covered, LiftFit.tested_adstock, and
nowhere else. Past it a plan reads the family's shape, not the experiment.
check_observational reads a channel fitted to observational data, where spend follows the
business, against the tests a fit read: an F test of whether the tests' gaps could be the
channel's, and the factor of the lift it predicts that the tests read. Through an analyst's
effect-scale gap the factor gives the least marginal-sensitivity Gamma the tests leave the
observational channel, a floor under the confounding a plan assumes (De Bartolomeis et al. 2024
bound Gamma from below with a trial in the same way).
HONEST SCOPE:
- The noise is one scale, independent across periods and tests. A gap read through a synthetic control or a regression carries an error common to its periods, which this does not model; the intervals are then too narrow.
- The groups spend alike on every other channel and answer alike, so a group's spend and outcome divided by its share of the market are the market's (Heusch's scaling).
- The check's
Fis the region the intervals are profiles of: exact for a model linear in its parameters, approximate on a curve. ItsGammabounds the tested channel's confounding; read for a channel no test reached, it assumes the two are confounded alike. - The arithmetic is JAX's, as for the library's other JAX estimators: float64 under
jax_enable_x64, and float32, good to about seven digits, without it.
GeoArm
dataclass
¶
One group of a geo test: the tested channel's spend and the group's outcome. Experimental.
spend runs over the periods before the readout and then the readout, outcome over the
readout. A period's adstock reads the spend of the kernel's length up to it, so the readout's
first periods need the spend before them: a window cut from a longer series, with nothing
before it, understates the adstock at its start (Heusch 2026b). share is the group's share
of the market the channel is modelled at; its spend and outcome are divided by it.
Raises:
| Type | Description |
|---|---|
ValueError
|
on an empty or non-finite series, a negative spend, a spend shorter than the
readout, or a share outside |
history
property
¶
The periods of spend before the readout.
LiftTest
dataclass
¶
A geo test of one channel: a treated group whose spend on it changed and a control group that spent as planned, alike on every other channel. Experimental.
Raises:
| Type | Description |
|---|---|
ValueError
|
when the groups' spend or readout cover different numbers of periods. |
difference
property
¶
The treated group's outcome less the control's, each at the market's scale.
LiftFit
dataclass
¶
A channel fitted to lift tests' gaps, with an interval for each parameter.
Experimental. See fit_lift.
interval(parameter)
¶
parameter's interval, by its name in parameters.
ObservationalCheck
dataclass
¶
An observational channel read against the lift tests a fit read. Experimental. See
check_observational.
rejected
property
¶
Whether the tests reject the channel at level.
least_gamma(cvar_gap)
¶
The least marginal-sensitivity Gamma whose identified set reaches the tests.
In units of the lift the channel predicts, the set is 1 ± (Gamma-1)/(Gamma+1) cvar_gap,
chc.sensitivity's radius, cvar_gap the analyst's effect-scale CVaR gap as a share of
that lift. The least Gamma whose set reaches factor_interval inverts the radius
at the interval's distance from 1, as chc.barrier.barrier_gamma_star does. Less
confounding than this the tests refute at level, as a known-null outcome refutes it
in chc.uncertainty.negative_control_gamma: a floor, not a ceiling.
1.0 when the interval holds 1; inf when it lies cvar_gap or further from 1, so
that no level reconciles the two and the tests refute the model rather than calibrate it;
nan when the channel predicts no gap.
Raises:
| Type | Description |
|---|---|
ValueError
|
when |
fit_lift(tests, channel, *, level=0.95)
¶
Fit channel to the tests' gaps by least squares, each parameter with a profile-likelihood
interval. Experimental.
channel names the families, its kernel and its curve, and its kernel's and curve's values
are where the fit starts, so give a scale near the spend the tests saw. Every parameter is
fitted: a coefficient of either sign, so a channel that loses sales and one that does nothing
are both answers; a retention in (0, 1] and each scale and shape as its logarithm, within
a factor of e^40 of its start. The coefficient is never searched: the gap is linear in it,
so wherever the rest moves it takes its least-squares value there (variable projection, Golub
and Pereyra 1973), and the template's own is ignored. The periods of every test are stacked,
with one noise variance, estimated.
Each interval holds the values whose profile, the least cost with that value held, lies within
s^2 F_{1, n - p}(level) of the least cost, s^2 the noise variance over n - p degrees
of freedom. A walk out from the estimate finds each end and a root-finder sharpens it; an end
the tests leave open is the parameter's bound, or 0 or inf for a retention, a scale or
a shape that moves a factor of e^20 without reaching the cutoff. Should the walk find a
point cheaper than the fit by a millionth of the cutoff, the fit restarts from there.
Where the least cost lies on the way to a bound, the estimate is where the fit stopped on that way. Tests that never bent the curve are fitted best by a line, which a saturating family reaches only as its scale and coefficient grow without end; noise the size of the effect can be fitted best by a step, a curve saturated at any spend with a carryover gone within a period, which the family reaches as the scale and the retention fall together. The estimate predicts the gaps as the limit does, and its interval, open on the limit's side, is the answer about the parameter.
Raises:
| Type | Description |
|---|---|
TypeError
|
when |
ValueError
|
on no tests, |
RuntimeError
|
when a solve does not converge within 10 000 evaluations, or the profile keeps finding points cheaper than the fit. |
check_observational(fit, observed)
¶
Read observed, a channel fitted to observational data, against the tests fit read.
Experimental.
Two readings of the tests' gaps d, at the fit's level and with its noise variance s^2
over n - p degrees of freedom:
- An
Ftest ofobservedas a point of the fit's model: the least squares of the gaps it predicts over the fit's, per parameter, overs^2, againstF_{p, n-p}(Bates and Watts 1988), the joint region the fit's intervals are profiles of. - The factor, the least-squares multiple of the gaps it predicts,
g, that the tests read:c = g.d / g.g, with thetintervalc ± t s / |g|. It is the tested lift over the liftobservedpredicts, each period weighted by what it predicts there, so tests of either sign add rather than cancel;1when the lift is the channel's.
The F rejects a channel whose shape the tests contradict even where its size is right; the
factor says how far its size is off, and ObservationalCheck.least_gamma turns that into
a floor under the confounding. Where spend follows the business an observational fit reads the
business's decisions as the channel's effect, and the tests are what can say so: on Heusch's
generator it reads paid shopping's return at 8.45 against a true 4.20, with an oracle's
controls (Heusch 2026a).
Raises:
| Type | Description |
|---|---|
TypeError
|
when |
ValueError
|
when |