Budget bounds constrain per-channel (and optionally per-geo) allocation. Bounds are specified as xr.DataArray objects. You can define single-geo bounds, multidimensional (Channel x Geo) bounds, or use the optimizer_xarray_builder helper.
Single-Geo Bounds Structure:
- Dimensions:
["channel", "bound"] - Coordinates:
channel (list of channel names), bound (["lower", "upper"])
Multidimensional Bounds (Channel x Geo) Structure:
- Dimensions:
["channel", "geo", "bound"] - Coordinates:
channel, geo, bound
import numpy as np
import xarray as xr
from pymc_marketing.mmm.budget_optimizer import optimizer_xarray_builder
# Option 1: Manual Single-Geo Bounds
budget_bounds = xr.DataArray(
data=np.array([
[0.5, 1.5], # tv: 50%-150% of equal share
[0.3, 2.0], # radio: 30%-200%
[0.5, 1.5], # social
]) * equal_share_per_channel,
dims=["channel", "bound"],
coords={
"channel": channel_columns,
"bound": ["lower", "upper"],
},
)
# Option 2: Multidimensional Bounds (Channel x Geo)
budget_bounds = xr.DataArray(
data=np.stack([
np.full((n_channels, n_geos), 0.0), # lower bounds
np.full((n_channels, n_geos), max_budget), # upper bounds
], axis=-1),
dims=["channel", "geo", "bound"],
coords={
"channel": channel_columns,
"geo": geos,
"bound": ["lower", "upper"],
},
)
# Option 3: Using the Builder Helper
budget_bounds = optimizer_xarray_builder(
value=np.array([
[0.5 * equal_share, 1.5 * equal_share], # tv
[0.3 * equal_share, 2.0 * equal_share], # radio
[0.5 * equal_share, 1.5 * equal_share], # social
]),
channel=channel_columns,
bound=["lower", "upper"],
)