# Marketing Mix Modeling Concept Card: [Brand, market or course]

> A concept card for building, reading and challenging a marketing-mix model (MMM): adstock (carry-over), saturation
> curves, base versus incremental sales, decomposition, return on investment and marginal return, validation,
> calibration with experiments, and the open-source tools practitioners use. It is for MBA students in a marketing
> analytics elective and for managers who receive an MMM from an agency or an in-house team and must decide how far
> to trust it. It builds on the core card mba/mba-marketing-mix-card, which introduced the response curve, adstock and
> the equal-marginal-return allocation rule, and on mba/mba-regression-for-managers-card for regression basics; neither
> is re-taught here. Source basis: Koyck (1954), Broadbent (1979), Little (1970), Dorfman and Steiner (1954), Hanssens,
> Parsons and Schultz (2001), Jin et al. (2017) and Sethuraman, Tellis and Briesch (2011), written up in original words
> with simulated, fictional Northfield Outdoor Co. data. Every number was computed in Python (numpy) and is shown with
> its Excel route where Excel can compute it.
> Replace every [bracketed] value and every row marked *Example*.

<!--
Guidance: How to use this template
- Read the core card mba/mba-marketing-mix-card first: this card assumes you know R(x) = a(1 - exp(-x/k)), average versus
  marginal return and the allocation rule.
- Run the Python block in Worked example A. It simulates two years of weekly data whose true effects are known, fits an
  MMM and shows how a model can fit well and still mis-credit a channel. Reproduce the numbers before you read on.
- Fill every [ ] in Worked example B (adstock by hand) before you check the answer key, then do Exercise C.
- Use the "MMM review checklist" on any model you receive. Log every self-test miss in mba/mba-error-log.
- Related templates: mba/mba-marketing-mix-card (core), mba/mba-regression-for-managers-card (core),
  mba/mba-marketing-experiments-card, mba/mba-digital-attribution-card, mba/mba-marketing-analytics-guide,
  mba/mba-customer-lifetime-value-card, mba/mba-pricing-card, mba/mba-decision-analytics-guide, mba/mba-formula-sheet,
  academic/stats-linear-regression, academic/stats-time-series-arima, academic/stats-bayesian-inference.
-->

## Document control

<!-- Guidance: Record the data period, the granularity and every modelling choice that is not estimated from the data (adstock grid, saturation form, priors). A reviewer needs them to reproduce the model. -->

| Field | Value |
|---|---|
| Document ID | [MKT-MMM-001] |
| Course or project | [Marketing analytics elective / FY budget review] |
| Learner or analyst | [Your name] |
| Decision owner | [@owner] |
| Reviewed by | [Study group / analytics lead / agency] |
| Data set | [Northfield Outdoor Co., 104 weeks, simulated (fictional) / your data and source] |
| Granularity | [Weekly, national / weekly by region] |
| Model form | [OLS with grid-searched adstock and saturation / Bayesian / ridge] |
| Calibration evidence | [Search geo holdout, iROAS 3.7, SE 0.5 / your experiments] |
| Software | Python 3 with numpy; Excel (LINEST, EXP, Solver or Data Table) for the single-model route |
| Last updated | YYYY-MM-DD |
| Version | [0.1] |
| Classification | [Course work / internal] |

## When to use it

<!-- Guidance: MMM answers "how much did each channel add, and where should the next dollar go" at the level of total sales. Check that your question is at that level. -->

**One-sentence rule:** use an MMM when you need the incremental contribution and return of **all** major marketing
levers, including offline ones, on **total** sales, with privacy-safe aggregate data, and you have at least two years
of weekly history with real variation in spend.

| Good uses | Poor uses |
|---|---|
| Annual and quarterly budget setting across TV, digital, print, promotions | Day-to-day bidding or creative choice (too slow, too aggregate) |
| Separating base sales from marketing-driven sales | A brand with one year of data and constant spend in every week |
| Measuring channels with no user-level tracking (TV, radio, out-of-home) | Proving causality on its own (needs experiments to calibrate) |
| Price and promotion effects alongside media | Very small channels whose spend is below the noise level |
| Checking platform and attribution claims against total sales | Questions about individual customer journeys (mba/mba-digital-attribution-card) |

## The idea in plain language

<!-- Guidance: Practise explaining MMM in three sentences: sales are base plus the effects of each lever; each lever's effect lasts and saturates; the model estimates those shapes from how sales moved when spend moved. -->

An MMM explains weekly sales as a **base** (what the brand would sell with no current marketing: distribution, brand
strength, trend, season) plus the **incremental** effect of each marketing lever, plus price, promotions and outside
factors. Two shapes make it a marketing model rather than a plain regression:

