Theta method for univariate time series forecasting.

Classes

ThetaForecaster

class algorithms.timeseries.theta.ThetaForecaster(Regressor)

Theta method for univariate forecasting by curvature decomposition.

The Theta method decomposes a series into so-called theta lines, each obtained by modifying the local curvature (the second differences) of the original series by a coefficient \theta. The classic formulation uses two lines: \theta = 0, which is the ordinary least-squares straight line through the data, and \theta = 2, which doubles the curvature. Each line is extrapolated separately and the two forecasts are averaged. Despite its simplicity, the method won the M3 forecasting competition.
Constructor
__init__(
    self,
    theta: float = 2.0,
    alpha: float | None = None,
    season_length: int | None = None,
    seasonal: str = 'mul',
    seasonality_test: bool = True,
)

Overview

  1. Optionally deseasonalise the series with a classical decomposition
when season_length is given and a seasonality test fires.
  1. Fit an ordinary least-squares line a + b t to the
(deseasonalised) series.
  1. Build the theta line
Z_{\theta}(t) = \theta y_t + (1 - \theta)(a + b t).
  1. Extrapolate the \theta = 0 line by simple linear
extrapolation, and the \theta line by simple exponential smoothing (SES).
  1. Combine the two extrapolations with weights 1/\theta and
1 - 1/\theta, then reseasonalise.

Theory

A theta line rescales the second differences of the series,

\nabla^2 Z_{\theta}(t) = \theta \, \nabla^2 y_t ,

so \theta = 0 removes all curvature (a straight line) and \theta > 1 amplifies it. The solution of that difference equation with the two boundary conditions that minimise the squared deviation from the data is

Z_{\theta}(t) = \theta y_t + (1 - \theta)(a + b t),

with a, b the OLS intercept and slope of y on t = 1, \dots, n. The combined forecast is

\hat{y}_{n+h} = \frac{1}{\theta} \, \ell_n(Z_{\theta}) + \left(1 - \frac{1}{\theta}\right) \bigl(a + b (n + h)\bigr),

where \ell_n(Z_{\theta}) is the SES level of the theta line.

Equivalence with SES plus drift. Hyndman and Billah (2003) showed that for \theta = 2 and equal weights the method is exactly simple exponential smoothing with a drift of b / 2:

\hat{y}_{n+h} = \ell_n + \frac{b}{2} \left[ h - 1 + \frac{1}{\alpha} - \frac{(1 - \alpha)^n}{\alpha} \right],

where \ell_n is the SES level of the original series initialised at \ell_0 = y_1. This implementation reproduces that identity to machine precision.

Parameters

theta
float = 2.0
Curvature coefficient of the second theta line. Must be strictly positive. theta=2 gives the classic Theta method.
alpha
float = None
SES smoothing parameter for the theta line. When None it is chosen on a deterministic grid by minimising the in-sample sum of squared one-step errors of the theta line.
season_length
int = None
Number of periods in a season. When None (or 1) no seasonal adjustment is attempted.
seasonal
{"mul", "add"} = "mul"
Type of seasonality used by the classical decomposition. "mul" falls back to "add" when the series is not strictly positive.
seasonality_test
bool = True
When True, seasonal adjustment is applied only if the autocorrelation at lag season_length is significant at the 90% level.

Attributes

alpha_
float
SES smoothing parameter actually used.
intercept_
float
OLS intercept :math:`a` of the deseasonalised series.
slope_
float
OLS slope :math:`b` of the deseasonalised series.
level_
float
Final SES level of the theta line.
drift_
float
Slope of the combined forecast function with respect to the horizon, :math:`b (1 - 1/\theta)`. For the classic :math:`\theta = 2` this is :math:`b / 2`, the drift of the equivalent SES-with-drift model.
seasonal_indices_
np.ndarray or None
Estimated seasonal indices of length season_length.
is_seasonal_
bool
Whether seasonal adjustment was applied.
seasonal_mode_
str or None
The decomposition actually used, "mul" or "add".
fitted_values_
np.ndarray
In-sample one-step-ahead forecasts.
resid_
np.ndarray
In-sample residuals.
n_obs_
int
Number of training observations.

Notes

Complexity:

  • Training: O(n) for a fixed alpha, O(gn) when
alpha is optimised over a grid of g values.
  • Prediction: O(h).
When to use ThetaForecaster:
  • Short and medium series where a robust, low-variance benchmark is
wanted; it is very hard to beat on the M-competition data.
  • Series with a clear local trend that should be damped rather than
extrapolated at full strength.
  • Monthly or quarterly business data, combined with season_length.
  • As a baseline against which ARIMA or exponential smoothing is judged.

References

Assimakopoulos2000
Assimakopoulos, V., & Nikolopoulos, K. (2000). The theta model: a decomposition approach to forecasting. International Journal of Forecasting, 16(4), 521-530. :doi:`10.1016/S0169-2070(00)00066-2`
Hyndman2003
Hyndman, R. J., & Billah, B. (2003). Unmasking the Theta method. International Journal of Forecasting, 19(2), 287-290. :doi:`10.1016/S0169-2070(01)00143-1`
Fiorucci2016
Fiorucci, J. A., Pellegrini, T. R., Louzada, F., Petropoulos, F., & Koehler, A. B. (2016). Models for optimising the theta method and their relationship to state space models. International Journal of Forecasting, 32(4), 1151-1161. :doi:`10.1016/j.ijforecast.2016.02.005`
python
>>> import numpy as np
>>> from tuiml.algorithms.timeseries.theta import ThetaForecaster
>>> y = np.arange(1.0, 21.0)
>>> model = ThetaForecaster(theta=2.0, alpha=0.3).fit(y)
>>> forecast = model.predict(steps=3)
>>> forecast.shape
(3,)
>>> bool(np.all(np.diff(forecast) > 0))
True

Methods

get_parameter_schema (cls) -> Dict[str, Dict[str, Any]]

Return JSON Schema for constructor parameters.

get_capabilities (cls) -> List[str]

Return supported capabilities.

get_complexity (cls) -> str

Return complexity analysis.

get_references (cls) -> List[str]

Return academic citations.

fit (self, y: np.ndarray, X: Optional[np.ndarray]=None) -> 'ThetaForecaster'

Fit the Theta model to a univariate series.

Parameters
y
np.ndarray of shape (n_samples,)
Time series values.
X
np.ndarray = None
Ignored. Present for API consistency with regressors.
Returns
self
ThetaForecaster
Fitted estimator.
predict (self, steps: int=1, X: Optional[np.ndarray]=None) -> np.ndarray

Forecast future values of the series.

Parameters
steps
int = 1
Number of future time steps to forecast.
X
np.ndarray = None
Ignored. Present for API consistency with regressors.
Returns
forecast
np.ndarray of shape (steps,)
Forecasted values.
fit_predict (self, y: np.ndarray, steps: int=1) -> np.ndarray

Fit the model and forecast in one call.

Parameters
y
np.ndarray of shape (n_samples,)
Time series values.
steps
int = 1
Number of future time steps to forecast.
Returns
forecast
np.ndarray of shape (steps,)
Forecasted values.