TimeSeasonality#

class pymc_extras.statespace.models.structural.TimeSeasonality(season_length: int, duration: int = 1, innovations: bool = True, name: str | None = None, state_names: Sequence[str] | None = None, remove_first_state: bool = True, observed_state_names: Sequence[str] | None = None, share_states: bool = False, start_state: str | int | None = None, use_time_varying: bool = True)[source]#

Create a TimeSeasonality component for a state space model.

__init__(season_length: int, duration: int = 1, innovations: bool = True, name: str | None = None, state_names: Sequence[str] | None = None, remove_first_state: bool = True, observed_state_names: Sequence[str] | None = None, share_states: bool = False, start_state: str | int | None = None, use_time_varying: bool = True)[source]#

Deterministic seasonal pattern with optional stochastic drift.

Many time series exhibit regular patterns tied to the calendar: sales spike in December, electricity demand peaks on weekday evenings, ice cream consumption rises in summer. This component captures such effects by estimating a distinct effect for each period within a seasonal cycle, subject to the constraint that effects sum to zero over a complete cycle. This ensures the seasonality captures deviations from the level, not the level itself.

Parameters:
  • season_length (int) – Number of periods in one complete seasonal cycle. Must be at least 2.

  • duration (int, default 1) – Number of observations each seasonal effect spans. The default (1) means each observation gets its own seasonal effect. Set duration > 1 when your data frequency is finer than your seasonal pattern—for example, daily observations with monthly seasonality (season_length=12, duration≈30).

  • innovations (bool, default True) – If True, seasonal effects evolve stochastically over time, allowing the seasonal pattern to change gradually. If False, the pattern is deterministic (constant across all cycles).

  • name (str, optional) – Label for this component, used in parameter names and coordinates. Defaults to "Seasonal[s={season_length}, d={duration}]".

  • state_names (sequence of str, optional) –

    Labels for each seasonal period. Length must equal season_length. These appear in output coordinates, making results interpretable. For example, a weekly season might use the names of the days of the week.

    Defaults to ["{name}_0", "{name}_1", ...].

  • remove_first_state (bool, default True) –

    Controls how the sum-to-zero constraint is enforced.

    • True (recommended): Estimates s-1 free parameters; the first seasonal effect is computed as the

    negative sum of the others. This is the Durbin-Koopman [1] formulation. - False: Estimates all s parameters. You must enforce the constraint yourself, typically via a ZeroSumNormal prior. Use this when you want symmetric treatment of all seasons.

    Warning

    With remove_first_state=True, the first element of state_names does not appear in the parameter coordinates (since it’s not a free parameter).

  • observed_state_names (sequence of str, optional) – Labels for observed series. Defaults to ["data"] for univariate models.

  • share_states (bool, default False) – For multivariate models: if True, all series share the same seasonal pattern; if False, each series has independent seasonal effects. Ignored if k_endog=1.

  • start_state (str or int, optional) –

    Which seasonal period corresponds to the first observation (t=0). Specify as either a name from state_names or an integer index. Use this when your sample doesn’t start at the beginning of a cycle— for instance, if you have weekly seasonality but your data begins on a Wednesday, set start_state="Wed" or start_state=3.

    The index refers to positions in the original state_names (before any removal).

  • use_time_varying (bool, default True) – If True and duration > 1, the transition matrix will be time-varying to correctly handle the shifting seasonal effects. If False, a single very large and sparse transition matrix will be used. Ignored if duration = 1. The time-varying approach is suggested for now to keep the state space small.

Notes

The Model

The observation at time \(t\) is influenced by a seasonal effect \(\gamma_t\):

\[y_t = \ldots + \gamma_t + \varepsilon_t\]

where the seasonal effect cycles through \(s\) values, repeating every \(s\) observations (or every \(s \times d\) observations if duration > 1).

To ensure identifiability—separating seasonality from the overall level—we impose:

\[\sum_{j=0}^{s-1} \gamma_j = 0\]

Enforcing the Constraint: Two Approaches

  1. Durbin-Koopman formulation (remove_first_state=True)

    Parameterize only \(\gamma_1, \ldots, \gamma_{s-1}\) as free parameters, then define \(\gamma_0 = -\sum_{j=1}^{s-1} \gamma_j\). The state vector tracks these \(s-1\) values, and the transition matrix rotates through the cycle while computing the implied \(\gamma_0\) automatically. The state transition follows:

    \[\begin{split}T_\gamma = \begin{bmatrix} -1 & -1 & \cdots & -1 \\ 1 & 0 & \cdots & 0 \\ 0 & 1 & \ddots & \vdots \\ \vdots & & \ddots & 0 \end{bmatrix}\end{split}\]

    This formulation is statistically efficient (minimal state dimension) and guarantees the constraint by construction.

  2. Unconstrained formulation (remove_first_state=False)

    All \(s\) seasonal effects are free parameters. The state simply cycles via a permutation matrix. The sum-to-zero constraint must be imposed through the prior (e.g., pm.ZeroSumNormal). This formulation treats all states symmetrically and can be more intuitive when you want to directly interpret each seasonal effect, but it has a slightly larger state dimension.

Duration: Handling Mismatched Frequencies

When duration > 1, each seasonal effect is held constant for \(d\) consecutive observations before transitioning to the next. This produces a step-function pattern and is useful when data frequency exceeds seasonal frequency (e.g., when observations are daily, but the seasonal pattern repeats monthly).

The total cycle length becomes \(s \times d\) observations.

Stochastic Seasonality

With innovations=True, seasonal effects evolve over time:

\[\gamma_{j,t+1} = \gamma_{j,t} + \omega_{j,t}, \quad \omega_{j,t} \sim N(0, \sigma^2_\gamma)\]

This allows the seasonal pattern to adapt—capturing phenomena like shifting holiday shopping patterns or changing commuter behavior. The latent season effect evolves with a Gaussian random walk. The smoothness of evolution is controlled by the prior on sigma_{name}.

Examples

Weekly seasonality for daily data:

>>> mod = st.TimeSeasonality(
...     season_length=7,
...     state_names=['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
...     start_state='Mon',  # Data starts on Monday
...     name='day_of_week',
... )

Monthly seasonality for daily data (each month held constant for ~30 days):

>>> mod = st.TimeSeasonality(
...     season_length=12,
...     duration=30,
...     name='month',
... )

See also

FrequencySeasonality

Alternative parameterization using Fourier basis functions. More compact for long seasonal periods but less interpretable (effects do not map to specific calendar periods). Can handle non-integer season lengths.

References

Methods

__init__(season_length[, duration, ...])

Deterministic seasonal pattern with optional stochastic drift.

build([name, filter_type, verbose, mode])

Build a StructuralTimeSeries statespace model from the current component(s)

make_and_register_data(name, shape[, dtype])

Helper function to create a pytensor symbolic variable and register it in the _name_to_data dictionary

make_and_register_variable(name, shape[, dtype])

Helper function to create a pytensor symbolic variable and register it in the _name_to_variable dictionary

make_symbolic_graph()

populate_component_properties()

set_coords()

Set default coordinate specifications.

set_data_info()

Set default data specifications.

set_parameters()

Set component parameter specifications.

set_shocks()

Set default shock specifications based on the number of sources of innovations in the component.

set_states()

Set default state specification based on number of states and endogenous variables in the component.

Attributes

coords

data_names

exog_names

k_endog

k_posdef

k_states

n_seasons

Number of unique seasonal parameters (season_length - 1 if remove_first_state, else season_length).

n_timesteps

needs_exog_data

observed_state_names

param_dims

param_info

param_names

shock_names

state_names