Incrementality.compute_joint_incremental_contribution#

Incrementality.compute_joint_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 the incremental contribution of all channels together.

Perturbs every channel in the same counterfactual and returns one number per period, rather than perturbing channels one at a time.

This is a different estimand from summing compute_incremental_contribution() over channel, and the difference is not an error in either of them. Per-channel increments are unilateral: each answers “what changes if this channel’s spend is scaled by α, holding the others at their actual spend” – at the default α = 0, “what would we lose without this channel”. Whenever the response is not additive in the channels – under link="log", or when a mu_effect mixes channels before they reach the response – the two disagree by the interaction between the channels. Under link="identity" with no channel-dependent effects they coincide exactly.

The direction of the disagreement is not fixed. With strictly positive contributions at α = 0 the unilateral numbers sum to more than the joint, since interaction mass is counted by every channel that touches it; with contributions of mixed sign, or with α > 1, the sum can fall short instead. Either way it is not a total.

Report this number when the question is “how much of the target does media drive in total”, and the per-channel ones when the question is “which channel should I cut”. Adding the per-channel numbers up answers neither.

Parameters:
frequency{“original”, “weekly”, “monthly”, “quarterly”, “yearly”, “all_time”}

Time aggregation frequency, as in compute_incremental_contribution().

start_datestr or pd.Timestamp, optional

Start date for evaluation window. If None, uses start of fitted data.

end_datestr or pd.Timestamp, optional

End date for evaluation window. If None, uses end of fitted data.

include_carryoverbool, default=True

Include adstock carryover effects.

num_samplesint or None, optional

Number of posterior samples to use.

random_stateRandomState or Generator or None, optional

Random state for reproducible subsampling.

counterfactual_spend_factorfloat, default=0.0

Multiplicative factor applied to every channel’s spend.

central_tendency{“median”, “mean”}, default=”median”

Central tendency of the predictions being differenced.

Returns:
xr.DataArray

Joint incremental contribution in original scale, with dimensions (chain, draw, date, *custom_dims), or without date when frequency == "all_time". There is no channel dimension: the number is not attributable to a single channel.

See also

compute_incremental_contribution

Per-channel, unilateral increments.

Examples

Total media incrementality, and how far the per-channel numbers are from it:

joint = mmm.incrementality.compute_joint_incremental_contribution(
    frequency="all_time"
)
unilateral = mmm.incrementality.compute_incremental_contribution(
    frequency="all_time"
)
interaction = (
    unilateral.sum("channel").mean(("chain", "draw"))
    / joint.mean(("chain", "draw"))
    - 1
)