Budget optimization scenarios

This document describes fixed and flexible budget scenarios and gives example code to help you understand how to customize your budget optimization.

Fixed budget: Maximize ROI or incremental KPI

In this scenario for a given budget, you can find the optimal allocation across channels and tactics to maximize the return on investment (ROI). If kpi_type is set to non-revenue and revenue_per_kpi is unknown, the fixed budget scenario finds the optimal allocation to maximize the incremental KPI.

This scenario represents the default settings and runs if you call the optimize() method from the BudgetOptimizer class without any additional arguments. There is also the option to modify the fixed budget scenario by altering the optimization time window, inputting distinct budget and pre-optimized media spend allocations, and indicating the lower and upper limits for media expenditures.

Example code

The following code example shows a customized fixed budget scenario:

budget_optimizer = optimizer.BudgetOptimizer(meridian)

pct_of_spend = {
    'Channel0': 0.2,
    'Channel1': 0.2,
    'Channel2': 0.2,
    'Channel3': 0.1,
    'Channel4': 0.2,
    'Channel5': 0.1,
}
spend_constraint = {
    'Channel0': 0.3,
    'Channel1': 0.2,
    'Channel2': 0.3,
    'Channel3': 0.3,
    'Channel4': 0.3,
    'Channel5': 0.2,
}

build_channel_args = meridian.input_data.get_paid_channels_argument_builder()

optimization_results = budget_optimizer.optimize(
      selected_times=('2023-01-16', '2024-01-15'),
      budget=70_000_000,
      pct_of_spend=build_channel_args(**pct_of_spend),
      spend_constraint_lower=build_channel_args(**spend_constraint),
      spend_constraint_upper=build_channel_args(**spend_constraint),
)

Key parameters

  • selected_times: Tuple containing the start and end time dimensions for the duration to run the optimization on. Time dimensions should align with the Meridian time dimensions. By default, all time periods are used.

    Example

    To modify the date range:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    opt = budget_optimizer.optimize(selected_times=('2023-01-16','2024-01-15')) # date range from week 100 to 200
    
  • budget: Number indicating the total budget for the fixed budget scenario. Defaults to the historical budget.

    Example

    To change the budget to 70_000_000:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    opt = budget_optimizer.optimize(budget=70_000_000)
    
  • pct_of_spend: Numeric list of size n_paid_channels containing the percentage allocations for channel spend. 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.

    Example

    To specify the allocation:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    opt = budget_optimizer.optimize(
        pct_of_spend=build_channel_args(
            Channel0=0.2,
            Channel1=0.2,
            Channel2=0.2,
            Channel4=0.1,
            Channel5=0.2,
            Channel6=0.1,
        ), # non-optimized allocation is 20% to Channel0, 20% to Channel1 etc
    )
    
  • spend_constraint_lower: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the lower bound of media-level spend. The lower bound of media-level spend is ((1 - spend_constraint_lower) * budget * allocation). The value entered must be between 0-1. The default value is 0.3 (meaning 70% of media spend) for a fixed budget and 1 (meaning 0% of media spend) for a flexible budget.

    Example

    To change the lower spend constraint:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    build_channel_args = build_channel_args.with_default_value(0.3)
    opt = budget_optimizer.optimize(
        spend_constraint_lower=build_channel_args(
            Channel1=0.2,
            Channel5=0.2,
        ), # change the lower bound to 70%, 80%, 70%, 70%, 70% and 80% of media spends
    )
    
  • spend_constraint_upper: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the upper bound of media-level spend. The upper bound of media-level spend is (1 + spend_constraint_upper) * budget * allocation). The default value is 0.3 (meaning 130% of media spend) for a fixed budget and 1 (meaning 200% of media spend) for a flexible budget.

    Example

    To change the upper spend constraint:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    build_channel_args = build_channel_args.with_default_value(0.3)
    opt = budget_optimizer.optimize(
        spend_constraint_upper=build_channel_args(
            Channel1=0.2,
            Channel5=0.2,
        ), # change the upper bound to 130%, 120%, 130%, 130%, 130% and 120% of media spends
    )
    

Flexible budget: Target minimum ROI

In this scenario for a flexible budget, you input a specific minimum ROI target. This option lets you find out what is the maximum budget you can spend without any budget limit until the overall ROI hits X at the total marketing level. Meridian provides a recommended maximum budget, as well as an optimal allocation of the budget across media channels, to achieve that ROI target. Notably, the target ROI constraint is applied at the total marketing ROI level and not at channel level.

