@@ -36,13 +36,104 @@ class StructuralTimeSeries(PyMCStateSpace):
36
36
decomposing a univariate time series into level, trend, seasonal, and cycle components. It also admits the
37
37
possibility of exogenous regressors. Unlike the SARIMAX framework, the time series is not assumed to be stationary.
38
38
39
+ Parameters
40
+ ----------
41
+ ssm : PytensorRepresentation
42
+ The state space representation containing system matrices.
43
+ name : str
44
+ Name of the model. If None, defaults to "StructuralTimeSeries".
45
+ state_names : list[str]
46
+ Names of the hidden states in the model.
47
+ observed_state_names : list[str]
48
+ Names of the observed variables.
49
+ data_names : list[str]
50
+ Names of data variables expected by the model.
51
+ shock_names : list[str]
52
+ Names of innovation/shock processes.
53
+ param_names : list[str]
54
+ Names of model parameters.
55
+ exog_names : list[str]
56
+ Names of exogenous variables.
57
+ param_dims : dict[str, tuple[int]]
58
+ Dimension specifications for parameters.
59
+ coords : dict[str, Sequence]
60
+ Coordinate specifications for the model.
61
+ param_info : dict[str, dict[str, Any]]
62
+ Information about parameters including shapes and constraints.
63
+ data_info : dict[str, dict[str, Any]]
64
+ Information about data variables.
65
+ component_info : dict[str, dict[str, Any]]
66
+ Information about model components.
67
+ measurement_error : bool
68
+ Whether the model includes measurement error.
69
+ name_to_variable : dict[str, Variable]
70
+ Mapping from parameter names to PyTensor variables.
71
+ name_to_data : dict[str, Variable] | None, optional
72
+ Mapping from data names to PyTensor variables. Default is None.
73
+ verbose : bool, optional
74
+ Whether to print model information. Default is True.
75
+ filter_type : str, optional
76
+ Type of Kalman filter to use. Default is "standard".
77
+ mode : str | Mode | None, optional
78
+ PyTensor compilation mode. Default is None.
79
+
39
80
Notes
40
81
-----
82
+ The structural time series model decomposes a time series into interpretable components:
41
83
42
84
.. math::
43
85
44
- y_t = \mu_t + \gamma_t + c_t + \varepsilon_t
86
+ y_t = \mu_t + \nu_t + \cdots + \gamma_t + c_t + \xi_t + \varepsilon_t
87
+
88
+ Where:
89
+ - :math:`\mu_t` is the level component
90
+ - :math:`\nu_t` is the slope/trend component
91
+ - :math:`\cdots` represents higher-order trend components
92
+ - :math:`\gamma_t` is the seasonal component
93
+ - :math:`c_t` is the cycle component
94
+ - :math:`\xi_t` is the autoregressive component
95
+ - :math:`\varepsilon_t` is the measurement error
96
+
97
+ The model is built by combining individual components (e.g., LevelTrendComponent,
98
+ TimeSeasonality, CycleComponent) using the addition operator. Each component
99
+ contributes to the overall state space representation.
100
+
101
+ Examples
102
+ --------
103
+ Create a model with trend and seasonal components:
104
+
105
+ .. code:: python
106
+
107
+ from pymc_extras.statespace import structural as st
108
+ import pymc as pm
109
+ import pytensor.tensor as pt
45
110
111
+ trend = st.LevelTrendComponent(order=2 innovations_order=1)
112
+ seasonal = st.TimeSeasonality(season_length=12, innovations=True)
113
+ error = st.MeasurementError()
114
+
115
+ ss_mod = (trend + seasonal + error).build()
116
+
117
+ with pm.Model(coords=ss_mod.coords) as model:
118
+ P0 = pm.Deterministic('P0', pt.eye(ss_mod.k_states) * 10, dims=ss_mod.param_dims['P0'])
119
+
120
+ initial_trend = pm.Normal('initial_trend', sigma=10, dims=ss_mod.param_dims['initial_trend'])
121
+ sigma_trend = pm.HalfNormal('sigma_trend', sigma=1, dims=ss_mod.param_dims['sigma_trend'])
122
+
123
+ seasonal_coefs = pm.Normal('seasonal_coefs', sigma=1, dims=ss_mod.param_dims['seasonal_coefs'])
124
+ sigma_seasonal = pm.HalfNormal('sigma_seasonal', sigma=1)
125
+
126
+ sigma_obs = pm.Exponential('sigma_obs', 1, dims=ss_mod.param_dims['sigma_obs'])
127
+
128
+ ss_mod.build_statespace_graph(data)
129
+ idata = pm.sample()
130
+
131
+ References
132
+ ----------
133
+ .. [1] Harvey, A. C. (1989). Forecasting, structural time series models and the
134
+ Kalman filter. Cambridge University Press.
135
+ .. [2] Durbin, J., & Koopman, S. J. (2012). Time series analysis by state space
136
+ methods (2nd ed.). Oxford University Press.
46
137
"""
47
138
48
139
def __init__ (
@@ -306,7 +397,7 @@ def _extract_and_transform_variable(idata, new_state_names):
306
397
dropped_vars = set (var_names ) - set (latent_names )
307
398
if len (dropped_vars ) > 0 :
308
399
_log .warning (
309
- f' Variables { ", " .join (dropped_vars )} do not contain all hidden states (their last dimension '
400
+ f" Variables { ', ' .join (dropped_vars )} do not contain all hidden states (their last dimension "
310
401
f"is not { self .k_states } ). They will not be present in the modified idata."
311
402
)
312
403
if len (dropped_vars ) == len (var_names ):
@@ -333,23 +424,63 @@ class Component:
333
424
334
425
Parameters
335
426
----------
336
- name: str
337
- The name of the component
338
- k_endog: int
339
- Number of endogenous variables being modeled.
340
- k_states: int
341
- Number of hidden states in the component model
342
- k_posdef: int
343
- Rank of the state covariance matrix, or the number of sources of innovations in the component model
344
- observed_state_names: str or list or str, optional
345
- Names of the observed states associated with this component. Must have the same length as k_endog. If not
346
- provided, generic names are generated: ``observed_state_1, observed_state_2, ..., observed_state_k_endog``.
347
- measurement_error: bool
348
- Whether the observation associated with the component has measurement error. Default is False.
349
- combine_hidden_states: bool
350
- Flag for the ``extract_hidden_states_from_data`` method. When ``True``, hidden states from the component model
351
- are extracted as ``hidden_states[:, np.flatnonzero(Z)]``. Should be True in models where hidden states
352
- individually have no interpretation, such as seasonal or autoregressive components.
427
+ name : str
428
+ The name of the component.
429
+ k_endog : int
430
+ Number of endogenous (observed) variables being modeled.
431
+ k_states : int
432
+ Number of hidden states in the component model.
433
+ k_posdef : int
434
+ Rank of the state covariance matrix, or the number of sources of innovations
435
+ in the component model.
436
+ state_names : list[str] | None, optional
437
+ Names of the hidden states. If None, defaults to empty list.
438
+ observed_state_names : list[str] | None, optional
439
+ Names of the observed states associated with this component. Must have the same
440
+ length as k_endog. If None, defaults to empty list.
441
+ data_names : list[str] | None, optional
442
+ Names of data variables expected by the component. If None, defaults to empty list.
443
+ shock_names : list[str] | None, optional
444
+ Names of innovation/shock processes. If None, defaults to empty list.
445
+ param_names : list[str] | None, optional
446
+ Names of component parameters. If None, defaults to empty list.
447
+ exog_names : list[str] | None, optional
448
+ Names of exogenous variables. If None, defaults to empty list.
449
+ representation : PytensorRepresentation | None, optional
450
+ Pre-existing state space representation. If None, creates a new one.
451
+ measurement_error : bool, optional
452
+ Whether the component includes measurement error. Default is False.
453
+ combine_hidden_states : bool, optional
454
+ Whether to combine hidden states when extracting from data. Should be True for
455
+ components where individual states have no interpretation (e.g., seasonal,
456
+ autoregressive). Default is True.
457
+ component_from_sum : bool, optional
458
+ Whether this component is created from combining other components. Default is False.
459
+ obs_state_idxs : np.ndarray | None, optional
460
+ Indices indicating which states contribute to observed variables. If None,
461
+ defaults to None.
462
+
463
+ Examples
464
+ --------
465
+ Create a simple trend component:
466
+
467
+ .. code:: python
468
+
469
+ from pymc_extras.statespace import structural as st
470
+
471
+ trend = st.LevelTrendComponent(order=2, innovations_order=1)
472
+ seasonal = st.TimeSeasonality(season_length=12, innovations=True)
473
+ model = (trend + seasonal).build()
474
+
475
+ print(f"Model has {model.k_states} states and {model.k_posdef} innovations")
476
+
477
+ See Also
478
+ --------
479
+ StructuralTimeSeries : The complete model class that combines components.
480
+ LevelTrendComponent : Component for modeling level and trend.
481
+ TimeSeasonality : Component for seasonal effects.
482
+ CycleComponent : Component for cyclical effects.
483
+ RegressionComponent : Component for regression effects.
353
484
"""
354
485
355
486
def __init__ (
@@ -637,7 +768,7 @@ def _combine_component_info(self, other):
637
768
638
769
def _make_combined_name (self ):
639
770
components = self ._component_info .keys ()
640
- name = f' StateSpace[{ ", " .join (components )} ]'
771
+ name = f" StateSpace[{ ', ' .join (components )} ]"
641
772
return name
642
773
643
774
def __add__ (self , other ):
0 commit comments