Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
afelipeg avatar

Mmm Modeling

  • 1 installs
  • 1 repo stars
  • Updated May 7, 2026
  • afelipeg/anthropic-skills-for-enterprise-marketing-os

mmm-modeling is a Claude Code skill that builds Bayesian media mix models to measure channel ROI, incremental contribution and optimize marketing budgets.

About

mmm-modeling is a Claude Code skill for Bayesian Media Mix Modeling. It estimates channel ROI and incremental contribution, builds adstock and saturation curves, calibrates with lift tests, and optimizes budget allocation across channels. A marketing data analyst uses it to attribute sales to media spend and plan budgets. It uses PyMC-Marketing as the primary engine with a custom scipy fallback and bundles data validation and reporting scripts.

  • Bayesian Media Mix Modeling with PyMC-Marketing plus scipy fallback
  • Decomposes sales into base, media and trade contribution
  • Runs budget optimization and scenario planning with acid-test validation

Mmm Modeling by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #1,803 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
  • Data as of Jul 7, 2026 (Skillselion catalog sync)
At a glance

mmm-modeling capabilities & compatibility

Free; runs locally with PyMC-Marketing or a scipy fallback, digital spend optionally pulled via a connected Adspirer MCP.

Capabilities
media mix modeling · marketing attribution · budget optimization · scenario planning
Works with
excel
Use cases
data analysis
Pricing
Free
From the docs

What mmm-modeling says it does

Bayesian Media Mix Modeling using **PyMC-Marketing**
SKILL.md
This skill operates within the 17-skill Agency Growth OS.
SKILL.md
npx skills add https://github.com/afelipeg/anthropic-skills-for-enterprise-marketing-os --skill mmm-modeling

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars1
Last updatedMay 7, 2026
Repositoryafelipeg/anthropic-skills-for-enterprise-marketing-os

What it does

Build Bayesian media mix models to attribute sales to channels and optimize marketing budget allocation.

Who is it for?

Marketing data analysts measuring incremental channel contribution and optimizing media budgets

Skip if: Simple last-click attribution, or teams without spend and sales time-series data

When should I use this skill?

User asks about MMM, channel ROI, budget optimization, incrementality, saturation curves or scenario planning

What you get

Channel ROAS with contribution decomposition, an integrity report and optimized budget allocation

  • channel roas table
  • contribution decomposition
  • integrity report

By the numbers

  • operates within a 17-skill Agency Growth OS
  • 9-dimension acid-test validation suite
  • primary PyMC-Marketing plus scipy fallback

Files

SKILL.mdMarkdownGitHub ↗

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

LayerEngineWhen
Primarypymc-marketing MMM classDefault. Full Bayesian MCMC, geo-hierarchical, lift calibration, HSGP TVP
FallbackCustom scipy (scripts/)When pymc-marketing unavailable. MLE + bootstrap CIs
ScenarioAdapted from Meridian patternsBudget 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 ProducedSuggest Next
Channel ROAS + contribution sharemeasurement-incrementality (validate claims)
Budget optimization allocationmedia-routing-planner (implement in platform)
Integrity report with flagsperformance-diagnosis (investigate anomalies)
Scenario planning deckqbr-generator (embed in quarterly review)
Context brief + model paramsclient-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 TypeFormatUsed As
Sales / KPICSV with date + geo + valueTarget variable (y)
TV / Radio / OOH spendCSV with date + channel + valueMedia channels
Trade marketing (promo, price, ACV)CSV/ExcelControl + trade decomposition
Lift test resultsCSV (channel, x, delta_x, delta_y, sigma)Calibration
Distribution / sell-out (Nielsen/Kantar)CSV/ExcelControl 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/:

FileContentWhen
mmm_results.htmlFull interactive dashboard: contributions over time, saturation curves, waterfall, scenario slidersEvery MMM run
integrity_report.htmlAcid-test results with expandable detail per testEvery MMM run
mmm_scenarios.xlsxScenario comparison data + allocation tablesWhen optimization runs
mmm_executive.pptxSlides: integrity, decomposition, ROAS, scenarios (via pptx skill)When user requests deck
integrity_report.jsonMachine-readable results for client BI integrationEvery 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 FileContentRead When
references/model_specification.mdMMM constructor, adstock/saturation, priors, dims, scaling, prior predictiveSpecifying a new model
references/data_analysis.mdEDA patterns, data format, spend shares, long format for geoPreparing data
references/model_fit.mdFitting, diagnostics checklist, TimeSliceCrossValidator, common issuesFitting and diagnosing
references/media_deep_dive.mdContributions, ROAS, saturation curves, sensitivity, incrementalityPost-fit media analysis
references/budget_optimization.mdMultiDimensionalBudgetOptimizerWrapper, bounds, sweeps, constraintsOptimizing budgets
references/scenario_planning.mdMeridian-inspired scenario planning adapted for PyMC-MarketingForward-looking what-ifs
references/lift_test_calibration.mdadd_lift_test_measurements, data format, sigma estimationCalibrating with experiments
references/time_varying_parameters.mdHSGPKwargs, time-varying intercept/media, when to use TVPNon-stationary effects
references/custom_model.mdStandalone components with plain PyMC, spline baselines, custom likelihoodsBeyond MMM class
references/plot_api.mdComplete mmm.plot namespace with exact signaturesAny visualization
references/diagnostics_benchmarks.mdConvergence thresholds, industry ROI benchmarks, business logic guardsValidating results
references/acid_test_validation.mdPre/post-model integrity tests (Holt-Winters, Granger, VIF, permutation)Validating model truthfulness
references/context_layer.mdIndustry/category/market prior calibration, benchmark resolutionSetting informed priors
references/trade_marketing_decomposition.mdTrade vs media vs base demand separation, flexible schemaCPG/FMCG/Retail decomposition

Scripts

ScriptPurposeDepends On
scripts/data_validator.pyPre-modeling EDA, quality scoring, synthetic data generationpandas, numpy, scipy
scripts/report_generator.pyCoTA-formatted HTML/MD reports with embedded chartsmatplotlib (optional)
scripts/acid_test.pyModel integrity validation (Holt-Winters, Granger, VIF, absorption check)statsmodels
scripts/context_resolver.pyIndustry benchmark resolution → calibrated priorsjson (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

1. Always fit on FULL dataset — train/test splits are ONLY for TimeSliceCrossValidator stability checks 2. Zero divergences required — any divergences invalidate the posterior 3. R-hat < 1.01 for all parameters before proceeding 4. Call `add_original_scale_contribution_variable` before sample_posterior_predictive to get *_original_scale variables 5. Use `mmm.incrementality` for ROAS (accounts for adstock carryover), not element-wise division 6. Use `MultiDimensionalBudgetOptimizerWrapper` not BudgetOptimizer directly (handles geo allocation) 7. Never present platform ROAS as incremental without caveating (Agency Growth OS rule) 8. Search web for current industry benchmarks before comparing ROI results

Diagnostics Quick Reference

MetricTargetCritical
Divergences0Must be 0
R-hat< 1.01All 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.

Related skills

FAQ

What engine does mmm-modeling use?

PyMC-Marketing for full Bayesian MCMC, with a custom scipy-based fallback for restricted environments.

How is the model validated?

An acid-test suite using Holt-Winters, Granger causality and VIF, plus lift-test calibration.

Data Science & MLanalyticspipelines

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.