Skip to content

Seasonality Detection

seasonality

Frequency semantics — the one place that knows what each freq means.

Every model that decomposes seasonality needs "how many steps in one seasonal cycle" (the SARIMA m, Holt-Winters seasonal_periods, STL period …), and the Fourier features need "how many steps in one year". Both are pure functions of the run frequency, so they live here once instead of being copy-pasted into every model file — a mismatch would otherwise be invisible until a model silently used the wrong period.

Supported frequencies (SUPPORTED_FREQS) are the pandas offset aliases the product understands end-to-end: daily D, weekly W, month-start MS, month-end ME, and hourly h. These are the pandas ≥2.2 / 3.x spellings — the old M/H aliases were removed in pandas 3, so we use ME/h. Unknown frequencies fall back to a weekly/daily-ish default rather than raising, so a model still runs on an exotic freq — but is_supported lets callers (the input validator) reject one up front with a message.

Public surface: SUPPORTED_FREQS, seasonal_period, periods_per_year, is_supported.

is_supported

is_supported(freq)

True when freq is a frequency the product understands end-to-end.

Source code in src/scale_forecasting/seasonality.py
def is_supported(freq: str) -> bool:
    """True when ``freq`` is a frequency the product understands end-to-end."""
    return freq in SUPPORTED_FREQS

periods_per_year

periods_per_year(freq)

Steps in one year for freq — the period for yearly Fourier features.

Source code in src/scale_forecasting/seasonality.py
def periods_per_year(freq: str) -> float:
    """Steps in one year for ``freq`` — the period for yearly Fourier features."""
    return _PERIODS_PER_YEAR.get(freq, _DEFAULT_PERIODS_PER_YEAR)

seasonal_period

seasonal_period(freq)

Steps in one seasonal cycle for freq (e.g. 7 for daily, 12 for monthly).

Falls back to a weekly-ish default for an unrecognized frequency so a model still runs; use is_supported to reject unknown frequencies up front instead.

Source code in src/scale_forecasting/seasonality.py
def seasonal_period(freq: str) -> int:
    """Steps in one seasonal cycle for ``freq`` (e.g. 7 for daily, 12 for monthly).

    Falls back to a weekly-ish default for an unrecognized frequency so a model still
    runs; use `is_supported` to reject unknown frequencies up front instead.
    """
    return _SEASONAL_PERIOD.get(freq, _DEFAULT_PERIOD)