Skip to content

seasonal_emphasis_weight

yohou.utils.weighting.seasonal_emphasis_weight(seasonality, emphasis=2.0)

Generate weights emphasizing specific seasonal positions.

Creates a callable that gives higher weights to times matching the most recent seasonal position (e.g., same day of week, same day of month):

\[w(t) = \begin{cases} \text{emphasis} & \text{if } t \bmod s \equiv t_n \bmod s \\ 1 & \text{otherwise} \end{cases}\]

where \(s\) is the seasonal period and \(t_n\) is the most recent time.

Parameters

Name Type Description Default
seasonality int or list of int

Seasonal period(s) (e.g., 7 for weekly, 12 for monthly, or [7, 30] for both weekly and monthly patterns). Determines which times are in-phase with the most recent observation. If multiple seasonalities provided, times matching ANY pattern receive emphasis.

required
emphasis float

Weight multiplier for in-phase times. In-phase times receive weight emphasis, out-of-phase times receive weight 1.0. Values > 1.0 emphasize seasonality, values < 1.0 de-emphasize.

2.0

Returns

Type Description
Callable[[Series], Series]

Function accepting time series (datetime) and returning weight series (float64). Times matching the most recent seasonal position(s) receive higher weights.

See Also

Examples

>>> import polars as pl
>>> from datetime import datetime
>>> times = pl.Series(
...     "time",
...     [
...         datetime(2024, 1, 1),  # Monday
...         datetime(2024, 1, 2),  # Tuesday
...         datetime(2024, 1, 8),  # Monday (same weekday as most recent)
...         datetime(2024, 1, 9),  # Tuesday (most recent)
...     ],
... )
>>> weight_fn = seasonal_emphasis_weight(seasonality=7, emphasis=2.0)
>>> weights = weight_fn(times)
>>> weights
shape: (4,)
Series: 'weight' [f64]
[
    1.0
    1.0
    1.0
    2.0
]

Multiple seasonalities (weekly + monthly):

>>> times = pl.Series("time", [datetime(2024, 1, i) for i in range(1, 32)])
>>> weight_fn = seasonal_emphasis_weight(seasonality=[7, 30], emphasis=2.0)
>>> weights = weight_fn(times)
>>> # Times matching day-of-week OR day-of-month get emphasized

Source Code

Show/Hide source
def seasonal_emphasis_weight(
    seasonality: int | list[int],
    emphasis: float = 2.0,
) -> Callable[[pl.Series], pl.Series]:
    r"""Generate weights emphasizing specific seasonal positions.

    Creates a callable that gives higher weights to times matching the most
    recent seasonal position (e.g., same day of week, same day of month):

    $$w(t) = \begin{cases} \text{emphasis} & \text{if } t \bmod s \equiv t_n \bmod s \\ 1 & \text{otherwise} \end{cases}$$

    where $s$ is the seasonal period and $t_n$ is the most recent time.

    Parameters
    ----------
    seasonality : int or list of int
        Seasonal period(s) (e.g., 7 for weekly, 12 for monthly, or [7, 30] for
        both weekly and monthly patterns). Determines which times are in-phase
        with the most recent observation. If multiple seasonalities provided,
        times matching ANY pattern receive emphasis.
    emphasis : float, default=2.0
        Weight multiplier for in-phase times. In-phase times receive weight
        `emphasis`, out-of-phase times receive weight 1.0. Values > 1.0
        emphasize seasonality, values < 1.0 de-emphasize.

    Returns
    -------
    Callable[[pl.Series], pl.Series]
        Function accepting time series (datetime) and returning weight series
        (float64). Times matching the most recent seasonal position(s) receive
        higher weights.

    See Also
    --------
    - [`exponential_decay_weight`][yohou.utils.weighting.exponential_decay_weight] : Exponential decay weights for recent times.
    - [`linear_decay_weight`][yohou.utils.weighting.linear_decay_weight] : Linear decay weights for recent times.
    - [`compose_weights`][yohou.utils.weighting.compose_weights] : Compose multiple weight functions by multiplication.
    - [`validate_callable_signature`][yohou.utils.weighting.validate_callable_signature] : Validate callable signature for time weighting.
    - [`BaseReductionForecaster`][yohou.base.reduction.BaseReductionForecaster] : Reduction forecaster supporting time_weight.

    Examples
    --------
    >>> import polars as pl
    >>> from datetime import datetime
    >>> times = pl.Series(
    ...     "time",
    ...     [
    ...         datetime(2024, 1, 1),  # Monday
    ...         datetime(2024, 1, 2),  # Tuesday
    ...         datetime(2024, 1, 8),  # Monday (same weekday as most recent)
    ...         datetime(2024, 1, 9),  # Tuesday (most recent)
    ...     ],
    ... )
    >>> weight_fn = seasonal_emphasis_weight(seasonality=7, emphasis=2.0)
    >>> weights = weight_fn(times)
    >>> weights  # doctest: +NORMALIZE_WHITESPACE
    shape: (4,)
    Series: 'weight' [f64]
    [
        1.0
        1.0
        1.0
        2.0
    ]

    Multiple seasonalities (weekly + monthly):

    >>> times = pl.Series("time", [datetime(2024, 1, i) for i in range(1, 32)])
    >>> weight_fn = seasonal_emphasis_weight(seasonality=[7, 30], emphasis=2.0)
    >>> weights = weight_fn(times)
    >>> # Times matching day-of-week OR day-of-month get emphasized

    """
    # Normalize to list
    seasonalities = [seasonality] if isinstance(seasonality, int) else seasonality

    def _weight_fn(time: pl.Series) -> pl.Series:
        """Apply seasonal emphasis weighting to time series.

        Parameters
        ----------
        time : pl.Series
            Time series (datetime type).

        Returns
        -------
        pl.Series
            Weight series with emphasis on times matching seasonal pattern(s).

        """
        n = len(time)
        if n == 1:
            return pl.Series([1.0], dtype=pl.Float64).alias("weight")

        # Compute rank to determine position in sequence
        ranks = time.rank(method="ordinal") - 1

        # Check if any seasonality matches (OR logic)
        in_phase = np.zeros(n, dtype=bool)
        for s in seasonalities:
            # Most recent position modulo this seasonality
            most_recent_phase = int(ranks[-1]) % s
            # Check which positions match the most recent phase
            phases = ranks.to_numpy() % s
            in_phase = in_phase | (phases == most_recent_phase)

        # Apply emphasis to in-phase times
        weights = np.where(in_phase, emphasis, 1.0)
        return pl.Series(weights, dtype=pl.Float64).alias("weight")

    return _weight_fn

Tutorials

The following example notebooks use this component:

  • How to Apply Time-Weighted Training


    Forecasting-Models

    Use time_weight and sample_weight_alignment to emphasise recent or seasonal training samples in PointReductionForecaster, with visualisation of weight curves and alignment strategy comparison.

    View · Open in marimo