1. **Carry-over (adstock).** Advertising this week still sells next week. Koyck (1954) introduced geometrically
   declining distributed lags in economics; Broadbent (1979) coined "adstock" for the decaying stock of TV advertising.
   With retention rate $\theta$, the effect halves every $\ln 0.5/\ln\theta$ weeks.
2. **Saturation (diminishing returns).** The first dollars in a channel reach the most responsive people; later
   dollars reach people who already saw the message. The response curve flattens.

The model learns these shapes from history: weeks with heavy TV and weeks without, weeks with promotions and weeks
without. The coefficients then give each lever's contribution, its return on investment (ROI) and, most useful for
budgeting, its **marginal** return at current spend, which feeds the allocation rule of the core card.

The idea is old. Little (1970) argued that managers will only use models that are simple, robust and easy to
control; Dorfman and Steiner (1954) showed that a profit-maximising firm sets its advertising-to-sales ratio equal to
the ratio of its advertising elasticity to its price elasticity. Hanssens, Parsons and Schultz (2001) is the standard
reference on market response models. For scale, a meta-analysis of 751 short-term and 402 long-term brand advertising
elasticities from 56 studies (Sethuraman, Tellis and Briesch 2011) found an average short-term elasticity of 0.12 and
an average long-term elasticity of 0.24: a 10% rise in advertising goes with roughly 1.2% more sales in the short run.
An MMM that implies much larger effects deserves a second look. What is new since the late 2010s is the return of MMM as user-level tracking
declined, and Bayesian versions that combine data with prior knowledge and experiments (Jin et al. 2017).

## Formulas and notation

<!-- Guidance: Learn the model line, the two transformations and the three outputs (contribution, ROI, marginal ROI). The rest is estimation detail. -->

**Model** for week $t$:

$$\text{Sales}_t = \beta_0 + \beta_1 t + \gamma\, \text{Season}_t + \delta\,(\text{Price}_t - 1) + \sum_{c} \beta_c\, f\!\left(A_{c,t};\, k_c\right) + \varepsilon_t$$

**Geometric adstock** with retention $\theta_c \in [0, 1)$:

$$A_{c,t} = x_{c,t} + \theta_c A_{c,t-1}, \qquad \text{total weight of one unit of spend} = \frac{1}{1-\theta_c}, \qquad \text{half-life} = \frac{\ln 0.5}{\ln \theta_c}$$

