Reporting

The reporting module provides HTML report generation for MMM results with interactive Plotly visualizations.

Report Generator

Main report generator for MMM reports.

Assembles sections, applies styling, and produces portable HTML output.

class mmm_framework.reporting.generator.MMMReportGenerator(model=None, data=None, config=None, panel=None, results=None, sensitivity=None, llm=None, allocation=None, pacing=None, triangulation=None, spec_curve=None, cfo=None)[source]

Bases: object

Generate portable HTML reports from Bayesian MMM results.

Parameters

modelAny, optional

Fitted MMM model (BayesianMMM, NestedMMM, MultivariateMMM, etc.)

dataMMMDataBundle, optional

Pre-extracted data bundle (alternative to model)

configReportConfig, optional

Report configuration

panelAny, optional

Panel dataset used for fitting

resultsAny, optional

Model fit results

sensitivitydict, optional

Sensitivity analysis results

Examples

>>> from mmm_framework import BayesianMMM
>>> from mmm_reporting import MMMReportGenerator, ReportConfig
>>>
>>> # Fit model
>>> mmm = BayesianMMM(panel, model_config, trend_config)
>>> results = mmm.fit()
>>>
>>> # Generate report
>>> report = MMMReportGenerator(
...     model=mmm,
...     config=ReportConfig(
...         title="Q4 2025 Marketing Analysis",
...         client="Acme Corp",
...         analysis_period="Jan 2023 - Dec 2025",
...     )
... )
>>> report.to_html("mmm_report.html")
__init__(model=None, data=None, config=None, panel=None, results=None, sensitivity=None, llm=None, allocation=None, pacing=None, triangulation=None, spec_curve=None, cfo=None)[source]
add_section(section_type, section_config=None, position=None)[source]

Add a section to the report.

Return type:

MMMReportGenerator

Parameters

section_typestr

Type of section from SECTION_REGISTRY

section_configSectionConfig, optional

Configuration for the section

positionint, optional

Position to insert (None = append)

Returns

MMMReportGenerator

Self for chaining

remove_section(section_id)[source]

Remove a section by ID.

Return type:

MMMReportGenerator

Parameters

section_idstr

Section ID to remove

Returns

MMMReportGenerator

Self for chaining

render()[source]

Render complete HTML report.

Return type:

str

Returns

str

Complete HTML document

to_html(filepath)[source]

Save report to HTML file.

Return type:

Path

Parameters

filepathstr or Path

Output file path

Returns

Path

Path to saved file

to_string()[source]

Get report as HTML string.

Return type:

str

Returns

str

HTML document string

class mmm_framework.reporting.generator.ReportBuilder[source]

Bases: object

Fluent builder for creating customized reports.

Examples

>>> report = (
...     ReportBuilder()
...     .with_model(mmm)
...     .with_title("Q4 Analysis")
...     .with_client("Acme Corp")
...     .enable_all_sections()
...     .disable_section("diagnostics")
...     .with_credible_interval(0.9)
...     .build()
... )
__init__()[source]
with_llm(llm)[source]

Attach a LangChain chat model used to enrich CMO/planner narrative in the Augur readout (best-effort; templated fallback otherwise).

Return type:

ReportBuilder

with_insights(insights)[source]

Pre-supply CMO/planner narrative slots (skips insight generation).

Return type:

ReportBuilder

augur_readout()[source]

Configure the editorial “Media Performance Readout” (Augur) shell.

Sets the Augur palette + cream/ink shell, a numbered sticky contents nav, a confidentiality notice and channel-name formatting. The Augur section set (headline → reallocation → deep dives → posterior-predictive fit & checks → tests → next steps) is selected by shell == "augur".

Return type:

ReportBuilder

with_model(model, panel=None, results=None)[source]

Set the MMM model.

Return type:

ReportBuilder

with_data(data)[source]

Set pre-extracted data bundle.

Return type:

ReportBuilder

with_sensitivity(results)[source]

Add sensitivity analysis results.

Return type:

ReportBuilder

with_allocation(plan)[source]

Attach a budget-allocation plan (a default reallocation from planning.default_reallocation(), or a saved Planner plan).

The plan is exposed on the report bundle as allocation_results and turns the allocation section ON (classic and Augur). A falsy/empty plan is ignored, so the section stays off. The plan dict shape is the one the plan_budget op / AllocationSection consume.

Return type:

ReportBuilder

with_title(title)[source]

Set report title.

Return type:

ReportBuilder

with_client(client)[source]

Set client name.

Return type:

ReportBuilder

with_subtitle(subtitle)[source]

Set subtitle.

Return type:

ReportBuilder

with_analysis_period(period)[source]

Set analysis period string.

Return type:

ReportBuilder

with_color_scheme(scheme)[source]

Set color scheme.

Return type:

ReportBuilder

with_channel_colors(channel_colors)[source]

Set per-channel chart colors (a ChannelColors instance).

Return type:

ReportBuilder

with_credible_interval(prob)[source]

Set default credible interval (e.g., 0.8 for 80% CI).

Return type:

ReportBuilder

enable_section(section_name, **kwargs)[source]

Enable a section with optional configuration.

Return type:

ReportBuilder

disable_section(section_name)[source]

Disable a section.

Return type:

ReportBuilder

enable_all_sections()[source]

Enable all sections.

Return type:

ReportBuilder

minimal_report()[source]

Configure for minimal report (executive summary + ROI only).

Return type:

ReportBuilder

client_report()[source]

Configure a clean, client-ready report.

Includes: Executive Summary, Channel Performance, Decomposition, Saturation & Carryover, and a simplified Methodology note. Excludes: Diagnostics (trace plots / prior-posterior) and Sensitivity.

Also enables sticky side-nav, confidentiality notice, and automatic channel-name formatting (underscores → spaces, title-case).

Return type:

ReportBuilder

build()[source]

Build the report generator.

Return type:

MMMReportGenerator

Configuration

Configuration classes for MMM report generation.

Provides immutable, validated configuration for report customization.