Example code

The following code example shows a customized flexible budget scenario with a target minimum ROI:

budget_optimizer = optimizer.BudgetOptimizer(meridian)
optimization_results = budget_optimizer.optimize(
      selected_times=('2023-01-16','2024-01-15'),
      fixed_budget=False,
      spend_constraint_lower=0.5,
      spend_constraint_upper=0.5,
      target_roi=1,
)

Key parameters

  • fixed_budget: Boolean indicating whether it is a fixed budget optimization or flexible budget optimization. Defaults to True, indicating a fixed budget. If False, you must also specify either target_roi or target_mroi.

  • target_roi: Float indicating the target ROI constraint. Only used for flexible budget scenarios. The budget is constrained to when the ROI of the total media spend hits target_roi.

    Example

    To switch to flexible budget scenario and set the target ROI to 1:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    opt = budget_optimizer.optimize(fixed_budget=False, target_roi=1)
    
  • selected_times: Tuple containing the start and end time dimensions for the duration to run the optimization on. Time dimensions should align with the Meridian time dimensions. By default, all time periods are used.

  • pct_of_spend: Numeric list of size n_paid_channels containing the percentage allocation for the media spend. 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.

  • spend_constraint_lower: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the lower bound of media-level spend. The lower bound of media-level spend is ((1 - spend_constraint_lower) * budget * allocation). The value entered must be between 0-1. The default value is 0.3 (meaning 70% of media spend) for a fixed budget and 1 (meaning 0% of media spend) for a flexible budget.

  • spend_constraint_upper: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the upper bound of media-level spend. The upper bound of media-level spend is (1 + spend_constraint_upper) * budget * allocation). The default value is 0.3 (meaning 130% of media spend) for a fixed budget and 1 (meaning 200% of media spend) for a flexible budget.

Flexible budget: Target marginal ROI

In this scenario for a flexible budget, you input a marginal ROI target for incremental spend. This option tells what is the maximum budget you can spend without any budget limit until the marginal ROI of each media channel hits the target marginal ROI. It's still possible that the marginal ROI of some channels won't be equal to the target marginal ROI due to the feasible range of media spend. However, the marginal ROI will effectively shrink toward the target marginal ROI. Notably, the target marginal ROI is applied at channel level and not at the total marketing level.

Example code

The following code examples shows a customized flexible budget scenario with a target marginal ROI:

budget_optimizer = optimizer.BudgetOptimizer(meridian)
optimization_results = budget_optimizer.optimize(
      selected_times=('2023-01-16','2024-01-15'),
      fixed_budget=False,
      spend_constraint_lower=0.5,
      spend_constraint_upper=0.5,
      target_mroi=1,
)

Key parameters

  • fixed_budget: Boolean indicating whether it is a fixed budget optimization or flexible budget optimization. Defaults to True, indicating a fixed budget. If False, you must specify either target_roi or target_mroi.

  • target_mroi: Float indicating the target marginal ROI constraint. Only used for flexible budget scenarios. The budget is constrained to when the marginal ROI of the total media spend hits target_mroi.

    Example

    To switch to flexible budget scenario and set the target marginal ROI to 1:

    budget_optimizer = optimizer.BudgetOptimizer(meridian)
    opt = budget_optimizer.optimize(fixed_budget=False, target_mroi=1)
    
  • selected_times: Tuple containing the start and end time dimensions for the duration to run the optimization on. Time dimensions should align with the Meridian time dimensions. By default, all time periods are used.

  • pct_of_spend: Numeric list of size n_paid_channels containing the percentage allocation for the media spend. 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.

  • spend_constraint_lower: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the lower bound of media-level spend. The lower bound of media-level spend is ((1 - spend_constraint_lower) * budget * allocation). The value entered must be between 0-1. The default value is 0.3 (meaning 70% of media spend) for a fixed budget and 1 (meaning 0% of media spend) for a flexible budget.

  • spend_constraint_upper: Numeric list of length n_paid_channels to use different spend constraints for different channels, or a single float value as the same spend constraint for all media channels. If given as a channel-indexed array, the order must match (InputData.media + InputData.reach). Indicates the upper bound of media-level spend. The upper bound of media-level spend is (1 + spend_constraint_upper) * budget * allocation). The default value is 0.3 (meaning 130% of media spend) for a fixed budget and 1 (meaning 200% of media spend) for a flexible budget.