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 > 1when 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-1free parameters; the first seasonal effect is computed as the
negative sum of the others. This is the Durbin-Koopman [1] formulation. - False: Estimates all
sparameters. You must enforce the constraint yourself, typically via aZeroSumNormalprior. Use this when you want symmetric treatment of all seasons.Warning
With
remove_first_state=True, the first element ofstate_namesdoes 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_namesor 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, setstart_state="Wed"orstart_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
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.
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
FrequencySeasonalityAlternative 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
coordsdata_namesexog_namesk_endogk_posdefk_statesn_seasonsNumber of unique seasonal parameters (season_length - 1 if remove_first_state, else season_length).
n_timestepsneeds_exog_dataobserved_state_namesparam_dimsparam_infoparam_namesshock_namesstate_names