PyMC-Marketing V1.0.0 🎉
PyMC-Marketing v1.0.0 is out. After two years of 0.x releases, this is our stable API.
- Multidimensional MMM is now the MMM. Geo, product, and channel dimensions are first-class, with one
xr.Datasetcarrying all inputs. - ~1.5x faster sampling, thanks to model freezing and a static-shape numba
convolve1d. CPU now beats JAX on our demo MMM. - Built on PyMC 6, ArviZ 1.2, PyTensor 3, NumPy 2, with a new namespace-based plotting API on arviz-plots and interactive Plotly output.
- v1.0.0 by @williambdean in #2677 (Migration PR!)
And four new modelling workflows we're particularly excited about:
- PIE (alpha). A BART model that learns the campaign-features to measured-incrementality map from your geo tests and ghost-ad holdouts, then predicts a full incrementality posterior for campaigns that never ran one.
https://www.pymc-marketing.io/en/latest/notebooks/pie/pie_example.html - Funnel-aware MMM. Upper-funnel media creates demand that converts later through lower-funnel channels. Encode that mediation as a custom
MuEffectinstead of starving the upper funnel of credit — the advanced notebook scales it to a geo panel.
https://www.pymc-marketing.io/en/latest/notebooks/mmm/mmm_funnel_mueffect.html - Long-term brand effects. Brand spend looks weak in a standard MMM because the demand it builds drifts into the intercept. An MMM with a time-varying intercept plus a Bayesian VARX over awareness and consideration recovers it.
https://www.pymc-marketing.io/en/latest/notebooks/mmm/mmm_brand_metrics_long_term.html - Bayesian BLP. Structural demand estimation with own- and cross-price elasticities from aggregate market shares.
https://www.pymc-marketing.io/en/latest/notebooks/customer_choice/bayesian_blp.html
Upgrading from 0.19? MMM and CLV both have breaking changes — start with the migration guide notebooks.
Docs: https://www.pymc-marketing.io
THANK YOU TO ALL OUR USERS AND CONTRIBUTORS!!!
What's Changed
Major Changes 🛠
- Remove old MMM Class by @juanitorduz in #2487
- Rename module: multidimensional -> mmm by @juanitorduz in #2541
- Remove deprecated
Priormodule by @ColtAllen in #2789
Deprecations 🚨
- chore(model-builder): remove X_pred/y_pred deprecated aliases by @anevolbap in #2562
- fix(hsgp): stop forwarding deprecated drop_first to pm.gp.HSGP by @anevolbap in #2571
New Features 🎉
- integrates the v2 namespace-based plotting API into the
MMMby @isofer in #2555 - feat(mlflow): attach active run id to idata.attrs in autolog by @anevolbap in #2574
- feat(mmm): stamp version on optimize_budget and sample_response_distribution by @anevolbap in #2578
- feat(bass): refactor to ModelBuilder with save/load and OOS predictions by @williambdean in #2584
- Predicted Incrementality by Experimentation (PIE) for Ad Measurement MVP by @thomaspinder in #2593
- Multiplicative MMM (Log and Log-Log Models) by @juanitorduz in #2477
- Add input validation for CLV models by @shivamlalakiya in #2794
- feat(mmm): generalize supplementary data persistence via MuEffect.idata_groups() by @williambdean in #2583
- Freeze MMM model before sampling by @ricardoV94 in #2606
- feat: Fourier decomposition visualization — color_dim for plot_curve and plot_decomposition method by @williambdean in #2783
- Create
ModelFitterclass by @ColtAllen in #2805 - feat(mmm): ROAS calibration via add_cost_per_target_calibration + tutorial notebook by @daimon-pymclabs in #2828
Bugfixes 🐛
- fix(plot): use ceil division so plot_curve grid fits all axes by @anevolbap in #2561
- fix(plot): support combined sample dim in plot_curve by @anevolbap in #2587
- fix(mmm): correct return type on sample_posterior_predictive and sample_response_distribution by @anevolbap in #2591
- fix(deps): upper bound matplotlib <3.11 by @anevolbap in #2626
- fix(mmm): keep RootSaturation gradient finite at zero spend by @hermesdt in #2640
- fix(clv): render the CLV likelihood equations and correct daily/hourly CLV scaling by @daimon-pymclabs in #2820
- fix(customer_choice): distinguish products in MVITS HDI legend labels by @anevolbap in #2580
- Fix: Remove dims argument from Transformation.apply by @juanitorduz in #2427
- fix: multidimensional.MMM y needs Series name by @juanitorduz in #2543
- fix(plot): drop plt.show from MMMPlotSuite.cv_predictions by @anevolbap in #2559
- fix(utils): drop obsolete fit_data UserWarning workaround in from_netcdf by @anevolbap in #2558
- fix(mlflow): type-hint MMMWrapper.predict to silence MLflow warning by @anevolbap in #2579
- [HOT FIX] Dependencies: Upper bound pymc-extras by @juanitorduz in #2623
- Fix flaky test_log_model_graph_no_graphviz polluted by MLflow infra logs by @daimon-pymclabs in #2644
- Fix mypy errors blocking pre-commit on main by @daimon-pymclabs in #2667
Documentation 📖
- docs(mmm): clarify kappa description in hill_function by @anevolbap in #2540
- migrate budget allocation plots by @isofer in #2531
- docs: cleanup sphinx warnings + add audit-only docs CI by @anevolbap in #2547
- fix(docs): give InferenceData accessor properties a real docstring by @anevolbap in #2550
- docs: fix LaTeX rendering in diversification_ratio docstring by @anevolbap in #2551
- docs(sBG): add missing x-axis labels to Figures 4 and 5 by @anevolbap in #2552
- feat(docs): automate gallery from gallery.yaml by @anevolbap in #2557
- docs: drop stale 'y' parameter from MMM.sample_posterior_predictive docstring by @anevolbap in #2548
- docs: fix code comments and typos in mmm_fivetran_connectors notebook by @anevolbap in #2564
- docs: add Bass Diffusion Model to home and getting started pages by @anevolbap in #2573
- docs(mmm): document parameters on Saturation/Adstock wrapper classes by @anevolbap in #2586
- docs(transformers): document dim in adstock docstrings, drop stale axis by @anevolbap in #2565
- docs: drop redundant Field(description=...) on five pydantic models by @anevolbap in #2577
- docs(clv): align MAP vs MCMC comparison in quickstart by @anevolbap in #2575
- docs(fourier): replace stale result_callback example with sum=False by @anevolbap in #2549
- Reduce PyMC Labs logo and CTA prominence on landing page by @daimon-pymclabs in #2607
- exploring bayesian blp heterskedastic normal by @NathanielF in #2524
- docs: lead landing page with credibility signals and a production-focused CTA by @daimon-pymclabs in #2635
- v1.0.0 by @williambdean in #2677
- Funnel Effect Example via MuEffects by @juanitorduz in #2765
- docs(var): Add MMM-VAR joint optimisation routine. by @thomaspinder in #2773
- docs: address JOSS review feedback on documentation and CLV by @williambdean in #2748
- docs(theme): Juan Fan Service by @williambdean in #2791
- docs(mmm): tutorial on long-term brand effects with brand metrics and a Bayesian VARX by @juanitorduz in #2800
- docs(mmm): add advanced geo-level funnel MMM example (six models, one ground truth) by @juanitorduz in #2814
- update readme v1 by @juanitorduz in #2836
- docs(bass): update notebook to the BassModel workflow by @anevolbap in #2770
Maintenance 🔧
- Bound and Pin PyMC and PyTensor Versions by @juanitorduz in #2542
- fix(ci): stop duplicate-issues cancellations; harden triage by @anevolbap in #2560
- refactor(budget-optimizer): use PrivateAttr and model_post_init by @anevolbap in #2567
- Add merge_group trigger to CI workflows for merge queue support by @williambdean in #2678
- refactor(mmm): rename optimize_budget **minimize_kwargs to **allocate_budget_kwargs by @anevolbap in #2563
- ci: add git-ai workflow for authorship attribution by @PabloRoque in #2746
- Sync agent instructions across Cursor, Claude, and Copilot by @drbenvincent in #2780
- chore: migrate from arviz.InferenceData to xarray.DataTree by @williambdean in #2782
New Contributors
- @nightt5879 made their first contribution in #2617
- @hermesdt made their first contribution in #2640
Full Changelog: 0.19.4...1.0.0