import pandas as pd
import postforecast as pf
actuals = [100.0, 106.0, 109.0, 117.0, 121.0]
period_ends = pd.date_range("2024-03-31", periods=6, freq="QE", tz="UTC")
rows = []
for index, period_end in enumerate(period_ends):
for source, value in (("consensus", 100 + index * 5), ("model", 102 + index * 5)):
rows.append((period_end, source, float(value), period_end - pd.Timedelta(days=10)))
if index < len(actuals):
rows.append((period_end, "actual", actuals[index], period_end + pd.Timedelta(days=30)))
panel = pd.DataFrame(rows, columns=["period_end", "source", "value", "known_at"])
panel = panel.assign(entity="EXAMPLE", target="revenue", period=panel.period_end.dt.strftime("%Y-%m"))
panel = pf.validate_estimates(panel)First Bayesian estimate
Build a panel, prepare its history and apply BayesianPosterior
This complete example builds synthetic quarterly observations in memory. A real application supplies the same input contract.
1. Build the panel
2. Prepare and apply
Use the plain profile to keep this example’s weights based on historical errors. Its interval shape comes from research, whose fitted-through date is currently unknown; this example demonstrates arithmetic, not out-of-sample calibration. See preset provenance.
as_of = pd.Timestamp("2025-06-25", tz="UTC")
settings = pf.posterior_preset("plain-2026-09")
subjects = pf.prepare_bayesian_subjects(
panel, as_of, forecast_source="model", settings=settings,
)
method = pf.BayesianPosterior(forecast_source="model", settings=settings)
fitted = method.fit(subjects)
result = fitted.apply(subjects)
current = result.loc[result.period.eq("2025-06")]
current[["entity", "period", "posterior_level", "posterior_lower", "posterior_upper", "eligible"]]| entity | period | posterior_level | posterior_lower | posterior_upper | eligible | |
|---|---|---|---|---|---|---|
| 5 | EXAMPLE | 2025-06 | 125.958529 | 124.003786 | 128.274273 | True |
- Preparation: Reads only observations known strictly before
as_ofand attaches earlier-period growth and error statistics. - Fit: Validates those statistics; it does not train a model across rows.
- Apply: Combines each row’s prepared statistics and current readings.
- Settings: Use the same object in preparation and application.
3. Inspect and retain
current[["eligible", "eligibility_reason", "weight_prior", "weight_model", "weight_consensus"]]| eligible | eligibility_reason | weight_prior | weight_model | weight_consensus | |
|---|---|---|---|---|---|
| 5 | True | publishable | 0.169368 | 0.316205 | 0.514427 |
- Publication: Check
eligible; retained input values are not publication verdicts. - Units:
posterior_leveland bounds use the input units.posterior_growthis a fraction. - Freshness: Bayesian eligibility does not cap source ages. Apply a caller-defined freshness policy to the prepared age columns before publishing.
- Audit: Keep
fitted.to_dict(), the input snapshot, source identifiers andas_of.
Continue with Choose a method or the worked Bayesian tutorial.