Marketing Mix Modeling Lab
Estimate what each marketing channel really contributes to sales, including carryover and diminishing returns, and find a better way to split the same budget.
QUICK START: Choose Your Path
OVERVIEW & WHAT THIS TOOL SOLVES
The CMO walks into the executive meeting: "Paid Search delivers $4.10 in sales for every $1 spent, while TV averages only $2.40. Finance wants to shift our entire TV budget into Search." Should they? No: average ROI flatters saturated channels. If Search is already near its ceiling, the next dollar put into Search might earn only $0.90, while TV's next dollar earns $1.65. Marketing Mix Modeling (MMM) accounts for adstock carryover and diminishing-returns saturation so you can allocate budget where marginal ROI is highest.
π― Questions It Answers
- How much of our weekly sales would have happened anyway (base sales) vs. incremental lift from each paid channel?
- How long does each channel's advertising continue to drive sales after the flight ends (adstock carryover)?
- Which channels are already saturated, and where will the next dollar of budget earn the highest marginal return?
π What Data You Need
- Weekly (or monthly) aggregate time-series dataβtypically 52 to 104+ weeks.
- 1 aggregate outcome column (e.g., Weekly Revenue or Units Sold).
- 2 to 6 channel spend columns plus optional seasonality, holiday, or price control variables.
π‘ What You'll Discover
- Sales Decomposition Waterfall: Base sales vs. each channel's incremental contribution.
- Average vs. Marginal ROI Curves: Saturation curves showing current spend vs. optimal budget reallocation.
- Adstock & Collinearity Diagnostics: Carryover decay rates and identification warnings when channels co-move.
π Key Nuances & Common Traps to Watch For Click to expand βΎ
- Average ROI vs. Marginal ROI: Historical average ROI divides total channel revenue by total spend; marginal ROI measures the slope at your current spend level. Always reallocate budget based on marginal ROI.
- Adstock Carryover Matters: Upper-funnel channels like TV and video build awareness that decays over subsequent weeks. Ignoring carryover makes TV look weak and gives Paid Search credit for harvesting TV-driven demand.
- Stay Inside Tested Spend Ranges: Never trust a response curve extrapolated far beyond historical spend levels, and use geo-lift or incrementality experiments to triangulate collinear channels.
π Under the Hood: Model Equations & Variable Definitions Click to expand βΎ
Step 1 DATA SOURCE & PREPARATION
Choose a realistic marketing mix scenario with known ground truth, or upload your own weekly time series with continuous spend columns.
Marketing Case Study
Explore multi-channel marketing mix datasets with known ground-truth parameters, or download the raw weekly CSV to inspect in Excel or Python.
Upload Your Data
Upload checklist (wide weekly time series):
- One row per period (minimum 52 weeks; 104 weeks recommended)
- One
date/weekcolumn and one continuoussales/revenueoutcome column ($) - 2 to 6 continuous paid marketing channel spend columns (in dollars
$), after the date and sales columns - Optional control columns (e.g.,
price,promo, competitor index); a column named like these starts as a control, not a channel
Drag & Drop Weekly CSV File
Continuous weekly spend & sales measures (52+ weeks), with 2 to 6 channel spend columns.
Need to clean or reshape your data first? Open in Import Wizard →Variable Roles & Econometric Controls
π€ Try this:
- Load Brightwell, set TV carryover to 0, and observe how TV attribution drops.
- Load Tandem Bikes, toggle seasonality off and on, and watch Social credit shift.
- Load Lumen Beauty, check Diagnostics, and observe the collinearity warnings.
Step 2 SHAPE EACH CHANNEL (ADSTOCK & SATURATION)
Marketing dollars rarely act all at once or scale in a straight line. Adjust Carryover (\(\lambda\))βhow much of last week's ad buzz lingers into this weekβand Saturation (\(K\))βthe spend level where diminishing returns kick in. Sliders recompute model fit live.
π§ͺ Triangulate: Calibrate Channel with Geo-Experiment (DiD)
Observational MMM struggles when channels are collinear or flighted. Pin a channel's Average ROI to a causal lift test (e.g., Difference-in-Differences geo-experiment) to anchor the regression and resolve unidentifiability.
π¬ Hyperparameter Identification Surface (\(\lambda \times K\) Heatmap)
Inspect training \(R^2\) across the \(10 \times 10\) grid of carryover (\(\lambda\)) and saturation (\(K\) quantiles) evaluated during Auto-Fit. A flat ridge means many \((\lambda, K)\) pairs fit equally well.
Click β‘ Run Auto-Fit above to compute and visualize the \(\lambda \times K\) identification surface.
Step 3 WHAT DROVE SALES (FIT, DECOMPOSITION & ROI)
Evaluate model fit, decompose total sales into baseline versus media channels, and compare historical average returns with forward-looking marginal returns.
Actual vs. Fitted Sales with Holdout Window
Compares observed weekly sales against the fitted econometric model. The shaded gray area on the right is held out from training to test honest out-of-sample prediction accuracy.
π Reading Guide: Actual vs. Fitted
A good model tracks weekly peaks and troughs without chasing random week-to-week noise. The model is trained only on the unshaded weeks; the shaded right-hand window is a future holdout test. If Holdout MAPE is much worse than Training MAPE, the model is memorizing past noise (overfitting) rather than capturing true marketing response.
Sales Decomposition Over Time
Stacked area visualization separating underlying baseline sales from incremental sales driven by each paid marketing channel.
π Reading Guide: Decomposition
Base sales are what the business would sell with zero marketing (typically 60β80% for mature brands, driven by brand equity, repeat customers, and seasonal demand). Marketing channels create the colored incremental wedges stacked on top.
Channel Contribution, Average ROI & Marginal ROI
Historical Average ROI divides total attributed sales by total spend (looking backward at all dollars spent so far). Marginal ROI measures the slope \(dY/dX\)βhow much revenue the very next dollar invested in that channel will return. 90% moving-block bootstrap intervals are shown in brackets.
| Channel | Weekly Spend | Total Contribution | Marketing Share | Average ROI | Marginal ROI (\(dY/dX\)) | Half-Life | Verdict |
|---|
Fitted Response Curves (Steady-State Weekly Return)
Shows diminishing returns with 90% bootstrap confidence ribbon. Red markers denote current average weekly spend. Compare the steep dashed secant from \((0,0)\) (Average ROI) against the flatter orange tangent slope at current spend (Marginal ROI \(dY/dX\)).
π Reading Guide: Steady-State Curves vs. Pulsed Spend & Confidence Ribbons
Why can the steady-state curve look higher than historical Average ROI on flighted channels? The curve shows expected weekly contribution if you spent a constant amount every week. When historical spend was pulsed in heavy bursts (on/off flighting), high-spend bursts hit diminishing returns harder than steady weekly spend would (Jensen's inequality). Also note that the shaded 90% ribbon reflects linear coefficient (\(\hat{\beta}\)) uncertainty conditional on your current \((\lambda, K)\) slider settings.
Carryover Echo (Adstock Decay from $10,000 Flight)
What is "carryover" (adstock)? When you run a $10,000 burst of advertising (a "flight") in Week 1 and then spend $0 in Weeks 2β6, customers don't forget your brand on Sunday nightβsome remember the ad and buy one, two, or three weeks later. This chart shows how that single $10,000 campaign echoes forward across future weeks based on each channel's retention rate (\(\lambda\)).
π Reading Guide: What Does Carryover Mean in Plain English (& Why Does It Matter?)
Think of marketing carryover like heat in a pan after you turn off the stove:
- High Carryover (\(\lambda \approx 0.50\text{β}0.70\), e.g., TV or Brand Video β "Cast-Iron Skillet"): If \(\lambda = 0.60\), 60% of this week's ad awareness survives into next week. A $10,000 flight in Week 1 still acts like $6,000 of effective ad pressure in Week 2, $3,600 in Week 3, and $2,160 in Week 4. Almost 60% of the total impact happens after the week the money was spent!
- Low Carryover (\(\lambda \approx 0.05\text{β}0.20\), e.g., Paid Search β "Light Switch"): Search ads capture shoppers who are ready to buy right now. If \(\lambda = 0.10\), only 10% of the effect lingers into Week 2 ($1,000) and 1% into Week 3 ($100). When you stop spending, the effect turns off almost immediately.
- Why this matters for your budget: If you judge channels only by same-week sales (setting \(\lambda = 0\)), upper-funnel brand channels look weak because their delayed sales echo gets misattributed to Baseline or Search.
Step 4 MOVE THE MONEY (BUDGET REALLOCATION)
Reallocate the exact same total marketing budget across channels by shifting funds toward channels with higher marginal ROI, strictly bounded by historically observed spend.
| Channel | Current Weekly Spend | Recommended Spend | Change ($) | Current mROI | New mROI | Binding Constraint |
|---|
Current vs. Recommended Weekly Spend
Dumbbell visualization showing current spend (gray circles) versus recommended spend (green diamonds). The optimizer reallocates budget until marginal returns (\(dY/dX\)) equalize across channels or safety bounds bind.
π Reading Guide: How the Optimizer Moves Money (& Why Bounds Bind)
The optimizer follows the Equal-Marginal Principle: it takes dollars away from saturated channels where the next dollar earns very little (low Marginal ROI) and moves them into headroom channels where the next dollar earns more (high Marginal ROI). As a channel receives more money, its marginal return gradually drops due to diminishing returns. The shift stops either when all channels reach the exact same marginal ROI ("equal water levels") or when a channel hits your Max Reallocation Shift guardrail (e.g., \(\pm 30\%\)) or its Observed Historical Range (since we never extrapolate beyond spend levels actually tested in the data).
Step 5 WHAT THIS MEANS (START HERE)
Step 6 MANAGERIAL & STATISTICAL INTERPRETATION
π Reveal the Truth (Known-Truth Debrief)
Compare estimated parameters against the true data-generating process.
Step 7 MODEL DIAGNOSTICS & CHECKS
Collinearity (VIF) & Spend Correlation Matrix
VIF > 5 or pairwise spend correlation \(|r| > 0.80\) indicates channels move in lockstep, inflating standard errors and making individual channel shares harder to separate without an experiment.
Residual Autocorrelation & Durbin-Watson Check
Checks whether training errors (\(y_t - \hat{y}_t\)) exhibit unmodeled serial correlation (omitted seasonality, trend, or carryover). Ideal Durbin-Watson is near \(2.0\).
π Methodological Notes & Econometric Caveats (Plain-Language & Technical Reference)
Every Marketing Mix Model makes structural approximations to keep estimation transparent and fast in the browser. Expand or review the cards below for both a Plain-Language Intuition (for marketers and students) and the Econometric Formulation (for analysts and technical reviewers).
1. Bootstrap Intervals: Conditional vs. Full Joint Uncertainty
The 90% confidence intervals show how much ROI estimates wobble due to week-to-week sales noise assuming your chosen Carryover (\(\lambda\)) and Saturation (\(K\)) settings are fixed. If you are also uncertain about the exact curve shape, true uncertainty is widerβnudge the \(\lambda\) and \(K\) sliders or pin a channel with a geo-experiment to test sensitivity.
The moving-block residual bootstrap (\(B = 4\) weeks) resamples training innovations \(\hat{\varepsilon}_t\) and refits \(\hat{\beta}^{(r)} \ge 0\) via constrained OLS with \((\lambda_c, K_c, S_c)\) held fixed. Thus \([q_{0.05}, q_{0.95}]\) estimates the conditional sampling distribution \(p(\hat{\beta}, \text{mROI} \mid \hat{\lambda}, \hat{K}, \mathbf{y})\) rather than the full marginal posterior \(\int p(\beta, \lambda, K \mid \mathbf{y})\,d\lambda\,dK\) obtained via Bayesian MCMC (e.g., PyMC-Marketing). When experimental calibration pins are active, \(\beta_{\text{pinned}}^{(r)}\) is fixed across replicates, propagating variance reduction to collinear unpinned channels.
2. Steady-State Curves vs. Pulsed Historical Spend (Jensen's Inequality)
Response curves plot what a channel would return if you spent a steady, even amount every single week. When a channel was historically pulsed in bursts ("flighted" with On/Off weeks, like TV in Hearthstone), spending $20,000 every other week hits diminishing returns harder during "On" weeks than spending $10,000 every weekβso the steady-state curve at average spend sits above historical pulsed ROI.
For a concave Hill function \(H(A; K, S=1) = \frac{A}{A+K}\) (\(\frac{\partial^2 H}{\partial A^2} < 0\)), Jensen's inequality implies that for any time-varying adstock series \(A_{c,t}\) with mean \(\bar{A}_c = \frac{\bar{x}_c}{1-\lambda_c}\), steady-state evaluation exceeds the mean of pulsed evaluations: \(\beta_c H(\bar{A}_c; K_c) \ge \frac{1}{T}\sum_{t=1}^T \beta_c H(A_{c,t}; K_c)\). Consequently, the steady-state secant slope \(Y_{\text{ss}}(\bar{x}_c)/\bar{x}_c\) equals historical Average ROI \(\left(\sum_t \hat{y}_{c,t} / \sum_t x_{c,t}\right)\) only under constant spend (\(\text{Var}(x_{c,t}) = 0\)).
3. Auto-Fit Identification: In-Sample Grid Search vs. Regularized Priors
Auto-Fit scans a grid of Carryover and Saturation values to maximize training fit (\(R^2\)). With only 1β2 years of weekly data, different combinations of \(\lambda\) and \(K\) can sometimes produce nearly identical overall fit (a flat ridge on the heatmap), or latch onto holiday demand spikes if seasonality controls are turned off.
Coordinate descent minimizes unpenalized training \(\text{SSE}_{\text{train}}(\boldsymbol{\lambda}, \mathbf{K}) = \min_{\boldsymbol{\beta} \ge 0} \|\mathbf{y}_{\text{train}} - \mathbf{X}(\boldsymbol{\lambda}, \mathbf{K})\boldsymbol{\beta}\|_2^2\) across a \(10 \times 10\) grid per channel. Without informative Beta/Gamma priors on \((\lambda_c, K_c)\) or out-of-sample regularization, joint \((\lambda_c, K_c, \beta_c)\) parameters can be weakly identified along iso-\(R^2\) ridges when spend variation is narrow or collinearity (\(\text{VIF} > 5\)) is present. Always inspect the \(\lambda \times K\) Identification Surface and Holdout MAPE.
4. S-Curve Thresholds (\(S > 1\)) & Non-Convex Optimization
Standard diminishing-returns curves (\(S=1\)) are steepest at the first dollar, so equalizing marginal ROI (\(dY/dx\)) automatically finds the best budget split. S-curve channels (\(S=2\)) have a flat "dead zone" at low spend before ramping upβyou must either fund them past their takeoff threshold or not fund them at all.
For \(S = 1\), \(Y_c(x_c)\) is strictly concave on \([0, \infty)\), so the KKT condition \(\frac{\partial Y_c}{\partial x_c} = \mu\) (within box bounds \([L_c, U_c]\)) is necessary and sufficient for a global optimum. For \(S > 1\), \(Y_c(x_c)\) is convex below the inflection point \(x_{\text{infl}} = K_c(1-\lambda_c)\sqrt[S]{\frac{S-1}{S+1}}\) and achieves peak average return (ray tangency) at \(x_{\text{tan}} = K_c(1-\lambda_c)(S-1)^{1/S}\). Our allocator augments local \(dY/dx\) with threshold chord evaluation to \([x_{\text{infl}}, x_{\text{tan}}]\), and exports curves to the Resource Allocation Optimizer for multi-constraint portfolio solving.
EXPLORATORY QUESTIONS
- On Brightwell, Paid Search delivers a historical average ROI of ~$3.80 while TV delivers ~$2.45. Why would shifting budget from Search to TV increase overall sales?
- Set TV carryover (Ξ») to 0. Why does TV's estimated contribution shrink, and which channel artificially captures the credit?
- On Tandem Bikes, toggle seasonality off and on. Why does Social credit shrink by over 30% when seasonality is controlled?
- On Lumen Beauty, inspect the spend correlation and VIF between Social and Influencer. Why does the model struggle to separate their effects?
- Why does the budget optimizer refuse to recommend spending above a channel's historical observed maximum?