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

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)

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_of and 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_level and bounds use the input units. posterior_growth is 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 and as_of.

Continue with Choose a method or the worked Bayesian tutorial.