Exporting MMM data for frontends#
Production dashboards often render charts in JavaScript (React, Dash, Streamlit, custom UIs) rather than serving static matplotlib images from Python. PyMC-Marketing exposes tabular summaries with mean, median, and HDI bounds — the same statistics underlying the plot suite — so your frontend owns presentation.
Issue #2715 originally proposed a return_data=True flag on plot methods. The shipped design is the mmm.summary layer instead: one source of truth for both matplotlib/Plotly plots and JSON export.
Quick start#
After fitting an MMM:
from pymc_marketing.mmm.summary import dataframe_to_json_records
# Pandas records (simple path)
records = mmm.summary.contributions().to_dict(orient="records")
# JSON-safe records (ISO dates, native Python scalars)
df = mmm.summary.contributions()
records = dataframe_to_json_records(df)
Pass records to json.dumps or your API response layer.
Core API: mmm.summary#
mmm.summary returns an MMMSummaryFactory with defaults for HDI levels and output format:
df = mmm.summary.contributions() # default: pandas, 94% HDI
df = mmm.summary.roas(frequency="yearly")
df = mmm.summary.posterior_predictive(
hdi_probs=[0.80, 0.94],
frequency="monthly",
output_format="polars",
)
Common parameters across methods:
hdi_probs— HDI probability levels (default(0.94,))output_format—"pandas"or"polars"frequency— time aggregation ("weekly","monthly","yearly", etc.)dims— dimension filters, e.g.{"geo": ["CA"]}
See the full API: MMMSummaryFactory.
Methods overview#
Method |
What it summarizes |
|---|---|
|
Per-channel / control / seasonality contributions over time |
|
Mean per-period contributions and shares (waterfall decomposition) |
|
Channel share of total contribution |
|
Posterior predictive vs observed |
|
Prior predictive vs observed |
|
Residuals with HDI bands |
|
Residual distribution quantiles |
|
Prior vs posterior density grid |
|
Return on ad spend |
|
Raw spend per channel/date |
|
Saturation response curves |
|
Adstock decay curves |
|
Spend vs saturated effect |
|
Summed contributions by component type |
|
Percentage change between periods |
|
Raw sensitivity sweep |
|
Uplift curves from sensitivity sweep |
|
Marginal effects from sensitivity sweep |
Plot ↔ summary mapping#
If you know the matplotlib plot API, use the matching summary method for tabular export:
Plot entry point |
Summary entry point |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Budget allocation samples#
Budget plots are stateless: pass allocation samples to both plot and summary namespaces.
mmm.plot_suite = "new"
optimizer = BudgetOptimizerWrapper(
model=mmm, start_date="2024-01-01", end_date="2024-12-31"
)
result = optimizer.optimize_budget(budget=100_000)
samples = optimizer.sample_response_distribution(
allocation_strategy=result.budgets
)
df_roas = optimizer.summary.allocation_roas(samples=samples)
df_ts = optimizer.summary.contribution_over_time(samples=samples)
records = df_roas.to_dict(orient="records")
samples is an xr.Dataset from sample_response_distribution() (after optimize_budget()), not the optimization result object itself.
Cross-validation results#
After TimeSliceCrossValidator.run():
cv_idata = cv.run(X, y, mmm=mmm)
df_pred = cv.summary.predictions()
df_stab = cv.summary.param_stability(var_names=["alpha"])
df_crps = cv.summary.crps()
When you already hold an MMMCVPlotSuite, cv.plot.summary exposes the same factory bound to the CV DataTree.
Interactive Plotly path#
mmm.plot_interactive reads from the same summary DataFrames. Use it for quick Plotly exploration, or call mmm.summary directly when you need raw tables for a custom frontend.
Release notes (copy-paste)#
Use these bullets in GitHub release notes under New Features:
Expanded
mmm.summarywith decomposition, diagnostics, sensitivity, and transformation summaries as JSON-serializable DataFrames for custom frontendsAdded
optimizer.summaryandcv.plot.summaryparity with budget and CV plot APIsNew guide: Exporting MMM data for frontends