Bayesian Marketing Mix I modeled five channels to find the one that works. Not by correlation, by adstock and saturation.

A three-year weekly dataset, five media channels, one Ridge and PyMC-Marketing model with adstock decay and Hill saturation curves, checked by holdout, Monte Carlo and time-series cross-validation before it's allowed to say where budget should move.

Stack
Python · scikit-learn · PyMC-Marketing · Pandas
Status
Open source · GitHub ↗︎
  • 0.9489R², 156 modeled weeks
  • 2.74%mean absolute % error
  • 5.5% → 47.3%email budget share

Bayesian Marketing Mix

Python · scikit-learn · PyMCView code
Bayesian Marketing Mix Model: actual-versus-predicted sales and per-channel budget allocation charts

01 · Problem

Which channel actually earned its budget?

TV took the largest share of a three-year, five-channel spend, but a raw correlation between spend and sales can't tell a channel's own effect apart from a trend or a competitor moving at the same time.

02 · Solution

Model each channel's real, saturating effect.

Adstock decay carries a channel's spend forward across the weeks after it runs, and Hill saturation curves cap its return, so the fit separates each channel's own diminishing effect from everything happening around it.

03 · Engineering

A 5-fold time-series split scores every fold forward in time only, never validating a week against data from after it.

04 · Key decisions

  • Synthetic, fixed-seed data, not a real client's
  • Adstock + Hill curves, one per channel
  • 1,000-run Monte Carlo for uncertainty bounds
  • 5-fold time-series CV, never out of order
  • ROAS-weighted optimizer over current spend

Engineering note

The results are real; the money behind them isn't. Every dollar comes from a fixed random seed, not an advertiser's ledger.

generate_data.py builds all 156 weeks with a seeded random generator, so TV's -11.9% ROI and email's +1,152% aren't a real company's numbers, they're what the model recovers when the true relationship is already known. The question is whether the method finds that answer, not the dollar figures.