create_zero_dataset#

pymc_marketing.mmm.utils.create_zero_dataset(model, start_date, end_date, channel_xr=None, include_carryover=True, preserve_observed=False, carry_in_periods=0, carryover_periods=None)[source]#

Create an xr.Dataset for future prediction, with zero fills.

Creates a dataset with dates from start_date to end_date and all model dimensions, filling channel and control variables with zeros (or with values from channel_xr if provided), under the canonical underscore names (_channel, _control). Date-varying variables that the model’s mu_effects read are zero-filled too, under their own names, so that MuEffect.set_data has something to set for a window other than the training one.

Parameters:
model

Fitted MMM instance. Must have xarray_dataset, date_column, channel_columns, control_columns, dims and adstock attributes. mu_effects is read when present.

start_date, end_date

Date range for the prediction period.

channel_xr

Optional per-dimension channel values. Data variables must be a subset of model.channel_columns. Dimensions must be a subset of model.dims and must not include the date dimension. Values are broadcast across every date in the generated range.

include_carryover

Whether to extend the date range past end_date by adstock.l_max periods, so that spend inside the window is scored with the carry-over it produces after it. The extension is trailing; nothing is prepended, and the window therefore starts from a cold adstock state.

carry_in_periods

Number of leading dates to prepend, taken from the training index so they are real observed dates. _channel holds its observed spend on them, so the adstock does not start cold. Clips itself when the window starts near the beginning of training. The history has to run up to the window at the training frequency: when it does not (a window opening long after training ends), a UserWarning is issued and the window starts cold, because year-old spend is not last period’s.

carryover_periods

Periods to extend past end_date when include_carryover, defaulting to model.adstock.l_max. Pass the effective carryover when an effect chains a further adstock behind the model’s own, or its tail is truncated.

preserve_observed

Whether every non-decision variable – controls, and the variables the model’s mu_effects read – takes its observed value on each date the training data covers, falling back to zero only on dates it does not. _channel is unaffected: it is the decision variable and stays at zero (or at channel_xr). The default False zero-fills everything, which is the function’s contract (a window with no committed activity) and keeps _control exactly as existing callers get it. create_optimization_model() opts in explicitly; the deprecated sample_response_distribution() relies on the default, since it scores an allocation against a zeroed baseline.

Returns:
xr.Dataset

Dataset with _channel (and optionally _control) variables, indexed by ("date", *dims, "channel"), plus one variable per date-varying name the model’s mu_effects read: zero-filled by default, observed wherever the training data reaches when preserve_observed is set.

Notes

By default the effect variables are zeros, which is what a future window with no committed activity means. In-sample that is a change of scenario rather than a reconstruction of history: an exogenous series the effect reads – a category-demand index, a committed budget – comes back as zero rather than at its fitted value, so the response no longer matches the posterior it was fitted to. preserve_observed=True is the fix, and needs no knowledge of which window it was handed: taking the observed value wherever the training data reaches reproduces history in-sample and decays to zeros on genuinely future dates. This is not adstock carry-in, which is a leading block of real spend before the window; that is include_last_observations.

For a scenario that is neither – a planned promotional calendar, a committed lower-funnel budget – build the model with create_optimization_model(), pm.set_data those variables on it, and hand that model to BudgetOptimizer directly. That model’s date axis is three blocks, carry_in + decisions + carry_over, each flank effective_carryover_lags() wide, so pass carry_in_periods=mmm.effective_carryover_lags() and adstock_periods=mmm.effective_carryover_lags() along with num_periods (BudgetOptimizer checks that the three add up to the axis and says so if they do not).

An effect variable whose coordinate labels do not cover the shared index (a channel-dimensioned variable carrying a subset of the channels, say) is NaN-filled the moment xarray aligns it, and is refused here by name rather than written into the model as NaN.