Guide: Performing Budget Optimisation

This guide explains how to use the budget optimisation features within the ammm library.

Overview

Budget optimisation aims to find the allocation of a total marketing budget across different channels that is predicted to maximise the overall contribution (e.g., sales, conversions), based on the fitted MMM and the estimated response curves for each channel. The core principle behind this is to allocate spend to channels where the marginal ROI (mROI)—the return from the next dollar spent—is highest, until the mROIs equalize across channels or budget constraints are met.

Prerequisites

  1. Fitted Model: You need a fitted DelayedSaturatedMMM model instance. The model fitting process estimates the necessary response curve parameters.

  2. Understanding of the Process: The optimization is a two-step process:

    • First, a parametric curve (e.g., sigmoid) is fitted to the channel contributions from the Bayesian model.

    • Second, a numerical optimizer finds the budget allocation that maximizes the sum of these curves, given a total budget and constraints.

Runnable Example

This example demonstrates how to run the budget optimizer on a fitted model. It assumes you have already run the model as shown in the Quickstart Guide.

import pandas as pd
from abacus_mmm.src.core.mmm_model import DelayedSaturatedMMM

# --- Step 1: Fit the Model (same as in the Quickstart) ---
# (This part is assumed to be done. For a runnable script, you would include
# the data loading and model fitting code from the quickstart here.)

# Let's create a placeholder fitted model for this example to run.
# In your actual use case, you would use your real fitted model.
print("Loading data and fitting a dummy model for the optimization example...")
input_data_df = pd.read_excel("demo/demo_data.xlsx", sheet_name="data_mmm")
target_column = "revenue"
X = input_data_df.drop(columns=[target_column])
y = input_data_df[target_column]
mmm = DelayedSaturatedMMM(
    date_column="date",
    channel_columns=["channel_1", "channel_2", "channel_3", "channel_4", "channel_5"],
    control_columns=["promo", "sin_month", "cos_month"],
    adstock_max_lag=4,
    yearly_seasonality=2,
)
idata = mmm.fit(X, y, draws=500, tune=500)
print("Model fitting complete.")
# --- End of Model Fitting Step ---


# --- Step 2: Run the Budget Optimizer ---
print("\nRunning the budget optimizer...")

# Define the total budget you want to allocate.
# For this example, we'll use the sum of the historical spend.
total_budget = X[mmm.channel_columns].sum().sum()
print(f"Total budget to allocate: ${total_budget:,.2f}")

# Define the method for the response curve. 'sigmoid' is a common choice.
response_curve_method = "sigmoid"

# 1. Estimate the parameters of the response curves from the fitted model.
parameters = mmm.compute_channel_curve_optimization_parameters_original_scale(
    method=response_curve_method
)
print("\nEstimated Response Curve Parameters (alpha, lam):")
for channel, params in parameters.items():
    print(f"  {channel}: alpha={params[0]:.2f}, lam={params[1]:.6f}")

# 2. (Optional) Define bounds for the budget of each channel.
# For example, you might require a minimum spend on a certain channel.
budget_bounds = {
    "channel_1": (1000, None),  # Min spend of 1000, no max
    "channel_3": (None, 50000), # No min, max spend of 50000
}
print(f"\nBudget bounds: {budget_bounds}")

# 3. Run the budget allocator.
optimised_results_df = mmm.optimize_channel_budget_for_maximum_contribution(
    method=response_curve_method,
    total_budget=total_budget,
    budget_bounds=budget_bounds,
    parameters=parameters
)

# 4. Analyze the results.
print("\nOptimization Results:")
print(optimised_results_df)

# The output DataFrame shows the estimated contribution and the optimal budget
# for each channel, as well as the total.

Interpreting the Results

The output of the example script will be a DataFrame that looks something like this (values will vary):

             estimated_contribution  optimal_budget
channel_1              25000.00        30000.00
channel_2              35000.00        40000.00
...
total                 150000.00       160000.00
  • optimal_budget: This column shows the suggested budget allocation after optimisation. Compare this to the historical spend to see the recommended shifts.

  • estimated_contribution: This column shows the estimated contribution from each channel based on the optimal budget.

Key Considerations

  • Approximation: The optimisation relies on the accuracy of the fitted parametric response curves (e.g., sigmoid). These are approximations of the true, non-parametric response curves from the Bayesian model.

  • Total Budget: The optimisation allocates a fixed total budget. It suggests how to best spend that amount, not whether to increase or decrease the total budget.

  • Constraints: If you provide budget_bounds, the optimizer will respect them. This is useful for incorporating business constraints.

  • Model Accuracy: The optimisation is only as good as the underlying MMM fit. Ensure your main model diagnostics are satisfactory before trusting the optimization results.