Interval Forecasting¶
In this tutorial, we will wrap a point forecaster with SplitConformalForecaster to produce prediction intervals, visualize them as shaded bands, and measure how well the intervals cover the true values. Along the way, we will use predict_interval, plot_forecast, and EmpiricalCoverage.
Try it interactively
Wrap a point forecaster with SplitConformalForecaster to produce 95% prediction intervals with statistical coverage guarantees.
ViewOpen in marimoPrerequisites¶
- Completed Getting Started
Load the Data¶
We use the tourism monthly dataset and extract a single series. The T1__ prefix is the panel identifier (see Core Concepts); we rename the column to plain tourists:
from yohou.datasets import fetch_tourism_monthly
bunch = fetch_tourism_monthly()
y = (
bunch.frame
.select("time", "T1__tourists")
.rename({"T1__tourists": "tourists"})
.drop_nulls()
)
print(f"Series length: {len(y)} months")
We split into train and test, holding out 12 months:
from yohou.model_selection import train_test_split
forecasting_horizon = 12
y_train, y_test = train_test_split(y, test_size=forecasting_horizon)
print(f"Train: {len(y_train)} months, Test: {forecasting_horizon} months")
Build a Point Forecaster¶
We first build a reduction forecaster with lag features and seasonal differencing. This is the same pattern as Getting Started:
from yohou.compose import FeaturePipeline
from yohou.point import PointReductionForecaster
from yohou.preprocessing import LagTransformer
from yohou.stationarity import SeasonalDifferencing
from sklearn.linear_model import Ridge
point_forecaster = PointReductionForecaster(
estimator=Ridge(),
target_transformer=SeasonalDifferencing(seasonality=12),
feature_transformer=FeaturePipeline([
("lags", LagTransformer(lag=[1, 2, 3, 12])),
]),
)
Wrap with SplitConformalForecaster¶
SplitConformalForecaster takes any point forecaster and wraps it to produce prediction intervals. It holds out calibration_size training observations to measure forecasting errors, then constructs intervals wide enough to cover 95% of those errors:
from yohou.interval import SplitConformalForecaster
conformal = SplitConformalForecaster(
point_forecaster=point_forecaster,
calibration_size=24,
)
conformal.fit(y_train, forecasting_horizon=forecasting_horizon)
Notice that the API follows the same fit/predict pattern as every other forecaster in Yohou.
Predict Intervals¶
Now we call predict_interval to get the lower and upper bounds. The default coverage rate is 0.95 (a 95% prediction interval):
y_pred_int = conformal.predict_interval(forecasting_horizon=forecasting_horizon)
print(y_pred_int.head(4))
The output should look something like:
shape: (4, 4)
┌─────────────────────┬─────────────────────┬────────────────────────┬───────────────────────┐
│ vintage_time ┆ time ┆ tourists_lower_0.95 ┆ tourists_upper_0.95 │
│ --- ┆ --- ┆ --- ┆ --- │
│ datetime[μs] ┆ datetime[μs] ┆ f64 ┆ f64 │
╞═════════════════════╪═════════════════════╪════════════════════════╪═══════════════════════╡
│ 1993-07-01 00:00:00 ┆ 1993-08-01 00:00:00 ┆ 6313.553540 ┆ 7096.259105 │
│ 1993-07-01 00:00:00 ┆ 1993-09-01 00:00:00 ┆ 3948.652368 ┆ 4699.295620 │
│ 1993-07-01 00:00:00 ┆ 1993-10-01 00:00:00 ┆ 2700.083519 ┆ 3408.338242 │
│ 1993-07-01 00:00:00 ┆ 1993-11-01 00:00:00 ┆ 1657.301340 ┆ 2393.047151 │
└─────────────────────┴─────────────────────┴────────────────────────┴───────────────────────┘
Notice the column names: tourists_lower_0.95 and tourists_upper_0.95, derived from the target column name and the coverage rate. Each row gives the interval for one forecast step.
Visualize the Intervals¶
plot_forecast detects the interval columns automatically and renders them as a shaded band:
from yohou.plotting import plot_forecast
fig = plot_forecast(y_test, y_pred_int, y_train=y_train[-24:])
fig.show()
You should see the forecast line with a shaded 95% band around it. The training history appears on the left, and the test actuals overlay the forecast period.
Check Empirical Coverage¶
How many of the 12 test points actually fall inside the predicted intervals? EmpiricalCoverage measures this:
from yohou.metrics import EmpiricalCoverage
cov_scorer = EmpiricalCoverage()
cov_scorer.fit(y_train)
empirical_cov = cov_scorer.score(y_test, y_pred_int)
print(f"Target coverage: 0.95")
print(f"Empirical coverage: {empirical_cov:.3f}")
The output should look something like:
The empirical coverage of 0.75 is below the 0.95 target. With only 12 test observations and 24 calibration points, this gap is expected: it is a small-sample artifact, not a model failure. As both the calibration set and the test set grow, the empirical coverage converges toward the target rate. See Interval Forecasting for the theory behind this guarantee.
What You Built¶
We wrapped a point forecaster with SplitConformalForecaster to produce 95% prediction intervals, visualized them as shaded bands with plot_forecast, and measured the empirical coverage with EmpiricalCoverage. Any point forecaster in Yohou can be wrapped this way to produce calibrated intervals.
Next Steps¶
- Class-Probability Forecasting: Forecast categorical outcomes as probability distributions over classes
- Forecasting Workflow: Cross-validation, hyperparameter search, and residual diagnostics
- Interval Forecasting: Conformal prediction theory, coverage guarantees, and calibration sizing
- How to Produce Prediction Intervals: Practical recipes for configuring and scoring interval forecasters