Skip to content

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.

By Gordon Geraghty·MIT Licence·Updated: 24 September 2026·ADVANCED
01 Prerequisites & Architecture
Stage 01 Architecture

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.

Difficulty:Advanced Architecture
Time:30–45 mins
Required Access & Permissions:
Historical Marketing Spend Data Access (104+ weeks recommended)Python 3.9+ Environment
STEP 01CSV / Data Warehouse Table
Weekly Channel SpendOmnichannel marketing investment dataset
STEP 02Adstock Feature Engineering
Adstock TransformationGeometric memory decay: S(t) = Spend(t) + α·S(t-1)
STEP 03Non-Linear Saturation Transform
Hill Saturation CurveDiminishing marginal returns modeling
STEP 04Scikit-Learn / SciPy Pipeline
Ridge / Bayesian FitRevenue decomposition & optimal budget allocation
02 Interactive Configurator

Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline Configurator

Worked Example · Deterministic Calculation

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

ParameterValueContext & 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.13x

3 Computed Output Metrics

Projected Attributed Revenue$170,116.00USD / monthTotal modeled monthly revenue taking non-linear saturation curves into account
Blended Portfolio ROI2.13xBlended ReturnBlended revenue generated per marketing dollar invested ($170,116 / $80,000)
Computed MetricResultInterpretation & Threshold
Total Monthly Spend$80,000.00 USD / monthTotal blended media budget across all 4 channels
Projected Attributed Revenue$170,116.00 USD / monthTotal modeled monthly revenue taking non-linear saturation curves into account
Blended Portfolio ROI2.13x Blended ReturnBlended revenue generated per marketing dollar invested ($170,116 / $80,000)
Search Marginal ROI2.94x Marginal ReturnEstimated marginal return on the next dollar allocated to Google Search
Meta Marginal ROI2.19x Marginal ReturnEstimated marginal return on the next dollar allocated to Meta Paid Social

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.

INSTRUMENT BOUNDARIES

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 Engineering

Architect 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.

Load MMM Topology:

Model Specifications

Media Channels & Diminishing Returns Curve

Google Paid SearchHalf-Life: 0.6w | Saturation: 57%
Meta Paid SocialHalf-Life: 1w | Saturation: 56%
YouTube & CTV VideoHalf-Life: 2.4w | Saturation: 46%
TikTok AdsHalf-Life: 0.7w | Saturation: 43%

Generated Pipeline Specification

Blended Portfolio ROI
2.13x
Across all modeled channels
Projected Revenue
$170,697.00
From $80,000.00 spend
"""
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.")
Export & Deployment Actions1-click clipboard transfer, shareable URL hash, and local file downloads.

Built by Gordon Geraghty, Head of Performance MediaZero Data Sent to Server
03 Deployment & Export

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

Adstock & Hill Transformation Enginemmm_pipeline.pypython

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 model
04 QA & Verification Guide

MMM 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

✓
Verify Minimum 104 Weeks of Continuous Data

Confirm dataset contains at least 2 full annual cycles to disentangle holiday seasonality from ad effectiveness.

✓
Check Variance Inflation Factor (VIF < 5.0)

Ensure channels scaling spend simultaneously do not cause severe multicollinearity in regression coefficients.

✓
Validate Non-Negative Coefficients

Verify regularization constraints enforce positive marginal contributions for all advertising channels.

Terminal Diagnostic & Debug Commands

Execute MMM Pipeline (CLI)bash

Runs model fit and displays R², MAPE, and channel ROI decomposition table.

python mmm_pipeline.py --input sample_mmm_data.csv --alpha 0.6

Failure Remediation & Troubleshooting

Issue: Negative Channel Coefficients (e.g. Meta ROI = -0.4)

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 Licence

This resource is free, open and un-gated under the MIT Open Source Licence. You are encouraged to use, integrate and cite it with attribution:

Geraghty, G. (2026). Omnichannel Media Mix Modelling (MMM) Lightweight Python Pipeline. Gordon Geraghty Resources Hub. https://gordongeraghty.com/resources/performance-media/lightweight-mmm-pipeline
BibTeX Format
@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.