diff_diff.to_meridian_roi_prior#
- diff_diff.to_meridian_roi_prior(*, incremental_outcome=None, incremental_outcome_se=None, aggregation_result=None, scale=None, spend, parameter='roi_m', se_widening=1.0)[source]
Build a Meridian lognormal ROI/mROI prior from scoped experiment result(s).
Per experiment
roi = incremental_outcome / spendwith standard deviationincremental_outcome_se / spend * se_widening. Multiple experiments for the same channel are pooled with spend weights - the spend-weighted average ROI the Meridian FAQ suggests for multiple experiments (citing section 3.4 of Google’s MMM calibration whitepaper):roi_mean = sum(w_i * roi_i)withw_i = spend_i / sum(spend). The pooledroi_sd = sqrt(sum((w_i * sd_i)^2))is this library’s uncertainty propagation for that average (treats experiments as independent - widen viase_wideningwhen experiments share controls or windows). The pooled mean/sd map to lognormal(mu, sigma)via Google’s closed form.Two input routes. Either the caller supplies the scoped estimand explicitly -
incremental_outcomeis the total incremental outcome the experiment measured, matching the estimand of the target prior: forparameter="roi_m"that is the outcome attributable to the channel’s spend against a zero-spend counterfactual (a full holdout), divided here byspend= the channel’s total spend over the window; forparameter="mroi_m"it is the marginal outcome of the spend change, divided by that spend change - or passesaggregation_result=: withscale=for a ‘simple’/’group’ container (this function derivingincremental_outcome = att * scaleandincremental_outcome_se = se * scaleper container row), or aresults.aggregate('total')container alone (a single already-scaled total row; NO scale accepted). Rescaling happens ONLY under that explicit contract; sign, estimand match, and population remain the caller’s responsibility.- Parameters:
incremental_outcome (float or sequence of float, optional) – Total incremental outcome per experiment (the estimand matching
parameter), in the same currency/units as the MMM. Must be finite; the pooled ROI must be positive (lognormal support). Required together withincremental_outcome_seunlessaggregation_resultis given (the two routes are mutually exclusive).incremental_outcome_se (float or sequence of float, optional) – Standard error of
incremental_outcome(finite, positive), computed for the caller’s aggregation. Required together withincremental_outcomeunlessaggregation_resultis given.aggregation_result (AggregationResult, optional) – The pinned container returned by
results.aggregate('simple')(one experiment),results.aggregate('group')(one experiment per cohort, feeding the spend-weighted pooling below), orresults.aggregate('total')(one already-scaled total experiment). Mutually exclusive withincremental_outcome/incremental_outcome_se; requiresscalefor ‘simple’/’group’ containers and FORBIDS it for ‘total’. Rows are consumed inaggregation_result.to_dataframe()order - the ordersummary()prints - and per-row sequence kwargs (scale,spend) align to that order. Raw results objects andEventStudyResultsare rejected; this function never callsaggregate()itself. Group-container cautions: (1) cohort rows come from ONE fit sharing controls and windows, and the container carries only per-row SEs (the joint covariance’s off-diagonals are discarded upstream), so the independence pooling below can misstate the pooled uncertainty - it is anti-conservative when the net weighted covariance is positive (shared controls typically induce positive correlation), and the direction cannot be determined from marginal SEs alone;se_widening > 1is a conservative heuristic, not an exact correction; (2) a scalarspendreplicates per row, makingtotal_spend = n_rows * spendand the pooled prior the arithmetic MEAN of cohort ROIs,sum(effect_i) / (n_rows * spend)- an n-fold understatement of channel ROI if the scalar was meant as the channel’s TOTAL spend; pass per-cohort spends.scale (float, sequence of float, or "auto", optional) – Converts each container row’s per-observation ATT to that experiment’s total incremental outcome:
incremental_outcome = att * scale,incremental_outcome_se = se * scale. Required with a ‘simple’/’group’aggregation_result; forbidden with a ‘total’ container, whose row is already the estimator-owned total (finite, positive; scalar or one value per row into_dataframe()order; e.g. treated units x treated periods for an unweighted additive fit - an example, not a universal formula).scale="auto"derivesscalefrom the container’s per-rownand is accepted ONLY for ImputationDiD and TwoStageDiD fits, whosenis the treated observations the ATT averages over. Passing"auto"acknowledges three assumptions the container cannot verify: additive-level outcome (not log/rate/share), an unweighted fit (survey-weighted fits pair a weightedattwith a raw-countn), and fully identified effects (ImputationDiD reports the raw treated-observation count at BOTH levels while averaging finite effects only; TwoStageDiD does so only at'simple'- its'group'counts are post-filter; the affected fits warn at fit time on the degenerate branch, and"auto"there overcounts). An overflowingatt * scalesurfaces via this function’s ordinary finiteness errors -incremental_outcomeis validated beforeincremental_outcome_se, so a row where both overflow reportsincremental_outcome.spend (float or sequence of float) – Spend the incremental outcome is divided by (finite, positive): the channel’s total spend over the window for
roi_m, or the spend change formroi_m.parameter ({"roi_m", "mroi_m"}, default "roi_m") – Which Meridian prior the estimate informs.
roi_mis the return on the channel’s full spend (a full-holdout / zero-spend estimand);mroi_mis the marginal return of a spend change. Under saturation these differ, so the caller declares which theirincremental_outcomemeasures; the returned prior and.to_code()target that parameter with its own Meridian default for non-experiment channels.se_widening (float, default 1.0) – Multiplier on each experiment’s ROI standard deviation (finite, positive). Values above 1 encode skepticism about experiment-to-MMM transferability.
- Returns:
Pooled
roi_mean/roi_sd, the lognormalmu/sigma, the targetparameter, per-experiment detail, plus.to_dict()and the channel-scoped.to_code()snippet helper.- Return type:
- Raises:
ValueError – On an invalid
parameter, non-positivespend/se/se_widening, non-finite inputs, broadcasting mismatches, empty inputs, a non-positive pooled ROI mean (lognormal support), or non-finite scaled/pooled results.