Incrementality.compute_incremental_contribution#
- Incrementality.compute_incremental_contribution(frequency, start_date=None, end_date=None, include_carryover=True, num_samples=None, random_state=None, counterfactual_spend_factor=0.0, central_tendency='median')[source]#
Compute incremental channel contributions using counterfactual analysis.
Core incrementality function. Compares the model’s prediction under actual spend with its prediction under a counterfactual spend scenario, properly accounting for adstock carryover. Results are always returned in the original scale of the target variable, with the model’s link function applied – see the
module docstringfor the full mathematical formulation and for what per-channel increments do and do not mean under a multiplicative (log-link) model.- Parameters:
- frequency{“original”, “weekly”, “monthly”, “quarterly”, “yearly”, “all_time”}
Time aggregation frequency.
"original"uses data’s native frequency."all_time"returns a single value across the entire period.- start_date
strorpd.Timestamp, optional Start date for evaluation window. If None, uses start of fitted data.
- end_date
strorpd.Timestamp, optional End date for evaluation window. If None, uses end of fitted data.
- include_carryoverbool, default=True
Include adstock carryover effects. When True, prepends
l_maxobservations before the period to capture historical effects carrying into the evaluation period, and extends the evaluation window byl_maxperiods to capture trailing adstock effects from spend during the period.- num_samples
intorNone, optional Number of posterior samples to use. If None, all samples are used. If less than total available (chain × draw), a random subset is drawn.
- random_state
RandomStateorGeneratororNone, optional Random state for reproducible subsampling. Only used when
num_samplesis not None.- counterfactual_spend_factor
float, default=0.0 Multiplicative factor α applied to channel spend in the counterfactual scenario.
0.0(default): Zeroes out channel spend → total incremental contribution (classic on/off counterfactual).1.01: Scales spend to 101% of actual → marginal incremental contribution (response to a 1 % spend increase).Any value ≥ 0: Supported. Values > 1 measure the upside of more spend; values in (0, 1) measure the cost of less spend.
Note that α intervenes on spend, which is not the same as removing a channel’s effect. The two coincide only when the saturation maps zero spend to zero contribution (as
LogSaturationdoes). With a saturation whose value at zero spend is non-zero, or withtime_varying_mediascaling the contribution,0.0still leaves a residual channel effect in the response. To remove a component’s effect outright, usecompute_counterfactual_contributions_dataset().- central_tendency{“median”, “mean”}, default=”median”
Central tendency of the predictions being differenced. Only meaningful for non-linear links: under
link="log"the model’s response-scale prediction \(\exp(\mu)\,s\) is the median of theLogNormallikelihood, and"mean"rescales it by \(\exp(\sigma^2 / 2)\) to give an increment on the conditional-mean scale. Ignored underlink="identity", where the Normal mean and median coincide.
- Returns:
xr.DataArrayIncremental contributions in original scale with dimensions:
(chain, draw, date, channel, *custom_dims)whenfrequency != "all_time"(chain, draw, channel, *custom_dims)whenfrequency == "all_time"
For models with hierarchical dimensions like
dims=("country",), output has shape(chain, draw, date, channel, country).Sign convention: The result is always
Y(perturbed) − Y(actual)when α > 1 andY(actual) − Y(counterfactual)when α < 1 (including 0). Both total and marginal incrementality are therefore positive for channels with a positive effect.Estimand: each channel’s number is a unilateral intervention – what changes when that channel’s spend is scaled by α, with the others at actual spend. At α = 0 that is the leave-one-out question. The numbers sum to the total only when the response is additive in the channels; see
compute_joint_incremental_contribution().
- Raises:
ValueErrorIf frequency is invalid, period dates are outside fitted data range,
counterfactual_spend_factoris negative, orcentral_tendencyis not one of{"median", "mean"}. Also raised if amu_effectdeclares fewer carryover lags, or a narrowerevaluation_mode, than a spend counterfactual was measured to need (a declaration narrower than what was measured is refused rather than silently overridden); if the model produces non-finite predictions (NaN or infinity) for some posterior draw, which usually means a transform is dividing zero by zero; or if a post-fit mutation of an auxiliary date-indexed input is detected (MMM.sample_posterior_predictive(..., clone_model=False)or a directpm.set_data(...)call after fitting). Seespend_reachfor the full story on each.NotImplementedErrorIf the model’s link function has no
IncrementalReducer, or amu_effectthat depends on channel spend has not opted in viaincrementality_spec(). Also raised if the accounted nodes (channel_contributionplus the resolved effects) do not reproduce the full move in the linear predictor, which means some path from spend to the response is unattributed; seeassert_increment_is_complete().
- Warns:
UserWarningIf a spend counterfactual’s reach could not be measured because no interior date could be probed, the evaluation falls back to evaluating every period on the full date axis instead of a window, which is correct but slower, and the completeness check above is skipped for lack of anything to compare it against. See
measure().
See also
compute_joint_incremental_contributionAll channels perturbed together, for a total rather than a split.
References
Google MMM Paper: https://storage.googleapis.com/gweb-research2023-media/pubtools/3806.pdf
Examples
Compute quarterly incremental contributions:
incremental = mmm.incrementality.compute_incremental_contribution( frequency="quarterly", start_date="2024-01-01", end_date="2024-12-31", )
Mean contribution per channel per quarter:
incremental.mean(dim=["chain", "draw"])
Total annual contribution (all_time):
annual = mmm.incrementality.compute_incremental_contribution( frequency="all_time", start_date="2024-01-01", end_date="2024-12-31", )
Quarterly marginal incrementality (1 % spend increase):
marginal = mmm.incrementality.compute_incremental_contribution( frequency="quarterly", counterfactual_spend_factor=1.01, )