Marketing Mix Modeling Lab

Attribution & Mix Response Modeling

Estimate what each marketing channel really contributes to sales, including carryover and diminishing returns, and find a better way to split the same budget.

πŸ‘¨β€πŸ« Professor Mode: Guided Learning Experience

Walk through the difference between average ROI and marginal ROI, explore adstock decay, and see why optimizing budgets requires looking at where the next dollar works hardest.

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 β–Ύ
1. Geometric Adstock Carryover & Saturation Transformation Carryover & Diminishing Returns
\[ A_{c,t} = X_{c,t} + \lambda_c A_{c,t-1}, \qquad S_{c,t} = \frac{A_{c,t}^{\alpha_c}}{A_{c,t}^{\alpha_c} + \gamma_c^{\alpha_c}} \]
πŸ’¬ Read it in plain English: First, Adstock (\(A_{c,t}\)) blends this week's spend with a decaying fraction \(\lambda_c\) of last week's effective stock; second, the Hill saturation curve (\(S_{c,t}\)) bends that stock into an S-shaped or diminishing-returns curve between 0 and 1.
Variable & Symbol Directory (Every Term Defined) Hover or focus any role badge for details
Xc,t
Raw Channel Spend YOUR DATA β“˜
Actual dollars (or impressions) spent on marketing channel \(c\) in week \(t\).
Ac,t, λc
Adstocked Spend & Decay Rate ESTIMATED β“˜
\(A_{c,t}\) is effective cumulative ad stock in week \(t\); \(\lambda_c \in [0, 1)\) is the fraction carried over from week \(t-1\).
Sc,t
Saturated Channel Response OUTPUT β“˜
Diminishing-returns index (\(0\) to \(1\)) entering the sales regression for channel \(c\) in week \(t\).
αc, γc
Hill Shape & Half-Saturation Point ESTIMATED β“˜
\(\alpha_c\) governs curvature; \(\gamma_c\) is the inflection/half-saturation spend level where channel \(c\) reaches 50% of its ceiling.
2. Aggregate Marketing Mix Outcome Equation & Marginal ROI Decomposition & Optimization
\[ Y_t = \beta_0 + \sum_{c=1}^{C} \beta_c S_{c,t} + \sum_{m=1}^{M} \delta_m Z_{m,t} + \varepsilon_t, \qquad \text{mROI}_c = \frac{\partial \hat{Y}}{\partial X_c} \]
πŸ’¬ Read it in plain English: Weekly sales \(Y_t\) equal baseline sales \(\beta_0\) plus the sum of each channel's saturated contribution \(\beta_c S_{c,t}\) and control variables \(Z_{m,t}\), while Marginal ROI (\(\text{mROI}_c\)) measures the incremental revenue earned by the next $1 added to channel \(c\).
Variable & Symbol Directory (Every Term Defined) Hover or focus any role badge for details
Yt, β0
Weekly Sales & Baseline Intercept YOUR DATA β“˜
\(Y_t\) is total sales in week \(t\); \(\beta_0\) is baseline organic demand when paid media is zero.
βc
Channel Response Scaling Weight ESTIMATED β“˜
Scales saturated channel activation \(S_{c,t}\) into dollar or unit sales contribution for channel \(c\).
δm, Zm,t
Control Variable Effect & Value ESTIMATED β“˜
\(Z_{m,t}\) is control variable \(m\) in week \(t\) (e.g., holiday or seasonality); \(\delta_m\) is its estimated impact.
mROIc
Marginal Return on Next Dollar OUTPUT β“˜
Incremental sales generated by adding $1 more to channel \(c\) at its current spend level.

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.

πŸ“‹ Case Study Click to switch
πŸ“‚

Upload Your Data

Upload checklist (wide weekly time series):

  • One row per period (minimum 52 weeks; 104 weeks recommended)
  • One date/week column and one continuous sales/revenue outcome 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 →
πŸ“‚ Upload Data Click to switch

Variable Roles & Econometric Controls

πŸ€” Try this:

  1. Load Brightwell, set TV carryover to 0, and observe how TV attribution drops.
  2. Load Tandem Bikes, toggle seasonality off and on, and watch Social credit shift.
  3. 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.

Mode:
πŸ§ͺ 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.

Base Sales Share
--%
Sales without marketing
Media Lift Share
--%
Sales driven by marketing
Model Fit (RΒ²)
--
Training explanatory power
Holdout Error (MAPE)
--%
Out-of-sample prediction error

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.

Predicted Impact with Same Total Budget
+0.0% Sales Lift
Equalizing marginal return inside observed bounds.
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

Plain Language

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.

Econometric Detail

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)

Plain Language

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.

Econometric Detail

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

Plain Language

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.

Econometric Detail

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

Plain Language

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.

Econometric Detail

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

  1. 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?
  2. Set TV carryover (Ξ») to 0. Why does TV's estimated contribution shrink, and which channel artificially captures the credit?
  3. On Tandem Bikes, toggle seasonality off and on. Why does Social credit shrink by over 30% when seasonality is controlled?
  4. On Lumen Beauty, inspect the spend correlation and VIF between Social and Influencer. Why does the model struggle to separate their effects?
  5. Why does the budget optimizer refuse to recommend spending above a channel's historical observed maximum?