Mmm modeling
Skill afelipeg/Anthropic-Skills-for-enterprise-marketing-os/skills/mmm-modeling
30 connected Claude Skills for enterprise marketing ops. Install in-house to replace fragmented tools or reclaim outsourced operations. Marketing & Comms [working & non-working media]· CRM & Growth · Shopper & Trade · RGM · Finance.
npx -y skills add afelipeg/Anthropic-Skills-for-enterprise-marketing-os --skill mmm-modelingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Bayesian Media Mix Modeling with PyMC-Marketing + custom fallback. Acid-test validation (Holt-Winters, Granger, VIF), industry-calibrated priors, trade marketing decomposition, Meridian scenario planning, budget optimization. TRIGGERS: MMM, marketing mix model, media mix, ROI, marketing attribution, budget optimization, channel contribution, adstock, saturation curve, incrementality, ROAS, scenario planning, lift test, sensitivity analysis, marginal ROI, diminishing returns, media-driven sales, trade marketing Use when: analyzing channel ROI, optimizing budgets, measuring incremental contribution, building saturation curves, scenario planning, decomposing KPI drivers (base+media+trade), calibrating with lift tests, geo-hierarchical modeling, validating MMM integrity
SKILL.md
17.7 KB, as published. Nobody here has run it
Media Mix Modeling (MMM) Skill
Bayesian Media Mix Modeling using PyMC-Marketing (primary) with a custom scipy-based fallback for restricted environments. Includes Meridian-inspired scenario planning patterns for forward-looking budget optimization.
Architecture
| Layer | Engine | When |
|---|---|---|
| Primary | pymc-marketing MMM class | Default. Full Bayesian MCMC, geo-hierarchical, lift calibration, HSGP TVP |
| Fallback | Custom scipy (scripts/) | When pymc-marketing unavailable. MLE + bootstrap CIs |
| Scenario | Adapted from Meridian patterns | Budget sweeps, fixed/flexible optimization, flighting |
Detection logic — try import pymc_marketing first; if ImportError, fall back to custom scripts.
Agency Growth OS Integration
This skill operates within the 17-skill Agency Growth OS. Its position in the three cycles:
Execution Cycle
media-routing-planner → mmm-modeling → measurement-incrementality
↘ budget_optimization → media-routing-planner (re-optimize)
Intelligence Cycle
weekly-control-tower → performance-diagnosis → mmm-modeling (if root cause is channel mix)
mmm-modeling → qbr-generator (quarterly decomposition + scenario slides)
mmm-modeling → client-memory-synthesizer (store model parameters + integrity score)
Chain Routing
After mmm-modeling completes, suggest the next skill via sendPrompt():
| Output Produced | Suggest Next |
|---|---|
| Channel ROAS + contribution share | measurement-incrementality (validate claims) |
| Budget optimization allocation | media-routing-planner (implement in platform) |
| Integrity report with flags | performance-diagnosis (investigate anomalies) |
| Scenario planning deck | qbr-generator (embed in quarterly review) |
| Context brief + model params | client-memory-synthesizer (persist to tenant) |
Data Ingestion
Digital media data → Adspirer MCP (automated)
When the model needs digital campaign spend data (Google Ads, Meta, LinkedIn, TikTok), call the Adspirer MCP tools already connected:
# Claude calls Adspirer tools to pull spend data:
# - get_campaign_performance → spend by channel × date
# - get_campaign_structure → channel taxonomy
# - analyze_search_terms → search query volume (control variable)
Use tool_search for Adspirer tools when the user mentions digital spend data. The skill does NOT embed Adspirer tool calls directly — Claude resolves them at runtime via the connected MCP.
Non-digital data → Human batch upload (CSV/Excel)
Data the human must provide (no MCP available):
| Data Type | Format | Used As |
|---|---|---|
| Sales / KPI | CSV with date + geo + value | Target variable (y) |
| TV / Radio / OOH spend | CSV with date + channel + value | Media channels |
| Trade marketing (promo, price, ACV) | CSV/Excel | Control + trade decomposition |
| Lift test results | CSV (channel, x, delta_x, delta_y, sigma) | Calibration |
| Distribution / sell-out (Nielsen/Kantar) | CSV/Excel | Control variables |
Workflow: Human uploads → data_validator.py validates → context layer resolves priors → model fits.
Visualization Strategy
This skill ALWAYS produces visual deliverables. The strategy controls WHERE they render to avoid crashing the app.
In-chat: Tables + lightweight SVG (always)
Every MMM run produces formatted tables in chat as immediate output:
- Channel ROAS with HDI ranges (table)
- Integrity scorecard pass/warn/fail (table)
- Contribution share % breakdown (table)
- Scenario comparison (table)
For visual charts in chat, use the Visualizer with lightweight SVG only:
- Max 6 channels × 50 data points per chart
- No animations, no JS interactivity
- Keep total SVG under 50KB
- One chart per Visualizer call (never stack multiple)
Suitable for inline SVG:
- Horizontal bar: contribution waterfall, ROAS comparison
- Line chart: saturation curves (≤6 lines), efficient frontier
- Donut: four-way decomposition (base/media/trade/interaction)
- Status indicators: integrity scorecard with color-coded pass/warn/fail
File deliverables: Full charts + dashboards (always generated)
After in-chat summary, ALWAYS generate file artifacts in /mnt/user-data/outputs/:
| File | Content | When |
|---|---|---|
mmm_results.html | Full interactive dashboard: contributions over time, saturation curves, waterfall, scenario sliders | Every MMM run |
integrity_report.html | Acid-test results with expandable detail per test | Every MMM run |
mmm_scenarios.xlsx | Scenario comparison data + allocation tables | When optimization runs |
mmm_executive.pptx | Slides: integrity, decomposition, ROAS, scenarios (via pptx skill) | When user requests deck |
integrity_report.json | Machine-readable results for client BI integration | Every MMM run |
Chart generation in HTML files
HTML dashboards use inline <svg> or lightweight Chart.js (CDN) — NOT matplotlib, NOT plotly, NOT heavy React state. This ensures they open fast in browser without crashing.
<!-- Pattern for HTML dashboard charts -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<canvas id="saturation_chart"></canvas>
<script>
new Chart(document.getElementById('saturation_chart'), {
type: 'line',
data: { /* from mmm results JSON */ },
options: { responsive: true, animation: false }
});
</script>
PPTX charts
Use the pptx skill to embed static chart images in slides. Generate chart as PNG via matplotlib in script → embed in slide. Charts render server-side, no app crash risk.
Quick Start (PyMC-Marketing)
import arviz as az
import numpy as np
import pandas as pd
from pymc_extras.prior import Prior
from pymc_marketing.mmm import GeometricAdstock, LogisticSaturation
from pymc_marketing.mmm.multidimensional import MMM
data_df = pd.read_csv("data.csv", parse_dates=["date"])
X = data_df.drop(columns=["y"])
y = data_df["y"]
mmm = MMM(
date_column="date",
channel_columns=["tv", "radio", "social"],
target_column="y",
adstock=GeometricAdstock(l_max=6),
saturation=LogisticSaturation(),
yearly_seasonality=5,
)
# CRITICAL: Always fit on FULL dataset. Train/test splits are ONLY for stability assessment.
mmm.build_model(X, y)
mmm.fit(X=X, y=y, nuts_sampler="nutpie", target_accept=0.9, random_seed=42)
mmm.sample_posterior_predictive(X=X, random_seed=42)
Quick Start (Custom Fallback)
# Validate data
python scripts/data_validator.py data.csv
# Generate report from results JSON
python scripts/report_generator.py model.json --roi roi.json --html
Reference Architecture
| Reference File | Content | Read When |
|---|---|---|
references/model_specification.md | MMM constructor, adstock/saturation, priors, dims, scaling, prior predictive | Specifying a new model |
references/data_analysis.md | EDA patterns, data format, spend shares, long format for geo | Preparing data |
references/model_fit.md | Fitting, diagnostics checklist, TimeSliceCrossValidator, common issues | Fitting and diagnosing |
references/media_deep_dive.md | Contributions, ROAS, saturation curves, sensitivity, incrementality | Post-fit media analysis |
references/budget_optimization.md | MultiDimensionalBudgetOptimizerWrapper, bounds, sweeps, constraints | Optimizing budgets |
references/scenario_planning.md | Meridian-inspired scenario planning adapted for PyMC-Marketing | Forward-looking what-ifs |
references/lift_test_calibration.md | add_lift_test_measurements, data format, sigma estimation | Calibrating with experiments |
references/time_varying_parameters.md | HSGPKwargs, time-varying intercept/media, when to use TVP | Non-stationary effects |
references/custom_model.md | Standalone components with plain PyMC, spline baselines, custom likelihoods | Beyond MMM class |
references/plot_api.md | Complete mmm.plot namespace with exact signatures | Any visualization |
references/diagnostics_benchmarks.md | Convergence thresholds, industry ROI benchmarks, business logic guards | Validating results |
references/acid_test_validation.md | Pre/post-model integrity tests (Holt-Winters, Granger, VIF, permutation) | Validating model truthfulness |
references/context_layer.md | Industry/category/market prior calibration, benchmark resolution | Setting informed priors |
references/trade_marketing_decomposition.md | Trade vs media vs base demand separation, flexible schema | CPG/FMCG/Retail decomposition |
Scripts
| Script | Purpose | Depends On |
|---|---|---|
scripts/data_validator.py | Pre-modeling EDA, quality scoring, synthetic data generation | pandas, numpy, scipy |
scripts/report_generator.py | CoTA-formatted HTML/MD reports with embedded charts | matplotlib (optional) |
scripts/acid_test.py | Model integrity validation (Holt-Winters, Granger, VIF, absorption check) | statsmodels |
scripts/context_resolver.py | Industry benchmark resolution → calibrated priors | json (no deps) |
Model Specification (PyMC-Marketing)
Transformations
GeometricAdstock — exponential decay carryover:
from pymc_marketing.mmm import GeometricAdstock
adstock = GeometricAdstock(l_max=6, normalize=True)
# Default prior: alpha ~ Beta(1, 3) — favors fast decay
# Custom: priors={"alpha": Prior("Beta", alpha=2, beta=5, dims="channel")}
LogisticSaturation — S-shaped diminishing returns:
from pymc_marketing.mmm import LogisticSaturation
saturation = LogisticSaturation()
# Default: lam ~ Gamma(3, 1), beta ~ HalfNormal(2)
# Informed: priors={"beta": Prior("HalfNormal", sigma=spend_shares, dims="channel")}
Full Model Config
from pymc_extras.prior import Prior
model_config = {
"intercept": Prior("Normal", mu=0.2, sigma=0.05),
"saturation_beta": Prior("HalfNormal", sigma=spend_shares, dims="channel"),
"gamma_control": Prior("Normal", mu=0, sigma=1, dims="control"),
"gamma_fourier": Prior("Laplace", mu=0, b=1, dims="fourier_mode"),
"likelihood": Prior("TruncatedNormal", lower=0, sigma=Prior("HalfNormal", sigma=1)),
}
See references/model_specification.md for constructor reference, all saturation alternatives (Hill, MichaelisMenten, Tanh, Root, etc.), hierarchical prior patterns, and scaling config.
Multidimensional (Geo-Hierarchical)
Activate with dims=("geo",). Use partial pooling (default recommendation):
from pymc_marketing.special_priors import LogNormalPrior
model_config = {
"saturation_beta": LogNormalPrior(
mean=Prior("Gamma", mu=1.0, sigma=1.0),
std=Prior("HalfNormal", sigma=1.0),
dims=("channel", "geo"), centered=False,
),
}
Workflow
Client Brief (industry, category, market, channels, KPI)
↓
Context Resolution (context_resolver.py → calibrated priors)
↓
EDA & Data Prep (data_validator.py)
↓
Acid-Test Pre-Model (acid_test.py → integrity baseline)
↓
Model Specification (priors from context layer)
↓
Build Model (mmm.build_model)
↓
Prior Predictive Checks
↓
[Optional] Add Lift Test Calibration
↓
[Optional] Add Trade Marketing Variables (see trade_marketing_decomposition.md)
↓
Fit on FULL Dataset (mmm.fit with nutpie)
↓
Diagnostics (divergences=0, R-hat<1.01, ESS>400)
↓
Acid-Test Post-Model (absorption check, ROI plausibility, permutation)
↓
Media Deep Dive (contributions, ROAS, saturation, sensitivity)
↓
Four-Way Decomposition (base + media + trade + interaction)
↓
Budget Optimization + Scenario Planning
↓
Report Generation (HTML dashboard + PPTX deck + JSON API)
Key APIs
Incrementality (preferred for ROAS/CAC)
roas = mmm.incrementality.contribution_over_spend(frequency="all_time")
marginal_roas = mmm.incrementality.marginal_contribution_over_spend(frequency="all_time", spend_increase_pct=0.01)
cac = mmm.incrementality.spend_over_contribution(frequency="quarterly")
Summary DataFrames
mmm.summary.posterior_predictive() # mean, median, HDI, observed
mmm.summary.contributions() # per-channel contributions
mmm.summary.roas() # ROAS with HDI
mmm.summary.saturation_curves() # saturation response
mmm.summary.adstock_curves() # decay profiles
Budget Optimization
from pymc_marketing.mmm.multidimensional import MultiDimensionalBudgetOptimizerWrapper
optimizer = MultiDimensionalBudgetOptimizerWrapper(model=mmm, start_date=..., end_date=...)
allocation, result = optimizer.optimize_budget(budget=1_000_000, budget_bounds=bounds)
response = optimizer.sample_response_distribution(allocation_strategy=allocation, include_carryover=True)
See references/budget_optimization.md for bounds setup, channel fixing, custom constraints, and budget sweeps.
Scenario Planning
See references/scenario_planning.md for Meridian-inspired patterns adapted for PyMC-Marketing:
- Fixed budget optimization (maximize ROI at given budget)
- Flexible budget optimization (find max budget at target ROI)
- Budget sweeps with efficient frontier
- Flighting / temporal distribution
- Multi-scenario comparison (conservative/moderate/aggressive)
- Cost-per-media-unit sensitivity
Save/Load
mmm.save("mmm_model.nc", engine="h5netcdf")
loaded = MMM.load("mmm_model.nc")
YAML Specification
from pymc_marketing.mmm.builders.yaml import build_mmm_from_yaml
mmm = build_mmm_from_yaml("model_spec.yaml", X=X, y=y)
Layer 1: Acid-Test Validation
Runs pre/post-model integrity checks to verify MMM truthfulness. This is how you expose holdco manipulation.
from scripts.acid_test import AcidTestValidator
# Pre-model (before fitting)
validator = AcidTestValidator(df, "date", "sales", ["tv", "social", "search"])
pre_report = validator.run_pre_model_tests()
# Post-model (after fitting, with MMM results)
post_report = validator.run_post_model_tests({
"r2": 0.82,
"channel_contributions": {"tv": 500000, "social": 200000, "search": 300000},
"channel_roas": {"tv": 2.1, "social": 1.5, "search": 3.2},
"baseline_pct": 0.55,
})
print(validator.to_summary())
validator.to_json("integrity_report.json")
Key tests: Holt-Winters baseline, Granger causality, VIF, baseline absorption check, ROI plausibility. See references/acid_test_validation.md.
Layer 2: Context Layer
Resolves industry/category/market benchmarks into calibrated priors:
from scripts.context_resolver import ContextResolver
resolver = ContextResolver(
industry="CPG", category="Beverages", market="Mexico",
channels=["tv", "social", "search", "ooh"],
distribution_model="indirect", trade_marketing_share=0.45,
)
brief = resolver.to_json("context_brief.json")
print(resolver.to_summary())
# Use resolved priors in model config
# brief.suggested_model_config → ready-to-use Prior strings
# brief.roi_benchmarks → feeds acid-test ROI plausibility check
# brief.adstock_priors → calibrated Beta distributions per channel
See references/context_layer.md for full benchmark databases and adaptation rules.
Layer 3: Trade Marketing Decomposition
Four-way decomposition: base demand + media-driven + trade-driven + interaction.
Add trade variables as controls with domain-informed priors:
control_columns = ["promo_depth", "price_index", "distribution_acv", "feature_flag"]
model_config = {
"saturation_beta": Prior("HalfNormal", sigma=spend_shares, dims="channel"),
"gamma_control": Prior("Normal", mu=trade_prior_means, sigma=trade_prior_sds, dims="control"),
}
For sophisticated decomposition with custom trade response curves, use a custom PyMC model. See references/trade_marketing_decomposition.md for flexible data schema (Tier 1-4), functional forms, and the holdco exposure play.
Critical Rules
- Always fit on FULL dataset — train/test splits are ONLY for
TimeSliceCrossValidatorstability checks - Zero divergences required — any divergences invalidate the posterior
- R-hat < 1.01 for all parameters before proceeding
- Call
add_original_scale_contribution_variablebeforesample_posterior_predictiveto get*_original_scalevariables - Use
mmm.incrementalityfor ROAS (accounts for adstock carryover), not element-wise division - Use
MultiDimensionalBudgetOptimizerWrappernotBudgetOptimizerdirectly (handles geo allocation) - Never present platform ROAS as incremental without caveating (Agency Growth OS rule)
- Search web for current industry benchmarks before comparing ROI results
Diagnostics Quick Reference
| Metric | Target | Critical |
|---|---|---|
| Divergences | 0 | Must be 0 |
| R-hat | < 1.01 | All params |
| ESS (bulk) | > 400 | > 800 preferred |
| Posterior R² | > 0.70 | > 0.80 preferred |
| Baseline % | 20-80% | Flags over/under-attribution |
See references/diagnostics_benchmarks.md for full checklist and industry ROI ranges.