Papilon
"Does the flap of a butterfly's wings in Brazil set off a tornado in Texas?" Edward Lorenz
Papilon (French for butterfly) is a Python library for marketing mix modeling and for studying how small changes move complex systems. Version 0.3.0 adds a Bayesian marketing mix model that chooses its own carryover shape by Bayes factor and reports when its posterior can't be trusted. It also includes the statistical-mechanics and simulation tools first released in 0.2.0.
Install
pip install papilon
Quickstart: a marketing mix model with a known truth
make_synthetic_mmm() creates three years of weekly sales for a brand running four
digital channels, and returns the parameters that generated it, so you can check every
estimate against the truth.
import papilon as pp
df, truth = pp.make_synthetic_mmm()
model = pp.MMM(
data=df,
target="sales",
media=["paid_search", "paid_social", "online_video", "display"],
date_col="date",
controls=["price_index", "promo"],
yearly_seasonality=2,
seed=11,
)
# Does online video's effect peak after the week it runs? Let the data decide.
families = model.compare_carryover({
"all_geometric": [],
"video_delayed": ["online_video"],
})
roi = model.get_roi() # ROAS with 90% intervals
plan = model.optimize_budget(total=df[model.media].sum(axis=1).mean())
Output from examples/mmm_quickstart.py, abridged (spend and sales in $ thousands):
log_evidence log_bayes_factor_vs_best probability ess_fraction verdict
all_geometric 241.223 -18.72 0.0 0.134 ACCEPT
video_delayed 259.943 0.00 1.0 0.197 ACCEPT
spend roas_mean roas_p05 roas_p95 mroas_mean true_roas
paid_search 4729.95 0.64 0.03 1.65 0.32 1.25
paid_social 7678.08 1.02 0.47 1.79 0.49 1.50
online_video 7369.53 1.76 1.50 2.05 0.88 1.57
display 3497.10 0.71 0.08 1.68 0.35 0.74
The model picks the delayed carryover shape for online video and puts its peak at 2.0 weeks, the true value. Every true ROAS falls inside its 90% interval. The point estimates for paid search and paid social are low: search spend in this dataset follows seasonal demand, which makes it hard to separate from seasonality with three years of weekly data. That is the job lift tests do, and calibration to experiments is on the roadmap.
How the model is fit
pp.MMM follows the inference path in
RFC 001, amortized structural inference:
- Proposal. A distribution over the media parameters (carryover, saturation, effect size). In 0.3.0, a Laplace fit at the posterior mode stands in for the trained amortized network the RFC describes.
- Importance sampling against the exact posterior. Baseline, trend, seasonality, controls and noise are integrated out in closed form, so sampling covers only the media parameters.
- Effective-sample-size gate. If the proposal matches the posterior well, it is
accepted. If not, it is refined from the weighted draws and tried again, or the fit
is escalated.
model.gate_resultsrecords every step. - Bayes factors. The same importance weights estimate each family's marginal
likelihood, so
compare_carryover()can choose between geometric and delayed adstock per channel.
Channel effects are constrained to be positive. The single-channel prototypes behind
this design, with their results, are in prototypes/.
Other tools (from 0.2.0)
import papilon as pp
pp.shannon_entropy(values) # information content
pp.simulate_kde_scenarios(df, columns, n_samples=1000) # scenario simulation
pp.analyze_relationships(df, target="sales") # correlation and mutual information
pp.infer_causal_structure(df, threshold=0.01) # pairwise dependency screen
pp.grid_search_optimize(df, feature_ranges, objective_fn, constraints)
infer_causal_structure tests each pair of variables for a significant linear
relationship. It is a screen for dependencies, not causal discovery: it does not orient
edges or adjust for confounders.
The notebook Papilon_Marketing_Optimization_Workflow.ipynb walks through these tools.
Tests
pip install -e ".[dev]"
pytest -m "not slow" # about 30 seconds
pytest # includes the full Bayes-factor recovery test, a few minutes
Roadmap
These are planned and not in the released package:
- The trained amortized network from RFC 001 in place of the Laplace proposal
- Calibration to lift tests and geo experiments
- Geo-level models with partial pooling
- Weibull adstock and logistic saturation
- Causal discovery and effect estimation beyond the dependency screen
License
Apache License 2.0. See LICENSE and NOTICE.
Citation
If you use Papilon in research, please cite:
Curry, B. (2026). Papilon: Bayesian marketing mix modeling with gated importance
sampling (Version 0.3.0). https://github.com/Bodhi8/papilon
Built by Brian Curry, Vector1 Research.
Metadata
Release files for papilon 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| papilon-0.3.0.tar.gz | 23.5 kB | Without provenance | Details for papilon-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| papilon-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for papilon-0.3.0-py3-none-any.whl |
Total release size: 45.1 kB
Release files / papilon-0.3.0.tar.gz
| Download URL | papilon-0.3.0.tar.gz |
|---|---|
| Size | 23.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e895538e8aeb1424d810a46acd4cb9e8f8ab776186799aa894f24b28f17d1e2
|
|
BLAKE2b-256 checksum How to use checksums |
135a0f12c9e194a4ffe0cac561ec1577c3c8d4f2b1e00ccd280ed92294902cd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Release files / papilon-0.3.0-py3-none-any.whl
| Download URL | papilon-0.3.0-py3-none-any.whl |
|---|---|
| Size | 21.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
32a3367f3e2c41ee5b71e4ae64bb218725b5c350796e6bb3fd5a4b450a978e4c
|
|
BLAKE2b-256 checksum How to use checksums |
7875076c68132372866bc32575acd18d73183bb4589dadb234121b9c9057b953
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Security for papilon 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.