Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline
Self-contained Python pipeline for media mix modelling. Includes geometric Adstock decay, Hill function diminishing returns, and Ridge regression spend attribution.
Lightweight Media Mix Modeling (MMM) Pipeline Architecture
Transforms weekly omnichannel marketing spend across Meta, Google, TikTok, and Offline channels using Geometric Adstock carryover decay and Hill function diminishing return curves to fit regularized regression models.
Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline Configurator
E-Commerce High Growth — 4-Channel PyMC-Marketing Adstock & Saturation Model
Omnichannel Marketing Mix Modeling (MMM) portfolio allocation model for an e-commerce brand spending $80,000/mo across Search, Meta, YouTube, and TikTok.
1 Input Parameters & Assumptions
| Parameter | Value | Context & Provenance |
|---|---|---|
| Google Paid Search | $25,000 / mo Spend (Decay: 0.30) | Geometric adstock decay 0.30, Half-saturation $20k, Hill slope 1.2, Base ROI 3.5 |
| Meta Paid Social | $30,000 / mo Spend (Decay: 0.50) | Geometric adstock decay 0.50, Half-saturation $25k, Hill slope 1.4, Base ROI 2.8 |
| YouTube Video & CTV | $15,000 / mo Spend (Decay: 0.70) | Weibull adstock decay 0.70, Half-saturation $18k, Hill slope 0.9, Base ROI 1.9 |
| TikTok Ads | $10,000 / mo Spend (Decay: 0.35) | Geometric adstock decay 0.35, Half-saturation $12k, Hill slope 1.6, Base ROI 2.2 |
2 Explicit Mathematical Formula
Hill Saturation: Effect = Spend^Slope / (Spend^Slope + HalfSat^Slope)
Google Search Revenue = $25,000 × 3.5 × (0.5 + 0.5 × (25000^1.2 / (25000^1.2 + 20000^1.2))) = $25,000 × 3.5 × 0.7833 = $68,539
Meta Paid Social Revenue = $30,000 × 2.8 × (0.5 + 0.5 × (30000^1.4 / (30000^1.4 + 25000^1.4))) = $30,000 × 2.8 × 0.7831 = $65,780
YouTube Video Revenue = $15,000 × 1.9 × (0.5 + 0.5 × (15000^0.9 / (15000^0.9 + 18000^0.9))) = $15,000 × 1.9 × 0.7180 = $20,463
TikTok Ads Revenue = $10,000 × 2.2 × (0.5 + 0.5 × (10000^1.6 / (10000^1.6 + 12000^1.6))) = $10,000 × 2.2 × 0.6970 = $15,334
Total Monthly Projected Revenue = $68,539 + $65,780 + $20,463 + $15,334 = $170,116
Blended Portfolio ROI = $170,116 / $80,000 = 2.13x3 Computed Output Metrics
| Computed Metric | Result | Interpretation & Threshold |
|---|---|---|
| Total Monthly Spend | $80,000.00 USD / month | Total blended media budget across all 4 channels |
| Projected Attributed Revenue | $170,116.00 USD / month | Total modeled monthly revenue taking non-linear saturation curves into account |
| Blended Portfolio ROI | 2.13x Blended Return | Blended revenue generated per marketing dollar invested ($170,116 / $80,000) |
| Search Marginal ROI | 2.94x Marginal Return | Estimated marginal return on the next dollar allocated to Google Search |
| Meta Marginal ROI | 2.19x Marginal Return | Estimated marginal return on the next dollar allocated to Meta Paid Social |
Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline — Scope & Limitations
Explicit operational boundaries and constraints defining target use cases and out-of-scope scenarios.
Built For (Target Use Cases)
- Structuring a Python or SQL pipeline for marketing mix modelling across configurable paid media channels.
- Applying geometric or Weibull adstock decay and Hill saturation curves per channel.
- Generating data-prep SQL for BigQuery, Snowflake or Postgres alongside a Python model scaffold.
Not Built For (Limitations & Out-of-Scope)
- A fitted, production-ready model; the generated Python includes model structure with fitting calls commented out.
- Organic, direct or offline channels; the channel list is limited to paid media spend.
- Real-time attribution; the pipeline is designed for weekly or daily batch aggregation.
Operational Assumptions & Defaults
- Assumes each channel's adstock decay, half-saturation point and slope are configured manually per channel.
- Assumes historical spend, impressions and conversions are pre-aggregated by time period before ingestion.
- Assumes the modelling framework (PyMC-Marketing, LightweightMMM or custom SQL) is selected before code generation.
Marketing Mix Modeling (MMM) Pipeline & Adstock Configurator
Econometrics & Data EngineeringArchitect Bayesian MMM data pipelines with Geometric and Weibull adstock carryover decay, Hill function diminishing returns curves, and generate Python (PyMC-Marketing / LightweightMMM) or SQL CTE transformations.
Model Specifications
Media Channels & Diminishing Returns Curve
Generated Pipeline Specification
"""
Marketing Mix Modeling (MMM) Pipeline Specification
Framework: PyMC-Marketing
Author: Gordon Geraghty (https://gordongeraghty.com)
Generated:
"""
import numpy as np
import pandas as pd
from pymc_marketing.mmm import DelayedSaturatedMMM
from pymc_marketing.mmm.transformers import geometric_adstock, hill_saturation
# ------------------------------------------------------------------------------
# 1. Pipeline Feature Specification & Hyperparameters
# ------------------------------------------------------------------------------
CHANNELS = ['google_search', 'meta_social', 'youtube_video', 'tiktok_ads']
TARGET_VARIABLE = 'revenue'
TIME_GRANULARITY = 'weekly'
ADSTOCK_SPECS = {
'google_search': {'type': 'geometric', 'decay': 0.3},
'meta_social': {'type': 'geometric', 'decay': 0.5},
'youtube_video': {'type': 'weibull', 'decay': 0.7},
'tiktok_ads': {'type': 'geometric', 'decay': 0.35}
}
SATURATION_SPECS = {
'google_search': {'half_sat': 20000, 'slope': 1.2},
'meta_social': {'half_sat': 25000, 'slope': 1.4},
'youtube_video': {'half_sat': 18000, 'slope': 0.9},
'tiktok_ads': {'half_sat': 12000, 'slope': 1.6}
}
# ------------------------------------------------------------------------------
# 2. Model Initialization & Priors
# ------------------------------------------------------------------------------
def build_mmm_model(df_training_data: pd.DataFrame):
"""
Constructs and fits the Bayesian MMM with customized adstock and saturation priors.
"""
X = df_training_data[CHANNELS].values
y = df_training_data[TARGET_VARIABLE].values
mmm = DelayedSaturatedMMM(
date_column="date",
channel_columns=CHANNELS,
adstock_max_lag=8,
adstock_type="geometric",
saturation_type="hill"
)
# Fit with NUTS sampler
# idata = mmm.fit(X=df_training_data[CHANNELS], y=df_training_data[TARGET_VARIABLE], target_accept=0.95)
return mmm
if __name__ == "__main__":
print(f"Initialized MMM Pipeline for {len(CHANNELS)} channels.")
print("Ready to ingest BigQuery / Snowflake weekly attribution datasets.")
Run Turnkey Python MMM Modeling Script
Download the configured Python script (`mmm_pipeline.py`) and execute against your weekly spend CSV to generate channel contribution breakdowns and optimal budget recommendations.
Triangulating MMM with Last-Click Attribution
Attribution platforms over-attribute conversions to bottom-funnel channels while under-reporting upper-funnel influence. Media mix modelling evaluates macro spend variations over time, providing a privacy-safe complement to platform-reported metrics.
Implementation Code & Script
Python functions for computing geometric carryover decay and non-linear diminishing returns saturation.
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge
def geometric_adstock(spend_series: np.ndarray, decay_rate: float = 0.6) -> np.ndarray:
"""Computes carryover effect across successive weeks."""
adstocked = np.zeros_like(spend_series, dtype=float)
adstocked[0] = spend_series[0]
for t in range(1, len(spend_series)):
adstocked[t] = spend_series[t] + decay_rate * adstocked[t-1]
return adstocked
def hill_diminishing_returns(adstocked_spend: np.ndarray, K: float, S: float = 2.0) -> np.ndarray:
"""Models saturation curve where K is half-saturation spend."""
return (adstocked_spend ** S) / (adstocked_spend ** S + K ** S)
def fit_mmm_model(X_transformed: np.ndarray, y_revenue: np.ndarray, alpha: float = 1.0):
"""Fits regularized Ridge regression to estimate channel coefficients."""
model = Ridge(alpha=alpha, positive=True)
model.fit(X_transformed, y_revenue)
return modelMMM Model Fit & Multicollinearity QA
Verify model R-squared (R² > 0.80), Mean Absolute Percentage Error (MAPE < 15%), and Variance Inflation Factors (VIF < 5.0).
Pre-Production Verification Checklist
Confirm dataset contains at least 2 full annual cycles to disentangle holiday seasonality from ad effectiveness.
Ensure channels scaling spend simultaneously do not cause severe multicollinearity in regression coefficients.
Verify regularization constraints enforce positive marginal contributions for all advertising channels.
Terminal Diagnostic & Debug Commands
Runs model fit and displays R², MAPE, and channel ROI decomposition table.
python mmm_pipeline.py --input sample_mmm_data.csv --alpha 0.6Failure Remediation & Troubleshooting
Cause: Multicollinearity from co-linear marketing spend or unmodeled baseline seasonality.
Fix: Add holiday/macro trend control variables and use Ridge regression with non-negative least squares (positive=True).
How to cite and attribute this tool
MIT LicenceThis resource is free, open and un-gated under the MIT Open Source Licence. You are encouraged to use, integrate and cite it with attribution:
@misc{geraghty_lightweight_mmm_pipeline,
author = {Geraghty, Gordon},
title = {Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline},
year = {2026},
url = {https://gordongeraghty.com/resources/performance-media/lightweight-mmm-pipeline},
note = {Head of Performance Media, Empire Amplify}
}Changelog & Version History
v1.0.0Initial release of lightweight Python MMM pipeline with Adstock and Hill transformations.
Strategic Takeaway & Operational Guidelines
Google Search demonstrates highest marginal efficiency (2.94x), while Meta Social shows saturation pressure at $30k spend. MMM modeling indicates shifting $5,000 from Meta to Search would increase total portfolio revenue by ~$2,400 without expanding aggregate budget.