Skip to content

How to Handle Complex Seasonality

This guide shows you three approaches for handling multiple overlapping seasonal periods (e.g., weekly + annual in daily data) in yohou.

Prerequisites

Try it interactively

How to Tune Fourier Seasonality Terms

Explore how Fourier harmonic count affects seasonal fit quality, compare Fourier vs Pattern seasonality, and tune harmonics jointly with GridSearchCV.

ViewOpen in marimo

Choose an Approach

Situation Recommended approach
Periods are known; seasonal shape is smooth Fourier terms
Shape is irregular; you have 3+ complete cycles per period Nested decomposition
You have exogenous features or a tree-based model Feature engineering

These approaches can be combined: use decomposition for the dominant period and Fourier terms for a secondary one, or pair calendar features with Fourier harmonics.

Use Fourier Terms for Smooth Patterns

FourierSeasonalityForecaster represents a seasonal pattern as sine/cosine pairs at specified harmonics. Each instance handles one period, so wrap multiple instances in a DecompositionPipeline for overlapping periodicities:

from yohou.compose import DecompositionPipeline
from yohou.stationarity.seasonality import FourierSeasonalityForecaster

pipeline = DecompositionPipeline(
    forecasters=[
        ("weekly", FourierSeasonalityForecaster(seasonality=7, harmonics=[1, 2, 3])),
        ("annual", FourierSeasonalityForecaster(seasonality=365.25, harmonics=[1, 2, 3, 4, 5])),
    ],
)

More harmonics capture more complex shapes but use more degrees of freedom. Start with 2 to 4 and increase if the residuals still show seasonal structure.

Nest Decomposition for Irregular Shapes

When the seasonal shape is irregular and you have enough data to estimate it directly, use PatternSeasonalityForecaster inside a DecompositionPipeline. Each forecaster fits the residuals left by the previous one:

from yohou.compose import DecompositionPipeline
from yohou.stationarity.seasonality import PatternSeasonalityForecaster

pipeline = DecompositionPipeline(
    forecasters=[
        ("weekly", PatternSeasonalityForecaster(seasonality=7)),
        ("annual", PatternSeasonalityForecaster(seasonality=365)),
    ],
)

Order by period length (shortest first) so the dominant short cycle is removed before estimating the longer one.

Add Calendar and Fourier Features for Tree-based Models

For tree-based regressors, encoding seasonal patterns as features often outperforms explicit decomposition. Use CalendarFeatureTransformer and FourierFeatureTransformer as part of a FeatureUnion:

from yohou.compose import FeatureUnion
from yohou.point import PointReductionForecaster
from yohou.preprocessing.calendar import CalendarFeatureTransformer
from yohou.preprocessing.time_features import FourierFeatureTransformer
from sklearn.ensemble import GradientBoostingRegressor

feature_pipeline = FeatureUnion([
    ("calendar", CalendarFeatureTransformer(features=["day_of_week", "month"])),
    ("fourier", FourierFeatureTransformer(seasonality=365.25, harmonics=[1, 2, 3])),
])

forecaster = PointReductionForecaster(
    estimator=GradientBoostingRegressor(),
    feature_transformer=feature_pipeline,
    target_as_feature="transformed",  # uses the transformed target as an additional feature
)

If you also need holiday indicators, add a HolidayFeatureTransformer step to the pipeline.

See Also