**Saturation.** The negative exponential of the core card, $f(A; k) = 1 - e^{-A/k}$, or the Hill curve (named after
Hill's 1910 dose-response work), which allows an S-shape:

$$f_{\text{Hill}}(A; K, s) = \frac{A^{s}}{A^{s} + K^{s}}$$

where $K$ is the half-saturation point and $s$ the shape. Tools differ in which they use; the business reading is the
same.

**Contribution, ROI and marginal ROI** of channel $c$ over the period:

$$\text{Contr}_c = \sum_t \beta_c f(A_{c,t}), \qquad \text{ROI}_c = \frac{\text{Contr}_c}{\sum_t x_{c,t}}$$

At a steady weekly spend $x$, the adstock settles at $x/(1-\theta)$, so the weekly response and marginal return are

$$R_c(x) = \beta_c\left(1 - e^{-x/((1-\theta_c)k_c)}\right), \qquad R_c'(x) = \frac{\beta_c}{(1-\theta_c)k_c}\, e^{-x/((1-\theta_c)k_c)}$$

**Profit test.** With gross margin $g$, the next dollar pays back if $g\, R_c'(x) > 1$; channels should be funded until
their marginal returns are equal (core card).

| Symbol | Meaning | Northfield example A (fitted) |
|---|---|---|
| $x_{c,t}$ | Spend in channel $c$, week $t$ (USD k) | TV average 46.58, search average 34.49 |
| $A_{c,t}$ | Adstocked spend | TV only |
| $\theta_c$ | Adstock retention per week | 0.60 for TV (search: none) |
| $k_c$ | Saturation scale (higher means slower saturation) | 175 for TV, 20 for search |
| $\beta_c$ | Maximum weekly incremental sales from the channel (USD k) | 500.51 TV, 289.55 search |
| $\delta$ | Price effect per unit of price index | -1,473.49 |
| $\text{Contr}_c$ | Incremental sales credited to $c$ over the period | TV 23,394.6, search 24,089.2 |
| $g$ | Gross margin | 0.45 |

## Assumptions and limits

<!-- Guidance: The first three rows decide whether the MMM can be causal at all. Write down how your model deals with each before you show a single ROI. -->

| Assumption | What it means | How to check or protect | If it fails |
|---|---|---|---|
| Spend varies for reasons unrelated to demand | Otherwise the model cannot separate marketing from demand | Ask how budgets are set; plot spend against season | Channel effects absorb seasonality: calibrate with experiments |
| Channels do not move in lockstep | Separate variation per channel | Correlation matrix of spend; variance inflation factors | Credit is split arbitrarily between correlated channels |
| Right functional form | Adstock and saturation shapes fit reality | Compare forms; residual plots; holdout error | Biased ROIs, wrong marginal returns |
| All important drivers included | Price, distribution, competitors, macro, weather | Business review of the variable list | Omitted variable bias (core regression card) |
| Stable effects over the window | The channel works the same in year 1 and year 2 | Rolling-window refits | Use time-varying coefficients or shorter windows |
| Enough data | Typically 2 to 3 years of weekly data, or regional panels | Count parameters against observations | Priors, pooling across regions, fewer channels |

**The central limit of MMM.** Many different parameter sets fit history almost equally well (sometimes called
equifinality). Fit statistics cannot choose among them; outside evidence (experiments, priors, business sense) must.
Worked example A shows this directly.

## How managers use it in practice

<!-- Guidance: When an MMM is presented to you, ask for three things: the decomposition chart, the response curve per channel with uncertainty, and the experiment it was calibrated against. -->

- **Budget setting.** Response curves feed the allocation rule; the profit-maximising budget sets every channel's
  marginal gross profit to one dollar (mba/mba-marketing-mix-card).
- **Triangulation.** MMM (top-down, all channels, total sales), attribution (bottom-up, digital paths) and experiments
  (causal, one channel at a time) are combined; experiments are the arbiter (mba/mba-marketing-experiments-card).
- **Base-sales tracking.** A shrinking base with steady marketing contributions is an early warning about brand health
  or distribution (mba/mba-brand-equity-card).
- **Tools (dated facts, checked 28 October 2026).** Meta's Robyn is an open-source, MIT-licensed MMM package in R
  (with a beta Python API first released on 22 November 2023) that uses ridge regression, Nevergrad hyperparameter
  optimisation and Prophet decomposition, and can be calibrated with lift tests
  ([Robyn](https://github.com/facebookexperimental/Robyn)). Google's Meridian is an open-source (Apache-2.0) Bayesian
  MMM in Python that supports national or geo-level data and experiment-based priors
  ([Meridian](https://github.com/google/meridian)); Google's earlier LightweightMMM states it is no longer supported and
  recommends Meridian, and its repository was archived in January 2026. PyMC-Marketing (PyMC Labs, Apache-2.0) offers
  Bayesian MMM with adstock, saturation and lift-test calibration. Check each project's page for current versions and
  methods before you rely on them.

```mermaid
flowchart TD
    Q["Budget question across channels"] --> D["Collect 2 to 3 years of weekly sales, spend, price, controls"]
    D --> E["Explore: spend versus season, channel correlations"]
    E --> M["Specify: adstock, saturation, controls, priors"]
    M --> F["Fit candidate models"]
    F --> V{"Holdout error acceptable?"}
    V -->|No| M
    V -->|Yes| C{"Consistent with experiments?"}
    C -->|No| CAL["Calibrate: priors or penalties from lift tests"]
    CAL --> F
    C -->|Yes| O["Decompose, ROI, marginal ROI, response curves"]
    O --> B["Re-allocate in measured steps"]
    B --> X["Run the next experiment on the most uncertain channel"]
    X --> D
```

## Worked example A (fully solved): a two-year MMM for Northfield

<!-- Guidance: Run the code, then read each step. The point of using simulated data is that you can compare the model with the truth, which never happens with real data. -->

**Situation (fictional).** Northfield Outdoor Co. spends on regional TV in flights (54 of 104 weeks, USD 4,844.8k in
total) and on paid search every week (USD 3,587.2k), and runs 10% price promotions in a few weeks. Weekly revenue
averages USD 1,036.6k. The CMO asks: what did TV and search each add, and where should next year's money go? Search
spend follows demand (correlation with the seasonal curve 0.929), as it does in most firms, because search budgets are
set against query volume.

The data are simulated so that the true effects are known: TV adstock 0.6 and scale 150 with a maximum of 500;
search scale 60 with a maximum of 300; noise SD 30. The analyst does not know these values.

### Step 1: fit the model

```python
import itertools
import numpy as np

rng = np.random.default_rng(61)                      # simulated Northfield data, 104 weeks
T = 104; t = np.arange(T); season = np.sin(2*np.pi*(t-10)/52)
price = 1 - 0.10*(rng.random(T) < 0.12)              # 10% promotions in some weeks
tv = np.where(rng.random(T) < 0.45, rng.uniform(60, 120, T), 0.0).round(1)
search = (35 + 12*season + rng.normal(0, 4, T)).round(1)

def adstock(x, theta):
    out, carry = np.zeros_like(x), 0.0
    for i, v in enumerate(x):
        carry = v + theta*carry; out[i] = carry
    return out

def sat(x, k):
    return 1 - np.exp(-x/k)

sales = (600 + 0.8*t + 120*season - 1500*(price-1) + 500*sat(adstock(tv, 0.6), 150)
         + 300*sat(search, 60) + rng.normal(0, 30, T)).round(1)

def X(theta, k_tv, k_s):
    return np.column_stack([np.ones(T), t, season, price-1,
                            sat(adstock(tv, theta), k_tv), sat(search, k_s)])

fits = []
for theta, k_tv, k_s in itertools.product(np.arange(0, 0.91, 0.05), range(50, 401, 25), range(20, 151, 10)):
    b, *_ = np.linalg.lstsq(X(theta, k_tv, k_s), sales, rcond=None)
    fits.append((((sales - X(theta, k_tv, k_s) @ b)**2).sum(), round(theta, 2), k_tv, k_s, b))
sse, theta, k_tv, k_s, b = min(fits, key=lambda f: f[0])
Xb = X(theta, k_tv, k_s)
print("theta, k_tv, k_s:", theta, k_tv, k_s, " R2:", round(1 - sse/((sales - sales.mean())**2).sum(), 4))
print("coefficients:", [round(float(v), 2) for v in b])
print("ROI TV:", round((Xb[:, 4]*b[4]).sum()/tv.sum(), 3), " ROI search:", round((Xb[:, 5]*b[5]).sum()/search.sum(), 3))
```

Output from the run:

```text
theta, k_tv, k_s: 0.6 175 20  R2: 0.978
coefficients: [507.33, 1.0, 129.13, -1473.49, 500.51, 289.55]
ROI TV: 4.829  ROI search: 6.715
```

The grid search tries 19 adstock rates, 15 TV scales and 14 search scales (3,990 models) and keeps the one with the
smallest sum of squared errors; for each, the remaining coefficients are ordinary least squares. **Excel route for
one candidate:** put spend in column B, the adstock in C with `=B3+$H$1*C2` (retention in `H1`), the saturation in D
with `=1-EXP(-C2/$H$2)`, put trend, season, price minus 1, the TV saturation and the search saturation in adjacent
columns, then `=LINEST(sales range, that block, TRUE, TRUE)` (LINEST returns the coefficients in reverse column
order, intercept last); use a Data Table or Solver over `H1` and `H2` to minimise `=SUMXMY2(actual, fitted)`.

### Step 2: validate out of sample

Refit on weeks 1 to 91 only and forecast weeks 92 to 104: the selected parameters are 0.55, 150 and 50, and the
holdout mean absolute percentage error is **3.03%** (`=AVERAGE(ABS((actual-forecast)/actual))`). The model fits and
forecasts well: $R^2$ = 0.978, residual SD 30.35, close to the true noise of 30.

### Step 3: decompose and compute returns

| Component | Contribution over 104 weeks (USD k) | Share of fitted sales | Spend (USD k) | ROI (revenue per USD) |
|---|---|---|---|---|
| Base (intercept, trend, season) | 58,114.6 | 53.9% | | |
| Promotions | 2,210.2 | 2.1% | | |
| TV | 23,394.6 | 21.7% | 4,844.8 | 4.829 |
| Paid search | 24,089.2 | 22.3% | 3,587.2 | 6.715 |

At a 45% gross margin, profit ROI is $0.45 \times 4.829 - 1 = 1.173$ for TV and 2.022 for search. Marginal ROAS at
average spend (steady-state formula): TV 3.675, search 2.581. TV's adstock of 0.60 means one dollar of TV counts 2.50
times in total, with a half-life of 1.36 weeks.

```vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "Northfield MMM decomposition of fitted weekly sales by quarter, USD thousands (simulated, fictional)",
  "width": 420,
  "height": 260,
  "data": {"values": [
    {"quarter": "Q1", "component": "Base", "usd_k": 5970.4}, {"quarter": "Q1", "component": "Promotions", "usd_k": 0.0}, {"quarter": "Q1", "component": "TV", "usd_k": 1259.1}, {"quarter": "Q1", "component": "Paid search", "usd_k": 2926.6},
    {"quarter": "Q2", "component": "Base", "usd_k": 8181.1}, {"quarter": "Q2", "component": "Promotions", "usd_k": 442.0}, {"quarter": "Q2", "component": "TV", "usd_k": 3449.9}, {"quarter": "Q2", "component": "Paid search", "usd_k": 3320.9},
    {"quarter": "Q3", "component": "Base", "usd_k": 7713.8}, {"quarter": "Q3", "component": "Promotions", "usd_k": 442.0}, {"quarter": "Q3", "component": "TV", "usd_k": 2816.1}, {"quarter": "Q3", "component": "Paid search", "usd_k": 3243.1},
    {"quarter": "Q4", "component": "Base", "usd_k": 5840.9}, {"quarter": "Q4", "component": "Promotions", "usd_k": 147.3}, {"quarter": "Q4", "component": "TV", "usd_k": 2452.3}, {"quarter": "Q4", "component": "Paid search", "usd_k": 2640.0},
    {"quarter": "Q5", "component": "Base", "usd_k": 6646.0}, {"quarter": "Q5", "component": "Promotions", "usd_k": 0.0}, {"quarter": "Q5", "component": "TV", "usd_k": 2663.0}, {"quarter": "Q5", "component": "Paid search", "usd_k": 2823.0},
    {"quarter": "Q6", "component": "Base", "usd_k": 8856.7}, {"quarter": "Q6", "component": "Promotions", "usd_k": 589.4}, {"quarter": "Q6", "component": "TV", "usd_k": 4086.6}, {"quarter": "Q6", "component": "Paid search", "usd_k": 3328.6},
    {"quarter": "Q7", "component": "Base", "usd_k": 8389.3}, {"quarter": "Q7", "component": "Promotions", "usd_k": 147.3}, {"quarter": "Q7", "component": "TV", "usd_k": 3244.4}, {"quarter": "Q7", "component": "Paid search", "usd_k": 3289.0},
    {"quarter": "Q8", "component": "Base", "usd_k": 6516.4}, {"quarter": "Q8", "component": "Promotions", "usd_k": 442.0}, {"quarter": "Q8", "component": "TV", "usd_k": 3423.1}, {"quarter": "Q8", "component": "Paid search", "usd_k": 2518.1}
  ]},
  "mark": "bar",
  "encoding": {
    "x": {"field": "quarter", "type": "ordinal", "title": "Quarter (13 weeks)"},
    "y": {"field": "usd_k", "type": "quantitative", "stack": "zero", "title": "Fitted sales (USD k)"},
    "color": {"field": "component", "type": "nominal", "title": "Component", "sort": ["Base", "Promotions", "TV", "Paid search"]},
    "tooltip": [{"field": "quarter"}, {"field": "component"}, {"field": "usd_k"}]
  }
}
```

### Step 4: compare with the truth

Because the data are simulated, we can do what no real analyst can:

| Channel | Fitted contribution | True contribution | Fitted ROI | True ROI | Fitted marginal ROAS | True marginal ROAS |
|---|---|---|---|---|---|---|
| TV | 23,394.6 | 25,794.0 | 4.829 | 5.324 | 3.675 | 3.834 |
| Paid search | 24,089.2 | 13,414.4 | **6.715** | **3.740** | 2.581 | 2.814 |

TV is roughly right. Search is credited with almost twice its true contribution, and its saturation scale (20 against
a true 60) is far off. The reason is the 0.929 correlation between search spend and the seasonal curve: when two
inputs move together, the data cannot tell which one produced the sales, and the model handed part of the base to
search (the fitted intercept is 507 against a true 600). The model had an excellent $R^2$ and holdout error, and was
still wrong about the channel that matters most for the budget. Fit is necessary, not sufficient.

A naive linear regression with no adstock and no season is far worse: it gives search a coefficient of 13.98 revenue
per dollar, because search is standing in for the whole seasonal swing. Adding the season term alone cuts it to 2.55.

### Step 5: calibrate with an experiment

Northfield runs a search geo holdout (mba/mba-marketing-experiments-card) and measures an incremental ROAS of 3.7 with
a standard error of 0.5. A simple calibration adds a penalty to the fit criterion for disagreeing with the experiment:
minimise $SSE/\hat{\sigma}^2 + \left((\text{ROI}_{\text{search}} - 3.7)/0.5\right)^2$, a rough stand-in for a Bayesian
prior.

| Model | $\theta$, $k_{TV}$, $k_s$ | SSE versus best fit | TV ROI | Search ROI | TV marginal ROAS | Search marginal ROAS |
|---|---|---|---|---|---|---|
| Best fit | 0.60, 175, 20 | 1.0000 | 4.829 | 6.715 | 3.675 | 2.581 |
| Calibrated | 0.55, 150, 70 | 1.0100 | 4.353 | 3.790 | 3.294 | 2.977 |
| Truth (simulator) | 0.60, 150, 60 | | 5.324 | 3.740 | 3.834 | 2.814 |

The calibrated model fits only 1% worse and now credits search correctly (3.790 against 3.740). The base rises to
USD 70,902.2k. TV moved further from the truth (4.353 against 5.324): the experiment fixed the channel it measured and
nothing else. The lesson for the measurement plan: the next experiment should be on TV.

### Step 6: the answer to the CMO

"Both channels pay back on average and at the margin: at a 45% gross margin the break-even marginal ROAS is 2.22, and
the calibrated model puts TV at 3.29 and search at 2.98. The uncalibrated model overstated search by about 80% because
search spend follows the season; we now use the calibrated version. Recommendation: raise total spend modestly, tilt
the increase towards TV, move in steps, and run a regional TV test this spring to pin down the TV curve."

## Worked example B (half solved): adstock and contribution by hand

<!-- Guidance: Do this on paper or in six spreadsheet rows. Being able to compute adstock by hand is the best protection against misreading a model's output. -->

A channel spends USD k 100, 0, 0, 50, 0, 0 in weeks 1 to 6. Retention $\theta$ = 0.5, saturation $k$ = 100,
$\beta$ = 400 (maximum weekly incremental sales, USD k).

| Week | Spend | Adstock $A_t = x_t + 0.5 A_{t-1}$ | $1 - e^{-A_t/100}$ | Contribution $400 \times$ previous column |
|---|---|---|---|---|
| 1 | 100 | 100.00 | 0.6321 | 252.8 |
| 2 | 0 | 50.00 | 0.3935 | 157.4 |
| 3 | 0 | [ ] | 0.2212 | [ ] |
| 4 | 50 | [ ] | [ ] | 185.9 |
| 5 | 0 | 31.25 | 0.2684 | [ ] |
| 6 | 0 | 15.625 | [ ] | 57.9 |

1. Fill the gaps. Excel: adstock `=B3+0.5*C2`, saturation `=1-EXP(-C3/100)`, contribution `=400*D3`.
2. Total contribution over six weeks: [ ]. ROI on USD 150k of spend: [ ].
3. What share of the week-1 spend's effect appears after week 1? Explain why a model without adstock would
   understate TV. [ ]
4. Half-life of this adstock in weeks: $\ln 0.5/\ln 0.5$ = [ ].
5. If the same USD 150k were spent as USD 25k a week for six weeks, would total contribution be higher or lower,
   counting only weeks 1 to 6 and counting the carry-over after week 6 as well? Explain using saturation and
   adstock. [ ]

## Exercise C (unsolved): stress-test the Northfield model

<!-- Guidance: Use the code in Worked example A. Change one thing at a time and write one sentence on what each change teaches. -->

1. Refit on weeks 1 to 91, forecast weeks 92 to 104 and report the selected parameters and the holdout MAPE.
2. Fit a linear model with trend, season and price but no adstock or saturation (spend enters as is). Report the TV
   and search coefficients and explain the difference from the full model.
3. Using the fitted parameters ($\beta$ = 500.51, $\theta$ = 0.60, $k$ = 175), compute TV's steady-state marginal ROAS at
   USD 100k a week. Is a 100k weekly TV level profitable at the margin with a 45% gross margin?
4. Re-run the simulation with search spend that does **not** follow the season (replace `12*season` by 0). Does the
   search ROI estimate get closer to the truth? Why?
5. Write the three questions you would ask an agency that presents this MMM.

## MMM review checklist

<!-- Guidance: Use this list on every MMM you receive. A "no" on any of the first four is a reason not to reallocate budget on the model alone. -->

| Check | Question | Answer |
|---|---|---|
| Data window | At least two years of weekly data, or a regional panel? | [Yes / no] |
| Spend variation | Did each channel have on and off periods or regional differences? | [ ] |
| Confounding | How is seasonality separated from demand-following spend (search, promotions)? | [ ] |
| Calibration | Which experiments were used, and does the model agree with them? | [ ] |
| Holdout | Out-of-sample error on a period the model never saw? | [ ] |
| Uncertainty | Are ROIs and curves shown with intervals or posterior ranges? | [ ] |
| Stability | Do ROIs change sharply when one quarter is added or removed? | [ ] |
| Marginal versus average | Is the budget recommendation based on marginal returns? | [ ] |
| Base trend | Is the base growing, flat or shrinking, and why? | [ ] |
| Decision | Which reallocation step, and which experiment will check it? | [ ] |

## Connections to other cards

<!-- Guidance: Pick the connection most relevant to your next case and add one line to My notes. -->

- **Core marketing mix:** mba/mba-marketing-mix-card for response curves and the allocation rule that turns this
  card's marginal returns into a budget.
- **Regression core:** mba/mba-regression-for-managers-card for omitted variable bias and collinearity, the two forces
  behind the search error in example A.
- **Experiments:** mba/mba-marketing-experiments-card for the lift tests that calibrate the model.
- **Attribution:** mba/mba-digital-attribution-card for the bottom-up view of digital channels.
- **Pricing and promotions:** mba/mba-pricing-card for the price coefficient and promotion economics.
- **Course map:** mba/mba-marketing-analytics-guide.
- **Statistics library:** academic/stats-time-series-arima (autocorrelation in weekly residuals) and
  academic/stats-bayesian-inference (priors and posteriors behind Bayesian MMM).

## Common mistakes and misconceptions

<!-- Guidance: Tick each mistake you have seen in a real MMM deck. Write the fix in mba/mba-error-log. -->

1. **Trusting a model because $R^2$ and holdout error are good.** Example A fits well and still nearly doubles search's credit.
2. **Ignoring demand-following spend.** Search, promotions and seasonal TV move with demand; without controls or
   calibration their effects are inflated.
3. **Budgeting on average ROI.** Average ROI justifies past spend; the next dollar earns the marginal return.
4. **Leaving out adstock.** TV and brand activity then look weaker than they are, and their effect leaks into the
   following weeks' base.
5. **Treating base sales as untouchable.** Past marketing builds the base; MMM measures only the short and medium term.
6. **Over-reading a precise-looking number** from a model with a dozen channels and two years of data. Ask for ranges.
7. **Refitting every month and chasing the new ROIs.** Instability is a symptom of weak identification, not news.
8. **Calibrating with one experiment and declaring the whole model validated.** Example A: calibrating search moved TV.
9. **Assuming tools decide the answer.** Robyn, Meridian and PyMC-Marketing differ in method; the data and the
   experiments decide what is identifiable.

## Self-test questions

<!-- Guidance: Answer from memory and write the answers down before you check. Repeat after two days and after a week. -->

1. What is base sales, and why does MMM estimate only the increment?
2. Write the geometric adstock recursion. What is the total weight of one dollar of spend at $\theta$ = 0.6?
3. What is the half-life of an adstock with $\theta$ = 0.6?
4. Why does Northfield's first model overstate search?
5. Define ROI and marginal ROI for a channel. Which one should drive next year's allocation?
6. At a 45% gross margin, what marginal ROAS makes the next dollar break even?
7. What does calibration with an experiment do, in one sentence?
8. Why might a model with a holdout MAPE of 3% still be wrong about a channel?
9. Name two differences between MMM and multi-touch attribution.
10. What does the Dorfman-Steiner condition say about the advertising-to-sales ratio?
11. When is the Hill curve preferred to the negative exponential?

## My notes and next steps

<!-- Guidance: Three lines: what you understood, what still confuses you, and which card or data set you will try next. -->

| Date | Note | Next step |
|---|---|---|
| YYYY-MM-DD | [What I understood today] | [Card or exercise to do next] |
| YYYY-MM-DD | *Example:* I can explain why fit does not validate a channel ROI | Do Exercise C part 4, then read the attribution card |
| YYYY-MM-DD | [What still confuses me] | [Question for office hours] |

## Sources and further reading

<!-- Guidance: Cite the primary work when you use a concept in an assignment. Textbooks are named for further reading only. -->

- Hill, A. V. (1910). The possible effects of the aggregation of the molecules of haemoglobin on its dissociation
  curves. *Journal of Physiology*, 40 (Proceedings of the Physiological Society, 22 January 1910).
- Dorfman, R., and Steiner, P. O. (1954). Optimal advertising and optimal quality. *American Economic Review*, 44(5),
  826-836.
- Koyck, L. M. (1954). *Distributed Lags and Investment Analysis*. Amsterdam: North-Holland.
- Little, J. D. C. (1970). Models and managers: the concept of a decision calculus. *Management Science*, 16(8),
  B466-B485.
- Broadbent, S. (1979). One way TV advertisements work. *Journal of the Market Research Society*, 21.
- Hanssens, D. M., Parsons, L. J., and Schultz, R. L. (2001). *Market Response Models: Econometric and Time Series
  Analysis* (2nd ed.). Boston: Kluwer Academic.
- Sethuraman, R., Tellis, G. J., and Briesch, R. A. (2011). How well does advertising work? Generalizations from
  meta-analysis of brand advertising elasticities. *Journal of Marketing Research*, 48(3), 457-471.
- Jin, Y., Wang, Y., Sun, Y., Chan, D., and Koehler, J. (2017). *Bayesian Methods for Media Mix Modeling with Carryover
  and Shape Effects*. Google Inc. technical report.
- Textbooks (further reading, by name only): Hanssens (ed.), *Empirical Generalizations about Marketing Impact*;
  Leeflang, Wieringa, Bijmolt and Pauwels, *Modeling Markets*; Hyndman and Athanasopoulos, *Forecasting: Principles and
  Practice*.
- Open resources: the documentation of Robyn (github.com/facebookexperimental/Robyn, MIT), Meridian
  (github.com/google/meridian, Apache-2.0) and PyMC-Marketing (github.com/pymc-labs/pymc-marketing, Apache-2.0);
  Hyndman and Athanasopoulos, *Forecasting: Principles and Practice*, free online edition at otexts.com.

## Answer key

<!-- Guidance: Check only after writing your own answers. For each miss, write down why your first answer was wrong. -->

**Worked example B.** (1) Week 3 adstock 25.00, contribution 88.5; week 4 adstock 62.50, saturation 0.4647; week 5
contribution 107.4; week 6 saturation 0.1447. (2) Total 849.8; ROI $849.8/150 = 5.67$. (3) Of the week-1 burst's
effect, everything in weeks 2 and 3 (157.4 and 88.5) and part of the later weeks is carry-over; a model without adstock
would attribute those sales to the base or to whatever else ran in those weeks, and would see TV's effect only in the
week of spend. (4) One week. (5) It depends on the window. Within weeks 1 to 6 the even plan is lower (814.0 against
849.8), because its adstock is still building up when the window closes. Including the carry-over after week 6 (30
further weeks of zero spend) the even plan is higher (995.8 against 910.7): spreading spend keeps each week on the
steep part of the saturation curve. Always compare schedules over a window long enough to capture the carry-over.

**Exercise C (parts 1 to 3).** (1) Parameters 0.55, 150 and 50; holdout MAPE 3.03%. (2) TV 1.295 and search 2.545
revenue per dollar of same-week spend: without adstock, TV's later-week effect is lost, and the linear form averages
over the steep and flat parts of each curve. (3) $500.51/(0.4 \times 175) \times e^{-100/70} = 1.714$; $0.45 \times
1.714 = 0.77 < 1$, so not profitable at the margin. Parts 4 and 5 are for your own work.

**Self-test.**

1. The sales the brand would make without current marketing (distribution, brand, season, trend). Only the increment
   responds to the budget decision.
2. $A_t = x_t + \theta A_{t-1}$; total weight $1/(1-0.6) = 2.5$.
3. $\ln 0.5/\ln 0.6 = 1.36$ weeks.
4. Search spend moves with the season (correlation 0.929), so the model cannot separate search from seasonal demand
   and gives search part of the base.
5. ROI is contribution divided by spend over the period; marginal ROI is the extra revenue from the next dollar at
   current spend. The marginal one drives allocation.
6. $1/0.45 = 2.22$.
7. It pulls the model's estimate for a channel towards the causal effect measured by the experiment, through a prior
   or a penalty.
8. Many parameter combinations fit equally well when channels are correlated with each other or with demand; fit
   cannot tell them apart.
9. MMM uses aggregate data on total sales and covers offline channels; attribution uses user-level digital paths and
   divides credit among touchpoints without measuring the base.
10. At the profit maximum, advertising spend divided by sales equals the advertising elasticity divided by the
    (absolute) price elasticity.
11. When response is S-shaped: little effect below a threshold of exposure, then rising, then saturating.

## Revision history

<!-- Guidance: Add a row each time you change the data window, the model form or the calibration evidence. -->

| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [Your name] | First draft from the template |
| 0.2 | YYYY-MM-DD | [Your name] | *Example:* Calibrated TV with the spring regional test |

## Standard and licence note

Based on the market response modelling literature (Koyck 1954; Dorfman and Steiner 1954; Little 1970; Broadbent 1979;
Hanssens, Parsons and Schultz 2001; Jin et al. 2017; Sethuraman, Tellis and Briesch 2011) and the public documentation
of the open-source tools named, checked on 28 October 2026. The text, simulated data and exercises are original and
fictional; no textbook, published case or course material was copied or paraphrased. This template is not affiliated
with or endorsed by any business school, publisher, author, ad platform or software vendor. Check the current
documentation of each tool before you rely on it. Template licence: MIT.