class mmm_framework.reporting.config.ColorPalette(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Pre-defined color palettes for reports.

SAGE = 'sage'
CORPORATE = 'corporate'
WARM = 'warm'
MONOCHROME = 'monochrome'
AUGUR = 'augur'
class mmm_framework.reporting.config.ColorScheme(primary='#8fa86a', primary_dark='#6d8a4a', accent='#6a8fa8', accent_dark='#4a6d8a', warning='#d4a86a', danger='#c97067', success='#6abf8a', text='#2d3a2d', text_muted='#5a6b5a', background='#fafbf9', background_alt='#f0f2ed', surface='#ffffff', border='#d4ddd4', font_sans='Source Sans 3, sans-serif')[source]

Bases: object

Color scheme for report styling.

primary: str = '#8fa86a'
primary_dark: str = '#6d8a4a'
accent: str = '#6a8fa8'
accent_dark: str = '#4a6d8a'
warning: str = '#d4a86a'
danger: str = '#c97067'
success: str = '#6abf8a'
text: str = '#2d3a2d'
text_muted: str = '#5a6b5a'
background: str = '#fafbf9'
background_alt: str = '#f0f2ed'
surface: str = '#ffffff'
border: str = '#d4ddd4'
font_sans: str = 'Source Sans 3, sans-serif'
classmethod from_palette(palette)[source]

Create color scheme from a named palette.

Return type:

ColorScheme

__init__(primary='#8fa86a', primary_dark='#6d8a4a', accent='#6a8fa8', accent_dark='#4a6d8a', warning='#d4a86a', danger='#c97067', success='#6abf8a', text='#2d3a2d', text_muted='#5a6b5a', background='#fafbf9', background_alt='#f0f2ed', surface='#ffffff', border='#d4ddd4', font_sans='Source Sans 3, sans-serif')
class mmm_framework.reporting.config.ChannelColors(colors=<factory>)[source]

Bases: object

Color mapping for media channels.

colors: dict[str, str]
get(channel)[source]

Get color for channel, with fallback to hash-based color.

Return type:

str

with_channel(channel, color)[source]

Return new ChannelColors with added/updated channel color.

Return type:

ChannelColors

__init__(colors=<factory>)
class mmm_framework.reporting.config.SectionConfig(enabled=True, title=None, subtitle=None, show_uncertainty=True, credible_interval=0.8, chart_height=400, chart_width=None, custom_notes=None)[source]

Bases: object

Configuration for individual report sections.

enabled: bool = True
title: str | None = None
subtitle: str | None = None
show_uncertainty: bool = True
credible_interval: float = 0.8
chart_height: int = 400
chart_width: int | None = None
custom_notes: str | None = None
with_updates(**kwargs)[source]

Return new config with updates.

Return type:

SectionConfig

__init__(enabled=True, title=None, subtitle=None, show_uncertainty=True, credible_interval=0.8, chart_height=400, chart_width=None, custom_notes=None)
class mmm_framework.reporting.config.ReportConfig(title='Marketing Mix Model Report', subtitle=None, client=None, analysis_period=None, generated_date=None, model_version=None, framework_version=None, color_scheme=<factory>, channel_colors=<factory>, font_family_serif="'DM Serif Display', serif", font_family_sans="'Inter', -apple-system, BlinkMacSystemFont, sans-serif", font_family_mono="'JetBrains Mono', monospace", shell='default', cmo_insights=<factory>, default_credible_interval=0.8, currency_symbol='$', currency_format=', .0f', percentage_format='.1%', decimal_format='.2f', large_number_format='short', executive_summary=<factory>, model_fit=<factory>, posterior_predictive=<factory>, estimands=<factory>, channel_roi=<factory>, decomposition=<factory>, saturation=<factory>, sensitivity=<factory>, pacing=<factory>, long_term=<factory>, triangulation=<factory>, spec_curve=<factory>, cfo=<factory>, causal_assumptions=<factory>, methodology=<factory>, diagnostics=<factory>, geographic=<factory>, mediators=<factory>, cannibalization=<factory>, factor_analysis=<factory>, headline=<factory>, marginal_returns=<factory>, reallocation=<factory>, flighting=<factory>, deep_dives=<factory>, carryover=<factory>, ppc_timeseries=<factory>, recommended_tests=<factory>, evidence_guide=<factory>, next_steps=<factory>, allocation=<factory>, include_plotly_js=True, plotly_cdn_version='2.27.0', include_print_styles=True, minify_html=False, uncertainty_callout=True, methodology_note=True, long_term_multiplier=None, show_nav=False, confidential=False, format_channel_names=False)[source]

Bases: object

Complete configuration for report generation.

title: str = 'Marketing Mix Model Report'
subtitle: str | None = None
client: str | None = None
analysis_period: str | None = None
generated_date: str | None = None
model_version: str | None = None
framework_version: str | None = None
color_scheme: ColorScheme
channel_colors: ChannelColors
font_family_serif: str = "'DM Serif Display', serif"
font_family_sans: str = "'Inter', -apple-system, BlinkMacSystemFont, sans-serif"
font_family_mono: str = "'JetBrains Mono', monospace"
shell: Literal['default', 'augur'] = 'default'
cmo_insights: dict[str, str]
default_credible_interval: float = 0.8
currency_symbol: str = '$'
currency_format: str = ',.0f'
percentage_format: str = '.1%'
decimal_format: str = '.2f'
large_number_format: Literal['short', 'full', 'scientific'] = 'short'
executive_summary: SectionConfig
model_fit: SectionConfig
posterior_predictive: SectionConfig
estimands: SectionConfig
channel_roi: SectionConfig
decomposition: SectionConfig
saturation: SectionConfig
sensitivity: SectionConfig
pacing: SectionConfig
long_term: SectionConfig
triangulation: SectionConfig
spec_curve: SectionConfig
cfo: SectionConfig
causal_assumptions: SectionConfig
methodology: SectionConfig
diagnostics: SectionConfig
geographic: SectionConfig
mediators: SectionConfig
cannibalization: SectionConfig
factor_analysis: SectionConfig
headline: SectionConfig
marginal_returns: SectionConfig
reallocation: SectionConfig
flighting: SectionConfig
deep_dives: SectionConfig
carryover: SectionConfig
ppc_timeseries: SectionConfig
recommended_tests: SectionConfig
evidence_guide: SectionConfig
next_steps: SectionConfig
allocation: SectionConfig
include_plotly_js: bool = True
plotly_cdn_version: str = '2.27.0'
include_print_styles: bool = True
minify_html: bool = False
uncertainty_callout: bool = True
methodology_note: bool = True
long_term_multiplier: float | None = None
show_nav: bool = False
confidential: bool = False
format_channel_names: bool = False
classmethod minimal(title='MMM Report', client=None)[source]

Create minimal report with only essential sections.

Return type:

ReportConfig

classmethod full(title='Marketing Mix Model Report', client=None)[source]

Create comprehensive report with all sections.

Return type:

ReportConfig

classmethod presentation(title='MMM Results', client=None)[source]

Create presentation-focused report optimized for stakeholders.

Return type:

ReportConfig

classmethod augur_readout(title='Media Performance Readout', client=None, *, cmo_insights=None)[source]

Create the editorial “Media Performance Readout” (Augur) report.

A narrative, evidence-coded client deliverable: masthead + numbered contents nav, a headline with a KPI strip and recommendations, a channel scorecard with Scale/Test/Hold/Reduce tiers, ROI-with-uncertainty, marginal-vs-average return, saturation, reallocation, per-channel deep dives, carryover, a posterior-predictive fit-over-time + checks section, and recommended tests / next steps. CMO/planner narrative is filled by reporting.insights.build_report_insights (templated fallback + optional LLM enrichment).

Return type:

ReportConfig

format_currency(value)[source]

Format value as currency.

Return type:

str

format_percentage(value)[source]

Format value as percentage.

Return type:

str

format_decimal(value)[source]

Format decimal value.

Return type:

str

__init__(title='Marketing Mix Model Report', subtitle=None, client=None, analysis_period=None, generated_date=None, model_version=None, framework_version=None, color_scheme=<factory>, channel_colors=<factory>, font_family_serif="'DM Serif Display', serif", font_family_sans="'Inter', -apple-system, BlinkMacSystemFont, sans-serif", font_family_mono="'JetBrains Mono', monospace", shell='default', cmo_insights=<factory>, default_credible_interval=0.8, currency_symbol='$', currency_format=', .0f', percentage_format='.1%', decimal_format='.2f', large_number_format='short', executive_summary=<factory>, model_fit=<factory>, posterior_predictive=<factory>, estimands=<factory>, channel_roi=<factory>, decomposition=<factory>, saturation=<factory>, sensitivity=<factory>, pacing=<factory>, long_term=<factory>, triangulation=<factory>, spec_curve=<factory>, cfo=<factory>, causal_assumptions=<factory>, methodology=<factory>, diagnostics=<factory>, geographic=<factory>, mediators=<factory>, cannibalization=<factory>, factor_analysis=<factory>, headline=<factory>, marginal_returns=<factory>, reallocation=<factory>, flighting=<factory>, deep_dives=<factory>, carryover=<factory>, ppc_timeseries=<factory>, recommended_tests=<factory>, evidence_guide=<factory>, next_steps=<factory>, allocation=<factory>, include_plotly_js=True, plotly_cdn_version='2.27.0', include_print_styles=True, minify_html=False, uncertainty_callout=True, methodology_note=True, long_term_multiplier=None, show_nav=False, confidential=False, format_channel_names=False)
class mmm_framework.reporting.config.ChartConfig(height=400, width=None, margin=<factory>, show_legend=True, legend_position='top', animation=True, responsive=True, show_credible_intervals=True, ci_alpha=0.2, ci_level=0.8, x_title=None, y_title=None, x_tickformat=None, y_tickformat=None)[source]

Bases: object

Configuration for individual charts.

height: int = 400
width: int | None = None
margin: dict[str, int]
show_legend: bool = True
legend_position: Literal['top', 'bottom', 'right', 'left'] = 'top'
animation: bool = True
responsive: bool = True
show_credible_intervals: bool = True
ci_alpha: float = 0.2
ci_level: float = 0.8
x_title: str | None = None
y_title: str | None = None
x_tickformat: str | None = None
y_tickformat: str | None = None
to_plotly_layout(color_scheme)[source]

Convert to Plotly layout dict.

Return type:

dict[str, Any]

__init__(height=400, width=None, margin=<factory>, show_legend=True, legend_position='top', animation=True, responsive=True, show_credible_intervals=True, ci_alpha=0.2, ci_level=0.8, x_title=None, y_title=None, x_tickformat=None, y_tickformat=None)

Sections

Report section renderers for MMM reports.

Each section is a modular component that can be enabled/disabled and customized independently.

class mmm_framework.reporting.sections.Section(data, config, section_config=None)[source]

Bases: object

Base class for report sections.

section_id: str = 'section'
default_title: str = 'Section'
__init__(data, config, section_config=None)[source]
property title: str
property is_enabled: bool
render()[source]
Return type:

str

property is_frequentist: bool

True when the fit behind this report produced no posterior.

property interval_noun: str

"credible interval" / "confidence interval".

Every section that names its interval reads this instead of writing “credible” literally. A bootstrap percentile interval describes the sampling variability of an estimator; the probability statement a credible interval licenses is simply false for it.

interval_phrase(ci_level)[source]

e.g. "90% credible interval" / "90% bootstrap confidence interval".

Return type:

str

class mmm_framework.reporting.sections.ExecutiveSummarySection(data, config, section_config=None)[source]

Bases: Section

Executive summary with key metrics and uncertainty callouts.

section_id: str = 'executive-summary'
default_title: str = 'Executive Summary'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.ModelFitSection(data, config, section_config=None)[source]

Bases: Section

Model fit visualization with actual vs predicted.

UPDATED: Now includes geo-level dropdown selector when geo data is available. The default view shows aggregated (total) model fit, with option to drill down to individual geographies.

section_id: str = 'model-fit'
default_title: str = 'Model Fit'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.ChannelROISection(data, config, section_config=None)[source]

Bases: Section

Channel ROI analysis with forest plot.

section_id: str = 'channel-roi'
default_title: str = 'Channel Performance'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.DecompositionSection(data, config, section_config=None)[source]

Bases: Section

Revenue decomposition showing contribution of each component.

UPDATED: Now includes geo-level dropdown selector when geo data is available. Features both stacked area (time series) and waterfall (total contribution) views, each with independent geo selectors.

section_id: str = 'decomposition'
default_title: str = 'Revenue Decomposition'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.SaturationSection(data, config, section_config=None)[source]

Bases: Section

Saturation and adstock analysis.

section_id: str = 'saturation'
default_title: str = 'Saturation & Carryover Effects'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.SensitivitySection(data, config, section_config=None)[source]

Bases: Section

Sensitivity analysis results.

section_id: str = 'sensitivity'
default_title: str = 'Sensitivity Analysis'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.PacingSection(data, config, section_config=None)[source]

Bases: Section

In-flight pacing — planned vs actual delivery (issue #107).

Closes the loop between the recommended plan and live delivery: planned vs actual spend by channel, divergence flags, and the expected effect on the outcome. Data-gated on bundle.pacing.

section_id: str = 'pacing'
default_title: str = 'In-flight pacing (plan vs actual)'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.LongTermSection(data, config, section_config=None)[source]

Bases: Section

Short-term vs long-term / brand effect (issue #106).

A weekly MMM measures activation + within-window carryover; it does NOT measure true long-term brand equity. This section surfaces the estimable immediate-vs-carryover split, states the long-term caveat plainly (so the report can’t be read as having measured brand), optionally shows an assumption-driven long-term-multiplier scenario, and lists the data needed to measure long-term properly. Data-gated on bundle.long_term.

section_id: str = 'long-term'
default_title: str = 'Short-term vs long-term (brand)'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.TriangulationSection(data, config, section_config=None)[source]

Bases: Section

Triangulation panel — MMM × experiment × platform (issue #104).

Puts each channel’s effect from the MMM, from experiments, and from platform-reported attribution side by side, with a reconciled recommendation and plain-language notes on any disagreement. Convergent evidence across independent methods is the most persuasive thing to show a skeptical CFO; divergence, shown honestly, is where the real conversation happens. Data-gated on bundle.triangulation.

section_id: str = 'triangulation'
default_title: str = 'Triangulation MMM × experiment × platform'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.SpecCurveSection(data, config, section_config=None)[source]

Bases: Section

Spec-curve / model-averaging robustness (issue #103).

Renders how each channel’s ROI moves across a pre-registered set of defensible specs, plus the model-averaged (BMA) estimate — so robustness across specs is itself a reported result and fragility can’t hide behind one hand-picked number. Data-gated on bundle.spec_curve.

The BMA weighting is labelled explicitly, because averaging a causal estimand with predictive weights is a category error the payload’s weighting_caveat spells out. See mmm_framework.validation.spec_curve.

section_id: str = 'spec-curve'
default_title: str = 'Specification robustness'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.MethodologySection(data, config, section_config=None)[source]

Bases: Section

Model methodology documentation.

section_id: str = 'methodology'
default_title: str = 'Methodology'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.CFOSection(data, config, section_config=None)[source]

Bases: Section

CFO one-pager — marketing’s P&L contribution + spend-cut risk (issue #108).

Rolls the model up into the two numbers a budget owner carries into a board room: marketing’s total incremental contribution vs the base (non-marketing) outcome, with a credible interval, and a spend-cut sensitivity (“cut marketing X% → this much revenue/profit at risk”). Data-gated on bundle.cfo.

section_id: str = 'cfo'
default_title: str = 'CFO one-pager contribution & revenue at risk'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.CausalAssumptionsSection(data, config, section_config=None)[source]

Bases: Section

Causal assumptions, identification strategy and sensitivity to unobserved confounding.

Always renders the no-unobserved-confounding / SUTVA caveat (the honest framing a causal-branded tool owes its readers). When the data bundle carries causal_assumptions (identification strategy, assumed confounders, and the robustness-value table from mmm_framework.validation), those details are rendered too.

section_id: str = 'causal-assumptions'
default_title: str = 'Causal Assumptions'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.DiagnosticsSection(data, config, section_config=None)[source]

Bases: Section

MCMC diagnostics and convergence checks.

Gated OFF for a frequentist fit, with a stated reason. Left on, the extractor supplies None R-hat/ESS and the table below renders them as “N/A” — which is correct but reads as a broken table rather than as a property of the estimator. Worse, before _merge_fit_provenance learned the paradigm, the recomputed ESS of a bootstrap trace was ≈``n_boot`` and the row rendered “✅ Pass”.

section_id: str = 'diagnostics'
default_title: str = 'Model Diagnostics'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.GeographicSection(data, config, section_config=None)[source]

Bases: Section

Geographic performance breakdown for multi-geo models.

section_id: str = 'geographic'
default_title: str = 'Geographic Analysis'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.MediatorSection(data, config, section_config=None)[source]

Bases: Section

Mediator pathway analysis for nested models.

section_id: str = 'mediators'
default_title: str = 'Mediator Pathway Analysis'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.CannibalizationSection(data, config, section_config=None)[source]

Bases: Section

Cross-product cannibalization effects analysis.

section_id: str = 'cannibalization'
default_title: str = 'Product Cannibalization Analysis'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.FactorAnalysisSection(data, config, section_config=None)[source]

Bases: Section

Latent-structure section for a non-MMM model — a CFA’s loadings, an LCA’s class profiles, etc. Renders a summary table + the model’s declared estimands as cards, with family-specific headings supplied by the bundle (latent_section_title / latent_table_title / latent_estimands_title). Empty unless the bundle carries factor_loadings or cfa_fit_indices.

section_id: str = 'factor-analysis'
default_title: str = 'Latent Structure'
property title: str
render()[source]
Return type:

str

class mmm_framework.reporting.sections.EstimandsSection(data, config, section_config=None)[source]

Bases: Section

Declared / default estimand results with credible intervals.

Renders the model’s pre-specified causal quantities (e.g. contribution ROI, marginal ROAS, incremental contribution per channel) as a single table of point estimate + credible interval. Data-driven: empty unless the bundle carries estimands (populated by the extractor for MMM models).

section_id: str = 'estimands'
default_title: str = 'Estimand Results'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.PosteriorPredictiveSection(data, config, section_config=None)[source]

Bases: Section

Posterior-predictive goodness-of-fit checks for MMM models.

Default-on for MMM models: shows whether the fitted model can reproduce the data it was trained on, via observed-vs-predicted, a density overlay of replicated datasets, predictive-interval calibration, and residual structure. Data-driven: empty unless the bundle carries posterior_predictive.

section_id: str = 'posterior-predictive'
default_title: str = 'Posterior Predictive Checks'
render()[source]
Return type:

str

class mmm_framework.reporting.sections.AllocationSection(data, config, section_config=None)[source]

Bases: Section

Budget-allocation plan: recommended per-channel (and per-geo) spend plus an optional forward flighting calendar.

Data-driven and default-off: renders only when the bundle carries allocation_results (a plan computed by the plan_budget op), so a normal model report that has no plan attached silently omits the section.

section_id: str = 'allocation'
default_title: str = 'Budget Allocation Plan'
render()[source]
Return type:

str

Data Extractors

Data extractors for MMM report generation.

Provides adapters to extract report data from various MMM model types: - BayesianMMM (core framework) - NestedMMM, MultivariateMMM, CombinedMMM (extensions) - PyMC-Marketing MMM class

Each extractor converts model-specific data structures into a unified MMMDataBundle that the report generator can consume.

This subpackage organizes extractors by domain: - bundle: MMMDataBundle data container - base: DataExtractor ABC and protocols - mixins: Shared extraction utilities (aggregation, geo-level) - bayesian: BayesianMMMExtractor for core framework - extended: ExtendedMMMExtractor for extension models - pymc_marketing: PyMCMarketingExtractor for compatibility

class mmm_framework.reporting.extractors.MMMDataBundle(dates=None, geo_names=None, actual_by_geo=None, predicted_by_geo=None, fit_statistics_by_geo=None, actual=None, predicted=None, fit_statistics=None, total_revenue=None, marketing_attributed_revenue=None, blended_roi=None, marketing_contribution_pct=None, channel_roi=None, channel_spend=None, channel_contribution=None, channel_evidence=None, component_totals=None, component_time_series=None, saturation_curves=None, adstock_curves=None, current_spend=None, sensitivity_results=None, pacing=None, long_term=None, triangulation=None, spec_curve=None, cfo=None, causal_assumptions=None, model_specification=None, model_kind='mmm', inference_family='bayesian', estimator=None, interval_kind=None, interval_semantics=None, frequentist_caveats=None, factor_loadings=None, cfa_fit_indices=None, latent_section_title=None, latent_table_title=None, latent_estimands_title=None, estimands=None, allocation_results=None, posterior_predictive=None, diagnostics=None, trace_data=None, trace_parameters=None, prior_samples=None, posterior_samples=None, channel_names=None, pooled_channels=None, time_varying_betas=None, reach_frequency=None, mediator_effects=None, cross_effects=None, outcome_correlations=None, geo_performance=None, geo_roi=None, geo_contribution=None, mediator_names=None, mediator_pathways=None, mediator_time_series=None, total_indirect_effect=None, product_names=None, cannibalization_matrix=None, net_product_effects=None, component_time_series_by_geo=None, component_totals_by_geo=None, actual_by_product=None, predicted_by_product=None, fit_statistics_by_product=None, component_time_series_by_product=None, component_totals_by_product=None)[source]

Bases: object

Unified data container for MMM report generation.

All fields are optional - sections will gracefully skip if data is missing.

Attributes

datesarray-like, optional

Time index for the model.

actualndarray, optional

Observed KPI values (aggregated to period level).

predicteddict, optional

Predictions with “mean”, “lower”, “upper” keys.

fit_statisticsdict, optional

Model fit metrics like “r2”, “rmse”, “mae”, “mape”.

channel_nameslist[str], optional

Names of media channels.

channel_roidict, optional

Channel-level ROI with uncertainty.

channel_spenddict, optional

Total spend per channel.

channel_contributiondict, optional

Channel contribution with uncertainty.

component_totalsdict, optional

Total contribution per component.

component_time_seriesdict, optional

Time series per component.

saturation_curvesdict, optional

Saturation curve data per channel.

adstock_curvesdict, optional

Adstock decay weights per channel.

current_spenddict, optional

Current spend level per channel.

diagnosticsdict, optional

MCMC diagnostics like divergences, rhat, ess.

trace_datadict, optional

MCMC trace samples for diagnostic plots.

trace_parameterslist[str], optional

Parameter names for trace plots.

prior_samplesdict, optional

Prior samples for comparison.

posterior_samplesdict, optional

Posterior samples for comparison.

model_specificationdict, optional

Model configuration details.

geo_nameslist[str], optional

Geography names for hierarchical models.

geo_performancedict, optional

Geo-level performance metrics.

geo_roidict, optional

Geo-level ROI by channel.

geo_contributiondict, optional

Geo-level contribution by component.

actual_by_geodict, optional

Observed values per geo.

predicted_by_geodict, optional

Predictions per geo with uncertainty.

fit_statistics_by_geodict, optional

Fit statistics per geo.

component_time_series_by_geodict, optional

Component time series per geo.

component_totals_by_geodict, optional

Component totals per geo.

product_nameslist[str], optional

Product names for multi-product models.

actual_by_productdict, optional

Observed values per product.

predicted_by_productdict, optional

Predictions per product with uncertainty.

fit_statistics_by_productdict, optional

Fit statistics per product.

component_time_series_by_productdict, optional

Component time series per product.

component_totals_by_productdict, optional

Component totals per product.

mediator_nameslist[str], optional

Mediator names for nested models.

mediator_pathwaysdict, optional

Effect pathways through mediators.

mediator_time_seriesdict, optional

Mediator values over time.

mediator_effectsdict, optional

Mediator effect estimates.

total_indirect_effectdict, optional

Total indirect effect with uncertainty.

cannibalization_matrixdict, optional

Cross-product cannibalization effects.

cross_effectsdict, optional

Cross-outcome effects.

outcome_correlationsndarray, optional

Correlation matrix between outcomes.

net_product_effectsdict, optional

Net effects per product after cannibalization.

total_revenuefloat, optional

Total observed revenue/KPI.

marketing_attributed_revenuedict, optional

Revenue attributed to marketing.

blended_roidict, optional

Overall blended ROI with uncertainty.

marketing_contribution_pctdict, optional

Marketing contribution as percentage.

sensitivity_resultsdict, optional

Sensitivity analysis results.

dates: ndarray | DatetimeIndex | list | None = None
geo_names: list[str] | None = None
actual_by_geo: dict[str, ndarray] | None = None
predicted_by_geo: dict[str, dict[str, ndarray]] | None = None
fit_statistics_by_geo: dict[str, dict[str, float]] | None = None
actual: ndarray | None = None
predicted: dict[str, ndarray] | None = None
fit_statistics: dict[str, float] | None = None
total_revenue: float | None = None
marketing_attributed_revenue: dict[str, float] | None = None
blended_roi: dict[str, float] | None = None
marketing_contribution_pct: dict[str, float] | None = None
channel_roi: dict[str, dict[str, float]] | None = None
channel_spend: dict[str, float] | None = None
channel_contribution: dict[str, dict[str, float]] | None = None
channel_evidence: dict[str, dict[str, Any]] | None = None
component_totals: dict[str, float] | None = None
component_time_series: dict[str, ndarray] | None = None
saturation_curves: dict[str, dict[str, ndarray]] | None = None
adstock_curves: dict[str, ndarray] | None = None
current_spend: dict[str, float] | None = None
sensitivity_results: dict[str, Any] | None = None
pacing: dict[str, Any] | None = None
long_term: dict[str, Any] | None = None
triangulation: dict[str, Any] | None = None
spec_curve: dict[str, Any] | None = None
cfo: dict[str, Any] | None = None
causal_assumptions: dict[str, Any] | None = None
model_specification: dict[str, Any] | None = None
model_kind: str = 'mmm'
inference_family: str = 'bayesian'
estimator: str | None = None
interval_kind: str | None = None
interval_semantics: str | None = None
frequentist_caveats: list[str] | None = None
property is_frequentist: bool
factor_loadings: list[dict[str, Any]] | None = None
cfa_fit_indices: dict[str, dict[str, float]] | None = None
latent_section_title: str | None = None
latent_table_title: str | None = None
latent_estimands_title: str | None = None
estimands: dict[str, dict[str, Any]] | None = None
allocation_results: dict[str, Any] | None = None
posterior_predictive: dict[str, Any] | None = None
diagnostics: dict[str, Any] | None = None
trace_data: dict[str, ndarray] | None = None
trace_parameters: list[str] | None = None
prior_samples: dict[str, ndarray] | None = None
posterior_samples: dict[str, ndarray] | None = None
channel_names: list[str] | None = None
pooled_channels: list[str] | None = None
time_varying_betas: dict | None = None
reach_frequency: dict | None = None
mediator_effects: dict[str, Any] | None = None
cross_effects: dict[str, Any] | None = None
outcome_correlations: ndarray | None = None
geo_performance: dict[str, dict[str, Any]] | None = None
geo_roi: dict[str, dict[str, dict[str, float]]] | None = None
geo_contribution: dict[str, dict[str, float]] | None = None
mediator_names: list[str] | None = None
mediator_pathways: dict[str, dict[str, Any]] | None = None
mediator_time_series: dict[str, ndarray] | None = None
total_indirect_effect: dict[str, float] | None = None
product_names: list[str] | None = None
cannibalization_matrix: dict[str, dict[str, dict[str, float]]] | None = None
net_product_effects: dict[str, dict[str, float]] | None = None
component_time_series_by_geo: dict[str, dict[str, ndarray]] | None = None
component_totals_by_geo: dict[str, dict[str, float]] | None = None
actual_by_product: dict[str, ndarray] | None = None
predicted_by_product: dict[str, dict[str, ndarray]] | None = None
fit_statistics_by_product: dict[str, dict[str, float]] | None = None
component_time_series_by_product: dict[str, dict[str, ndarray]] | None = None
component_totals_by_product: dict[str, dict[str, float]] | None = None
property has_geo_data: bool

Check if geo-level data is available.

property has_geo_decomposition: bool

Check if geo-level decomposition is available.

property has_product_data: bool

Check if product-level data is available.

property has_product_decomposition: bool

Check if product-level decomposition is available.

property has_estimands: bool

Check if realized estimand results (mean + CI) are available.

property has_posterior_predictive: bool

Check if posterior-predictive goodness-of-fit data is available.

property has_mediator_data: bool

Check if mediator pathway data is available.

property has_cannibalization_data: bool

Check if cannibalization data is available.

__init__(dates=None, geo_names=None, actual_by_geo=None, predicted_by_geo=None, fit_statistics_by_geo=None, actual=None, predicted=None, fit_statistics=None, total_revenue=None, marketing_attributed_revenue=None, blended_roi=None, marketing_contribution_pct=None, channel_roi=None, channel_spend=None, channel_contribution=None, channel_evidence=None, component_totals=None, component_time_series=None, saturation_curves=None, adstock_curves=None, current_spend=None, sensitivity_results=None, pacing=None, long_term=None, triangulation=None, spec_curve=None, cfo=None, causal_assumptions=None, model_specification=None, model_kind='mmm', inference_family='bayesian', estimator=None, interval_kind=None, interval_semantics=None, frequentist_caveats=None, factor_loadings=None, cfa_fit_indices=None, latent_section_title=None, latent_table_title=None, latent_estimands_title=None, estimands=None, allocation_results=None, posterior_predictive=None, diagnostics=None, trace_data=None, trace_parameters=None, prior_samples=None, posterior_samples=None, channel_names=None, pooled_channels=None, time_varying_betas=None, reach_frequency=None, mediator_effects=None, cross_effects=None, outcome_correlations=None, geo_performance=None, geo_roi=None, geo_contribution=None, mediator_names=None, mediator_pathways=None, mediator_time_series=None, total_indirect_effect=None, product_names=None, cannibalization_matrix=None, net_product_effects=None, component_time_series_by_geo=None, component_totals_by_geo=None, actual_by_product=None, predicted_by_product=None, fit_statistics_by_product=None, component_time_series_by_product=None, component_totals_by_product=None)
class mmm_framework.reporting.extractors.HasTrace(*args, **kwargs)[source]

Bases: Protocol

Protocol for objects with an ArviZ InferenceData trace.

property trace: Any
__init__(*args, **kwargs)
class mmm_framework.reporting.extractors.HasModel(*args, **kwargs)[source]

Bases: Protocol

Protocol for objects with a PyMC model.

property model: Any
__init__(*args, **kwargs)
class mmm_framework.reporting.extractors.DataExtractor[source]

Bases: ABC

Base class for model data extractors.

All concrete extractors should inherit from this class and implement the extract() method. Shared utilities for HDI computation, diagnostics extraction, and fit statistics are provided.

Attributes

ci_probfloat

Credible interval probability (default 0.8).

Examples

>>> class MyExtractor(DataExtractor):
...     def __init__(self, model, ci_prob=0.8):
...         self.model = model
...         self._ci_prob = ci_prob
...
...     @property
...     def ci_prob(self):
...         return self._ci_prob
...
...     def extract(self):
...         bundle = MMMDataBundle()
...         # ... extract data
...         return bundle
property ci_prob: float

Credible interval probability. Override in subclass.

abstract extract()[source]

Extract data from model into unified bundle.

Return type:

MMMDataBundle

stamp_inference_family(bundle, diagnostics)[source]

Copy the estimation paradigm onto the bundle for section gating.

Kept separate from _merge_fit_provenance() (which owns the diagnostics dict) because sections read the bundle, not the dict, and a section that has to reach into bundle.diagnostics to learn what vocabulary to use is a section that will forget to.

Return type:

None

class mmm_framework.reporting.extractors.AggregationMixin[source]

Bases: object

Mixin providing data aggregation utilities for extractors.

Provides methods for aggregating data by period, geography, and product while properly propagating uncertainty through sample aggregation.

class mmm_framework.reporting.extractors.GeoExtractionMixin[source]

Bases: object

Mixin providing geo-level extraction methods.

Requires the class to have panel, mmm, and ci_prob attributes.

class mmm_framework.reporting.extractors.ProductExtractionMixin[source]

Bases: object

Mixin providing product-level extraction methods.

Requires the class to have panel, mmm, and ci_prob attributes.

class mmm_framework.reporting.extractors.BayesianMMMExtractor(mmm, panel=None, results=None, ci_prob=0.8)[source]

Bases: DataExtractor, AggregationMixin, GeoExtractionMixin, ProductExtractionMixin, EstimandPPCMixin

Extract data from mmm-framework’s BayesianMMM class.

Inherits shared utilities from DataExtractor and AggregationMixin for HDI computation, fit statistics, and data aggregation.

Parameters

mmmBayesianMMM

Fitted BayesianMMM instance

panelPanelDataset

Panel data used for fitting

resultsMMMResults, optional

Fit results if available

ci_probfloat

Credible interval probability (default 0.8)

__init__(mmm, panel=None, results=None, ci_prob=0.8)[source]
property ci_prob: float

Credible interval probability.

extract()[source]

Extract all available data from BayesianMMM.

Return type:

MMMDataBundle

class mmm_framework.reporting.extractors.ExtendedMMMExtractor(model, panel=None, results=None, ci_prob=0.8)[source]

Bases: DataExtractor, EstimandPPCMixin

Extract data from mmm-framework’s extended MMM models.

Inherits shared utilities from DataExtractor for HDI computation, fit statistics, and MCMC diagnostics.

Supports NestedMMM (mediation pathways), MultivariateMMM (per-outcome fit, decomposition, and cross-effects), and CombinedMMM (both).

Parameters

modelAny

Extended MMM model instance (NestedMMM, MultivariateMMM, or CombinedMMM)

panelAny, optional

Accepted for interface parity with BayesianMMMExtractor (the extension models carry their own data arrays).

resultsAny, optional

Fit results, if available.

ci_probfloat

Credible interval probability (default 0.8)

__init__(model, panel=None, results=None, ci_prob=0.8)[source]
property ci_prob: float

Credible interval probability.

extract()[source]

Extract data from extended MMM model.

Return type:

MMMDataBundle

class mmm_framework.reporting.extractors.FactorAnalysisExtractor(model, ci_prob=0.94, **_)[source]

Bases: DataExtractor

Extract a latent-structure summary + estimands from a non-MMM garden model into an MMMDataBundle (family-agnostic: CFA, LCA, …).

__init__(model, ci_prob=0.94, **_)[source]
extract()[source]

Extract data from model into unified bundle.

Return type:

MMMDataBundle

class mmm_framework.reporting.extractors.PyMCMarketingExtractor(mmm, ci_prob=0.8)[source]

Bases: DataExtractor

Extract data from pymc-marketing’s MMM class.

Inherits shared utilities from DataExtractor for HDI computation, fit statistics, and MCMC diagnostics.

Provides compatibility with the standard pymc-marketing MMM.

Parameters

mmmAny

PyMC-Marketing MMM instance

ci_probfloat

Credible interval probability (default 0.8)

__init__(mmm, ci_prob=0.8)[source]
property ci_prob: float

Credible interval probability.

extract()[source]

Extract data from pymc-marketing MMM.

Return type:

MMMDataBundle

mmm_framework.reporting.extractors.create_extractor(model, **kwargs)[source]

Factory function to create appropriate extractor for model type.

Parameters:
  • model (Any) – MMM model instance.

  • **kwargs – Additional arguments passed to extractor.

Return type:

DataExtractor

Returns:

Appropriate extractor for the model type.

Charts

Decomposition Charts

Decomposition chart functions for MMM reporting.

Contains waterfall, stacked area, and time series decomposition charts.

mmm_framework.reporting.charts.decomposition.create_decomposition_chart(dates, components, config, chart_config=None, div_id='decompositionChart', chart_type='stacked_area')[source]

Create time series decomposition visualization.

Return type:

str

Parameters

datesarray-like

Time index

componentsdict

Mapping of component name to time series values

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

chart_typestr

Either “stacked_area” or “stacked_bar”

Returns

str

HTML string with embedded Plotly chart

mmm_framework.reporting.charts.decomposition.create_stacked_area_chart(dates, components, config, chart_config=None, div_id='stackedAreaChart')[source]

Convenience wrapper for stacked area decomposition chart.

Return type:

str

mmm_framework.reporting.charts.decomposition.create_waterfall_chart(categories, values, config, chart_config=None, div_id='waterfallChart', total_label='Total')[source]

Create waterfall chart for revenue decomposition.

Return type:

str

Parameters

categorieslist[str]

Component names (e.g., [“Baseline”, “TV”, “Search”, …])

valuesndarray

Contribution values for each component

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

total_labelstr

Label for the total bar

Returns

str

HTML string with embedded Plotly chart

mmm_framework.reporting.charts.decomposition.create_stacked_area_chart_with_geo_selector(dates, components_agg, components_by_geo=None, geo_names=None, config=None, chart_config=None, div_id='decompositionStackedArea')[source]

Create stacked area decomposition chart with geo selector dropdown.

Return type:

str

Parameters

datesarray-like

Time index for x-axis

components_aggdict

Aggregated component time series: {component_name: ndarray}

components_by_geodict, optional

Per-geo components: {geo_name: {component_name: ndarray}}

geo_nameslist, optional

List of geography names

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID for the chart

Returns

str

HTML string with embedded Plotly chart and dropdown

mmm_framework.reporting.charts.decomposition.create_waterfall_chart_with_geo_selector(component_totals_agg, component_totals_by_geo=None, geo_names=None, config=None, chart_config=None, div_id='decompositionWaterfall')[source]

Create waterfall chart for contribution breakdown with geo selector.

Return type:

str

Parameters

component_totals_aggdict

Aggregated component totals: {component_name: total_contribution}

component_totals_by_geodict, optional

Per-geo totals: {geo_name: {component_name: total}}

geo_nameslist, optional

List of geography names

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

Returns

str

HTML string with embedded Plotly waterfall chart

Diagnostic Charts

Diagnostic chart functions for MMM reporting.

Contains saturation curves, adstock decay, prior/posterior comparison, trace plots, and sensitivity analysis charts.

mmm_framework.reporting.charts.diagnostic.create_saturation_curves(channels, spend_ranges, response_curves, current_spend, config, chart_config=None, div_id='saturationCharts', ci_bands=None)[source]

Create saturation curve visualizations for each channel.

Return type:

str

Parameters

channelslist[str]

Channel names

spend_rangesdict

Mapping of channel to spend value array for x-axis

response_curvesdict

Mapping of channel to response curve values

current_spenddict

Mapping of channel to current spend level

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

ci_bandsdict, optional

Mapping of channel to (lower, upper) CI arrays

Returns

str

HTML string with embedded Plotly charts in a grid

mmm_framework.reporting.charts.diagnostic.create_adstock_chart(channels, lag_weights, config, chart_config=None, div_id='adstockChart')[source]

Create adstock/carryover decay visualization.

Return type:

str

Parameters

channelslist[str]

Channel names

lag_weightsdict

Mapping of channel to decay weight arrays

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

Returns

str

HTML string with embedded Plotly chart

mmm_framework.reporting.charts.diagnostic.create_prior_posterior_chart(parameter_names, prior_samples, posterior_samples, config, chart_config=None, div_id='priorPosteriorChart')[source]

Create prior vs posterior comparison visualization.

Return type:

str

Parameters

parameter_nameslist[str]

Names of parameters to plot

prior_samplesdict

Mapping of parameter name to prior samples

posterior_samplesdict

Mapping of parameter name to posterior samples

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

Returns

str

HTML string with embedded Plotly charts

mmm_framework.reporting.charts.diagnostic.create_trace_plot(parameter_names, traces_data, config, chart_config=None, div_id='tracePlot', n_chains=4)[source]

Create MCMC trace plots for diagnostics.

Return type:

str

Parameters

parameter_nameslist[str]

Parameters to visualize

traces_datadict

Mapping of parameter name to samples array (chains x draws)

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

n_chainsint

Number of MCMC chains

Returns

str

HTML string with embedded Plotly charts

mmm_framework.reporting.charts.diagnostic.create_sensitivity_chart(scenarios, base_values, alternative_values, config, chart_config=None, div_id='sensitivityChart')[source]

Create sensitivity analysis comparison chart.

Return type:

str

Parameters

scenarioslist[str]

Names of sensitivity scenarios

base_valuesndarray

Values from base model specification

alternative_valuesdict

Mapping of scenario name to alternative values

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

Returns

str

HTML string with embedded Plotly chart

Fit Charts

Model fit chart functions for MMM reporting.

Contains actual vs predicted visualizations with geo/product selectors.

mmm_framework.reporting.charts.fit.create_model_fit_chart(dates, actual, predicted_mean, predicted_lower, predicted_upper, config, chart_config=None, div_id='modelFitChart')[source]

Create model fit visualization showing actual vs predicted with uncertainty.

Return type:

str

Parameters

datesarray-like

Time index for observations

actualndarray

Observed KPI values

predicted_meanndarray

Posterior mean predictions

predicted_lowerndarray

Lower bound of credible interval

predicted_upperndarray

Upper bound of credible interval

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID for the chart

Returns

str

HTML string with embedded Plotly chart

mmm_framework.reporting.charts.fit.create_model_fit_chart_with_geo_selector(dates, actual_agg, predicted_agg, actual_by_geo=None, predicted_by_geo=None, geo_names=None, config=None, chart_config=None, div_id='modelFitChart')[source]

Create model fit visualization with geo selector dropdown.

Return type:

str

Parameters

datesarray-like

Time index for x-axis

actual_aggndarray

Aggregated observed values (sum over all geos)

predicted_aggdict

Aggregated predictions with keys “mean”, “lower”, “upper”

actual_by_geodict, optional

Per-geo observed values: {geo_name: ndarray}

predicted_by_geodict, optional

Per-geo predictions: {geo_name: {“mean”, “lower”, “upper”}}

geo_nameslist, optional

List of geography names

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID for the chart

Returns

str

HTML string with embedded Plotly chart and dropdown

mmm_framework.reporting.charts.fit.create_model_fit_chart_with_dimension_filter(dates, actual_agg, predicted_agg, actual_by_geo=None, predicted_by_geo=None, actual_by_product=None, predicted_by_product=None, geo_names=None, product_names=None, config=None, chart_config=None, div_id='modelFitChartFiltered')[source]

Create model fit visualization with multi-select dimension filters.

Supports filtering by geography and/or product with checkbox-based UI. Default view shows aggregated data; users can select multiple specific geos/products to compare.

Return type:

str

Parameters

datesarray-like

Time index for x-axis

actual_aggndarray

Aggregated observed values (sum over all dimensions)

predicted_aggdict

Aggregated predictions with keys “mean”, “lower”, “upper”

actual_by_geodict, optional

Per-geo observed values: {geo_name: ndarray}

predicted_by_geodict, optional

Per-geo predictions: {geo_name: {“mean”, “lower”, “upper”}}

actual_by_productdict, optional

Per-product observed values: {product_name: ndarray}

predicted_by_productdict, optional

Per-product predictions: {product_name: {“mean”, “lower”, “upper”}}

geo_nameslist, optional

List of geography names

product_nameslist, optional

List of product names

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID for the chart

Returns

str

HTML string with embedded Plotly chart and multi-select filters

mmm_framework.reporting.charts.fit.create_fit_statistics_with_geo_selector(fit_stats_agg, fit_stats_by_geo=None, geo_names=None, config=None, div_id='fitStatsTable')[source]

Create fit statistics display with geo selector.

This uses JavaScript to show/hide table rows based on selection.

Return type:

str

Parameters

fit_stats_aggdict

Aggregated fit statistics: {“r2”, “rmse”, “mape”}

fit_stats_by_geodict, optional

Per-geo stats: {geo_name: {“r2”, “rmse”, “mape”}}

geo_nameslist, optional

Geography names

configReportConfig

Report configuration

div_idstr

HTML div ID

Returns

str

HTML string with statistics table and selector

ROI Charts

ROI chart functions for MMM reporting.

Contains ROI forest plots and channel performance visualizations.

mmm_framework.reporting.charts.roi.create_roi_forest_plot(channels, roi_mean, roi_lower, roi_upper, config, chart_config=None, div_id='roiForestPlot', reference_line=1.0)[source]

Create forest plot showing channel ROI with credible intervals.

Return type:

str

Parameters

channelslist[str]

Channel names

roi_meanndarray

Posterior mean ROI for each channel

roi_lowerndarray

Lower bound of credible interval

roi_upperndarray

Upper bound of credible interval

configReportConfig

Report configuration

chart_configChartConfig, optional

Chart-specific configuration

div_idstr

HTML div ID

reference_linefloat

Value for vertical reference line (default 1.0 for break-even)

Returns

str

HTML string with embedded Plotly chart

Helpers

ROI Helpers

ROI computation functions for MMM reporting.

Functions for computing ROI with uncertainty quantification from fitted models.

mmm_framework.reporting.helpers.roi.compute_roi_with_uncertainty(model, spend_data=None, hdi_prob=0.94, n_samples=None)[source]

Compute ROI with full uncertainty quantification.

Computes average ROI (contribution / spend) for each channel with credible intervals derived from the posterior distribution.

Return type:

DataFrame

Parameters

modelBayesianMMM or ExtendedMMM

Fitted MMM model with trace

spend_datadict or pd.Series, optional

Channel spend totals. If None, extracts from model’s panel data.

hdi_probfloat

Probability mass for HDI (default 0.94)

n_samplesint, optional

Number of posterior samples to use. If None, uses all.

Returns

pd.DataFrame

DataFrame with ROI metrics per channel including: - spend: Total channel spend - contribution_mean/lower/upper: Revenue contribution with HDI - roi_mean/lower/upper: ROI with HDI - prob_positive: P(ROI > 0) - prob_profitable: P(ROI > 1)

Examples

>>> roi_df = compute_roi_with_uncertainty(mmm)
>>> print(roi_df[['channel', 'roi_mean', 'roi_hdi_low', 'roi_hdi_high', 'prob_profitable']])
mmm_framework.reporting.helpers.roi.compute_marginal_roi(model, channel, spend_level=None, delta=0.01, hdi_prob=0.94)[source]

Compute marginal ROI at a given spend level.

Marginal ROI is the derivative of the response curve with respect to spend, measuring the incremental return from the next dollar invested.

Return type:

dict[str, float]

Parameters

modelBayesianMMM

Fitted model

channelstr

Channel name

spend_levelfloat, optional

Spend level to evaluate. If None, uses current average spend.

deltafloat

Relative change for numerical differentiation

hdi_probfloat

HDI probability

Returns

dict

Marginal ROI statistics including mean, HDI, and comparison to average ROI

Decomposition Helpers

Component decomposition functions for MMM reporting.

Functions for computing model component decomposition with uncertainty.

mmm_framework.reporting.helpers.decomposition.compute_component_decomposition(model, include_time_series=True, hdi_prob=0.94)[source]

Compute full component decomposition of model predictions.

Breaks down total outcome into contributions from: - Baseline/intercept - Trend - Seasonality - Media channels (individually) - Control variables - Geographic/product effects (if applicable)

Return type:

list[DecompositionResult]

Parameters

modelBayesianMMM

Fitted model

include_time_seriesbool

Whether to include time series arrays

hdi_probfloat

HDI probability

Returns

list[DecompositionResult]

Decomposition results by component

Examples

>>> decomp = compute_component_decomposition(mmm)
>>> df = pd.DataFrame([d.to_dict() for d in decomp])
>>> print(df[['component', 'total_contribution', 'pct_of_total']])
mmm_framework.reporting.helpers.decomposition.compute_decomposition_waterfall(decomp, start_label='Starting Value', end_label='Total Outcome')[source]

Format decomposition for waterfall chart visualization.

Return type:

DataFrame

Parameters

decomplist[DecompositionResult]

Decomposition results

start_labelstr

Label for starting point

end_labelstr

Label for ending total

Returns

pd.DataFrame

DataFrame formatted for waterfall chart

Summary Helpers

Summary report generation functions for MMM reporting.

Functions for generating comprehensive model summaries with diagnostics.

mmm_framework.reporting.helpers.summary.generate_model_summary(model, hdi_prob=0.94)[source]

Generate comprehensive model summary for reporting.

Aggregates key metrics into a single dictionary suitable for report generation or dashboard display.

Return type:

dict[str, Any]

Parameters

modelBayesianMMM or ExtendedMMM

Fitted model

hdi_probfloat

HDI probability

Returns

dict

Summary containing: - model_info: Basic model metadata - diagnostics: MCMC convergence diagnostics - roi_summary: ROI by channel - decomposition: Component contributions - saturation_summary: Saturation levels - adstock_summary: Carryover effects