processing.forecast_scoring.seasonal_naive_forecasts

processing.forecast_scoring.seasonal_naive_forecasts(
    actual,
    seasons=(24, 168),
    *,
    unit='h',
    name_fmt='naive{season}',
)

Seasonal-naive persistence benchmarks derived from the actual series.

The seasonal-naive forecast for a timestamp is the observation one season earlier, which makes it the cheapest non-trivial comparator a campaign can be measured against. The result is keyed and shaped to drop straight into the forecasts argument of score_forecasts_by_period, so a benchmark is scored by the same kernel as every real entry rather than by a parallel code path.

Each series is shifted by calendar offset rather than by row count, so gaps and daylight-saving transitions in the index cannot silently misalign a benchmark. Timestamps whose season-earlier counterpart falls outside actual are dropped, which means the benchmark needs max(seasons) of history before the first period it can score.

This is pure: no logging, no plotting, no mutation.

Parameters

Name Type Description Default
actual pd.Series Ground-truth series with a DatetimeIndex. required
seasons Sequence[int] Season lengths, in unit. Defaults to (24, 168), the daily and weekly cycles of an hourly series. (24, 168)
unit str Pandas time unit the seasons are expressed in. Defaults to "h". 'h'
name_fmt str Key template for the returned mapping. {season} is substituted. Defaults to "naive{season}". 'naive{season}'

Returns

Name Type Description
dict[str, pd.Series] dict[str, pd.Series]: One forecast series per season, each
dict[str, pd.Series] indexed by the timestamps it can score.

Raises

Name Type Description
TypeError When actual is not a pd.Series with a DatetimeIndex.
ValueError When actual is empty, seasons is empty, or a season is not positive.

Examples

import pandas as pd
from spotforecast2_safe.processing.forecast_scoring import (
    score_forecasts_by_period,
    seasonal_naive_forecasts,
)

idx = pd.date_range("2026-06-01", periods=96, freq="h", tz="UTC")
actual = pd.Series(range(96), index=idx, dtype=float)

benchmarks = seasonal_naive_forecasts(actual, seasons=(24,))
print(sorted(benchmarks))

scored = score_forecasts_by_period(
    benchmarks, actual, metrics=("mae",), min_obs=24
)
print(scored[["entry", "mae"]].to_string(index=False))
['naive24']
  entry  mae
naive24 24.0
naive24 24.0
naive24 24.0