Abacus Core Mixins: Optimization (optimization.py)

This module provides the OptimizationMixin class, designed to be inherited by Marketing Mix Model (MMM) classes in Abacus. It offers experimental methods for analysing channel response curves and optimising budget allocation across channels to maximise expected contribution, based on the fitted model parameters and estimated saturation functions.

OptimizationMixin Class

This mixin assumes the inheriting class provides attributes and methods like:

  • X: The original input DataFrame used for training.

  • channel_columns: List of channel names.

  • compute_channel_contribution_original_scale(): A method (likely from ContributionMixin) that returns channel contributions in the original target scale.

Methods

compute_channel_curve_optimization_parameters_original_scale

def compute_channel_curve_optimization_parameters_original_scale(
    self, method: str = "sigmoid"
) -> dict[str, tuple[float, float]]:

(Experimental) Estimates the parameters of a specified saturating function (Sigmoid or Michaelis-Menten) for each channel’s contribution curve.

This method fits either a Sigmoid or Michaelis-Menten function to the relationship between the historical spend (self.X) and the mean posterior channel contribution (obtained via compute_channel_contribution_original_scale) for each channel. The estimated parameters represent the saturation characteristics of each channel.

Parameters:

  • method (str, optional): The type of saturation function to fit. Options are "sigmoid" (default) or "michaelis-menten".

Returns:

  • dict[str, tuple[float, float]]: A dictionary where keys are channel names. Values are tuples containing the estimated parameters (alpha, lam) for the chosen saturation function:

    • For Sigmoid: alpha is the limit (max contribution), lam is the shape parameter.

    • For Michaelis-Menten: alpha is the limit (max contribution), lam is the spend level at which contribution is half of alpha.

Raises:

  • ValueError: If method is not "sigmoid" or "michaelis-menten".

  • RuntimeError: If the model hasn’t been fitted with X data yet (required for estimating parameters).

Notes:

  • This method is marked as experimental. The accuracy of the estimated parameters depends on how well the chosen function fits the actual spend-contribution relationship derived from the model.

  • It uses helper functions (ut.estimate_sigmoid_parameters, ut.estimate_menten_parameters) internally.


optimize_channel_budget_for_maximum_contribution

def optimize_channel_budget_for_maximum_contribution(
    self,
    method: str,
    total_budget: int | float,
    budget_bounds: Optional[dict[str, tuple[float, float]]] = None,
    *,
    parameters: dict[str, tuple[float, float]],
) -> pd.DataFrame:

(Experimental) Optimises the allocation of a total budget across channels to maximise the total expected contribution, based on pre-estimated saturation parameters.

This method uses a numerical optimisation routine (abacus.core.opt.budget_allocator) to find the distribution of total_budget among the specified channels that yields the highest sum of contributions, given that each channel’s contribution follows the saturation function defined by method and its corresponding parameters. Optional bounds can be set for individual channel budgets.

Parameters:

  • method (str): The saturation function assumed for the channels. Must match the method used to generate parameters. Options: "sigmoid" or "michaelis-menten".

  • total_budget (int | float): The total budget available for allocation.

  • budget_bounds (dict[str, tuple[float, float]], optional): A dictionary specifying minimum and maximum allowed budget for individual channels. Keys are channel names, values are (min_budget, max_budget) tuples. If None, bounds are effectively [0, infinity).

  • parameters (dict[str, tuple[float, float]], keyword-only): Required. A dictionary providing the saturation parameters (alpha, lam) for each channel, typically obtained from compute_channel_curve_optimization_parameters_original_scale.

Returns:

  • pd.DataFrame: A DataFrame detailing the optimal budget allocation per channel and the corresponding expected contribution.

Raises:

  • TypeError: If budget_bounds is provided but is not a dictionary.

  • ValueError: If total_budget is not numeric, or if the required keyword-only argument parameters is missing or empty.

Notes:

  • This method is marked as experimental. Its effectiveness depends on the accuracy of the parameters provided.

  • It relies on the external function abacus.core.opt.budget_allocator to perform the optimisation.


(Private helper method _estimate_budget_contribution_fit is used internally for estimating contribution bounds, likely for plotting purposes not directly exposed in this mixin.)