Runs and outputs budget optimization scenarios on your model.
meridian
.
analysis
.
optimizer
.
BudgetOptimizer
(
meridian
:
meridian
.
model
.
model
.
Meridian
)
Finds the optimal budget allocation that maximizes outcome based on various scenarios where the budget, data, and constraints can be customized. The results can be viewed as plots and as an HTML summary output page.
Methods
create_optimization_grid
create_optimization_grid
(
new_data
:
(
xr
.
Dataset
|
None
)
=
None
,
use_posterior
:
bool
=
True
,
selected_times
:
(
tuple
[
str
|
None
,
str
|
None
]
|
None
)
=
None
,
start_date
:
meridian
.
data
.
time_coordinates
.
Date
=
None
,
end_date
:
meridian
.
data
.
time_coordinates
.
Date
=
None
,
budget
:
(
float
|
None
)
=
None
,
pct_of_spend
:
(
Sequence
[
float
]
|
None
)
=
None
,
spend_constraint_lower
:
_SpendConstraint
=
c
.
SPEND_CONSTRAINT_DEFAULT
,
spend_constraint_upper
:
_SpendConstraint
=
c
.
SPEND_CONSTRAINT_DEFAULT
,
gtol
:
float
=
0.0001
,
use_optimal_frequency
:
bool
=
True
,
use_kpi
:
bool
=
False
,
batch_size
:
int
=
c
.
DEFAULT_BATCH_SIZE
)
->
meridian
.
analysis
.
optimizer
.
OptimizationGrid
Creates a OptimizationGrid for optimization.
If start_date
or end_date
is specified, then the default values are
inferred based on the subset of time periods specified. Both start and end
time selectors should align with the Meridian time dimension coordinates in
the underlying model if optimizing the original data. If new_data
is
provided with a different number of time periods than in InputData
, then
the start and end time coordinates must match the time dimensions in new_data.time
. By default, all times periods are used. Either start or
end time component can be None
to represent the first or the last time
coordinate, respectively.
new_data
DataTensors
container with optional tensors: media
, reach
, frequency
, media_spend
, rf_spend
, revenue_per_kpi
, and time
. If None
, the original tensors from the
Meridian object are used. If new_data
is provided, the grid is created
using the versions of the tensors in new_data
and the original
versions of all the remaining tensors. If any of the tensors in new_data
is provided with a different number of time periods than in InputData
, then all tensors must be provided with the same number of
time periods and the time
tensor must be provided.use_posterior
True
, then the incremental outcome is derived
from the posterior distribution of the model. Otherwise, the prior
distribution is used.selected_times
start_date
and end_date
instead.start_date
None
, i.e. the first time period.end_date
None
, i.e. the last time period.budget
pct_of_spend
n_paid_channels
containing the
percentage allocation for spend for all media and RF channels. The order
must match (InputData.media + InputData.reach)
with values between
0-1, summing to 1. By default, the historical allocation is used. Budget
and allocation are used in conjunction to determine the non-optimized
media-level spend, which is used to calculate the non-optimized
performance metrics (for example, ROI) and construct the feasible range
of media-level spend with the spend constraints. Consider using InputData.get_paid_channels_argument_builder()
to construct this
argument.spend_constraint_lower
n_paid_channels
or float
(same constraint for all channels) indicating the lower bound of
media-level spend. If given as a channel-indexed array, the order must
match (InputData.media + InputData.reach)
. The lower bound of
media-level spend is (1 - spend_constraint_lower) * budget *
allocation)
. The value must be between 0-1. Defaults to 0.3
for fixed
budget and 1
for flexible. Consider using InputData.get_paid_channels_argument_builder()
to construct this
argument.spend_constraint_upper
n_paid_channels
or float
(same constraint for all channels) indicating the upper bound of
media-level spend. If given as a channel-indexed array, the order must
match (InputData.media + InputData.reach)
. The upper bound of
media-level spend is (1 + spend_constraint_upper) * budget *
allocation)
. Defaults to 0.3
for fixed budget and 1
for flexible.
Consider using InputData.get_paid_channels_argument_builder()
to
construct this argument.gtol
10*n
, where n
is
the smallest integer such that (budget - rounded_budget)
is less than
or equal to (budget * gtol)
. gtol
must be less than 1.use_optimal_frequency
use_kpi
True
, then the incremental outcome is derived from
the KPI impact. Otherwise, the incremental outcome is derived from the
revenue impact.batch_size
batch_size
. The calculation will generally be faster with
larger batch_size
values.
create_optimization_tensors
create_optimization_tensors
(
time
:
(
Sequence
[
str
]
|
meridian
.
backend
.
Tensor
),
cpmu
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
media
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
media_spend
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
cprf
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
rf_impressions
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
frequency
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
rf_spend
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
revenue_per_kpi
:
(
meridian
.
backend
.
Tensor
|
None
)
=
None
,
use_optimal_frequency
:
bool
=
True
)
->
meridian
.
analysis
.
analyzer
.
DataTensors
Creates a DataTensors
for optimizations from CPM and flighting data.
CPM is broken down into cost per media unit, cpmu
, for the media channels
and cost per impression (reach * frequency), cprf
, for the reach and
frequency channels.
The flighting pattern can be specified as the spend flighting or the media units flighting pattern at the time or geo and time granularity. If data is passed without a geo dimension, then the values are interpreted as national-level totals. If the model is a geo-level model, then the values are allocated across geos based on the population used in the model.
Below are the different combinations of tensors that can be provided:
For media:
1) media
, cpmu
(media units flighting pattern)
2) media_spend
, cpmu
(spend flighting pattern)
For R&F:
If use_optimal_frequency=True
, frequency
should not be provided.
Frequency input is not required for the optimization, so the new DataTensors
object will be created with frequuency
arbitrarily set to
1 and reach=rf_impressions
.
1) rf_impressions
, cprf
(impressions flighting pattern)
2) rf_spend
, cprf
(spend flighting pattern)
If use_optimal_frequency=False
:
1) rf_impressions
, frequency
, cprf
(impressions flighting pattern)
2) rf_spend
, frequency
, cprf
(spend flighting pattern)
time
cpmu
(n_media_channels),
(T, n_media_channels) or
(n_geos, T, n_media_channels) for any time
dimension
T .
</td>
</tr><tr>
<td>
media </td>
<td>
An optional tensor of media unit values with dimensions
(T,
n_media_channels) or
(n_geos, T, n_media_channels) for any time
dimension
T .
</td>
</tr><tr>
<td>
media_spend </td>
<td>
A tensor of media spend values with dimensions
(T,
n_media_channels) or
(n_geos, T, n_media_channels) for any time
dimension
T .
</td>
</tr><tr>
<td>
cprf </td>
<td>
A tensor of cost per impression (reach * frequency) with dimensions
(n_rf_channels), (T, n_rf_channels)
or (n_geos, T, n_rf_channels)
for any time dimension T
.rf_impressions
(T, n_rf_channels)
or (n_geos, T, n_rf_channels)
for any
time dimension T
.frequency
(n_rf_channels)
, (T, n_rf_channels)
or (n_geos, T, n_rf_channels)
for any time
dimension T
. If use_optimal_frequency=True
, then this tensor should
not be provided and the optimal frequency will be calculated and used.rf_spend
(T, n_rf_channels)
or (n_geos, T, n_rf_channels)
for any time dimension T
.revenue_per_kpi
()
, (T)
, or (n_geos, T)
for any time dimension T
.use_optimal_frequency
True
, the optiaml frequency will be
used in the optimization and a frequency value should not be provided.
In this case, reach=rf_impressions
and frequency=1
(by arbitrary
convention) in the new data. If False
, the frequency value must be
provided.
DataTensors
object with optional tensors media
, reach
, frequency
, media_spend
, rf_spend
, revenue_per_kpi
, and time
. optimize
optimize
(
new_data
:
(
meridian
.
analysis
.
analyzer
.
DataTensors
|
None
)
=
None
,
use_posterior
:
bool
=
True
,
selected_times
:
(
tuple
[
str
|
None
,
str
|
None
]
|
None
)
=
None
,
start_date
:
meridian
.
data
.
time_coordinates
.
Date
=
None
,
end_date
:
meridian
.
data
.
time_coordinates
.
Date
=
None
,
fixed_budget
:
bool
=
True
,
budget
:
(
float
|
None
)
=
None
,
pct_of_spend
:
(
Sequence
[
float
]
|
None
)
=
None
,
spend_constraint_lower
:
(
_SpendConstraint
|
None
)
=
None
,
spend_constraint_upper
:
(
_SpendConstraint
|
None
)
=
None
,
target_roi
:
(
float
|
None
)
=
None
,
target_mroi
:
(
float
|
None
)
=
None
,
gtol
:
float
=
0.0001
,
use_optimal_frequency
:
bool
=
True
,
use_kpi
:
bool
=
False
,
confidence_level
:
float
=
c
.
DEFAULT_CONFIDENCE_LEVEL
,
batch_size
:
int
=
c
.
DEFAULT_BATCH_SIZE
,
optimization_grid
:
(
meridian
.
analysis
.
optimizer
.
OptimizationGrid
|
None
)
=
None
)
->
meridian
.
analysis
.
optimizer
.
OptimizationResults
Finds the optimal budget allocation that maximizes outcome.
Define B to be the historical spend of a channel within selected_geos
and
between start_date
and end_date
. When the optimization assigns a new
budget N to this channel, the historical media units for each geo and time
period are assumed to scale by the ratio N / B. Media units prior to selected_times
are also scaled by N / B. The incremental outcome of each
channel is aggregated over selected_geos
and between start_date
and end_date
.
The incremental outcome includes the (lagged) amount generated between start_date
and end_date
by media executed prior to start_date
, but it
excludes the (lagged) amount generated after end_date
by media executed
between start_date
and end_date
. This definition does not require any
assumptions about media execution levels, media costs, or revenue per kpi
for time periods after end_date
.
These assumptions are equivalent to assuming that for each channel, neither the flighting pattern nor the cost per media unit depend on the overall budget assigned to that channel.
The following optimization parameters are assigned default values based on
the model input data:
1. Flighting pattern. This is the relative allocation of a channel's media
units across geos and time periods. By default, the historical flighting
pattern is used. The default can be overridden by passing new_data.media
. The flighting pattern is held constant during
optimization and does not depend on the overall budget assigned to the
channel.
2. Cost per media unit. By default, the historical spend divided by
historical media units is used. This can optionally vary by geo or time
period or both depending on whether the spend data has geo and time
dimensions. The default can be overridden by passing new_data.spend
.
The cost per media unit is held constant during optimization and does not
depend on the overall budget assigned to the channel.
3. Center of the spend box constraint for each channel. By default, the
historical percentage of spend within selected_geos
and between start_date
and end_date
is used. This can be overridden by passing pct_of_spend
.
4. Total budget to be allocated (for fixed budget scenarios only). By
default, the historical spend within selected_geos
and between start_date
and end_date
is used. This can be overridden by passing budget
.
Passing new_data.media
(or new_data.reach
or new_data.frequency
) will
override both the flighting pattern and cost per media unit. Passing new_data.spend
(or `new_data.rf_spend) will only override the cost per
media unit.
If start_date
or end_date
is specified, these values must be selected
from new_data.time
(if provided) or from Meridian.n_times
(if new_data.time
is not provided). The start_date
and end_date
default to
the first and last time periods, respectively.
new_data
DataTensors
container with optional tensors: media
, reach
, frequency
, media_spend
, rf_spend
, revenue_per_kpi
, and time
. If None
, the original tensors from the
Meridian object are used. If new_data
is provided, the optimization is
run on the versions of the tensors in new_data
and the original
versions of all the remaining tensors. If any of the tensors in new_data
is provided with a different number of time periods than in InputData
, then all tensors must be provided with the same number of
time periods and the time
tensor must be provided. In this case, spend
tensors must be provided with geo
and time
granularity. If use_optimal_frequency
is True
, new_data.frequency
does not need to
be provided and is ignored. The optimal frequency is used instead.use_posterior
True
, then the budget is optimized based on
the posterior distribution of the model. Otherwise, the prior
distribution is used.selected_times
start_date
and end_date
instead.start_date
Meridian.InputData.time
if new_data
is not provided; otherwise it is the first time period of new_data.time
.end_date
Meridian.InputData.time
if new_data
is not provided; otherwise it is the last time period of new_data.time
.fixed_budget
True
. If False
, must
specify either target_roi
or target_mroi
.budget
pct_of_spend
n_paid_channels
containing the
percentage allocation for spend for all media and RF channels. The order
must match (InputData.media + InputData.reach)
with values between
0-1, summing to 1. By default, the historical allocation is used. Budget
and allocation are used in conjunction to determine the non-optimized
media-level spend, which is used to calculate the non-optimized
performance metrics (for example, ROI) and construct the feasible range
of media-level spend with the spend constraints. Consider using InputData.get_paid_channels_argument_builder()
to construct this
argument.spend_constraint_lower
n_paid_channels
or float
(same constraint for all channels) indicating the lower bound of
media-level spend. If given as a channel-indexed array, the order must
match (InputData.media + InputData.reach)
. The lower bound of
media-level spend is (1 - spend_constraint_lower) * budget *
allocation)
. The value must be between 0-1. Defaults to 0.3
for fixed
budget and 1
for flexible. Consider using InputData.get_paid_channels_argument_builder()
to construct this
argument.spend_constraint_upper
n_paid_channels
or float
(same constraint for all channels) indicating the upper bound of
media-level spend. If given as a channel-indexed array, the order must
match (InputData.media + InputData.reach)
. The upper bound of
media-level spend is (1 + spend_constraint_upper) * budget *
allocation)
. Defaults to 0.3
for fixed budget and 1
for flexible.
Consider using InputData.get_paid_channels_argument_builder()
to
construct this argument.target_roi
target_roi
.target_mroi
target_mroi
.gtol
10*n
, where n
is
the smallest integer such that (budget - rounded_budget)
is less than
or equal to (budget * gtol)
. gtol
must be less than 1.use_optimal_frequency
True
, uses optimal_frequency
calculated by
trained Meridian model for optimization. If False
, uses historical
frequency or new_data.frequency
if provided.use_kpi
True
, runs the optimization on KPI. Defaults to revenue.confidence_level
batch_size
batch_size
. The calculation will generally be faster with
larger batch_size
values.optimization_grid
OptimizationGrid
object containing the grid
information. Grid creating is a time consuming part of optimization.
Creating one grid and running various optimizations on it can save time.
If None
or grid doesn't match the optimization arguments, a new grid
will be created.
OptimizationResults
object containing optimized budget allocation
datasets, along with some of the intermediate values used to derive them.