ThetaForecaster¶
- class omnicast.ThetaForecaster(seasonal_period=None)[source]
Bases:
BaseForecasterClassical Theta method (Assimakopoulos & Nikolopoulos, 2000).
Compatible Python reimplementation of R’s forecast::thetaf (Hyndman, package forecast, GPL-3) – not a call into R, and not yet verified against its numerical output; see CONTRIBUTING.md. It decomposes the series into two “theta lines”: the theta=0 line is the long-term linear trend, and the theta=2 line doubles local curvature around that trend. The theta=2 line is extrapolated with simple exponential smoothing, the theta=0 line is extrapolated linearly, and the two forecasts are averaged with equal weight, following Assimakopoulos & Nikolopoulos (2000), “The theta model: a decomposition approach to forecasting”, International Journal of Forecasting 16(4):521-530.
When seasonal_period is given, the series is deseasonalized first with a multiplicative classical decomposition (statsmodels.seasonal_decompose) and forecasts are reseasonalized afterwards; this requires strictly positive values and at least two full seasonal cycles.
Prediction intervals use the same residual-variance random-walk scaling (sqrt(sigma2 * h)) as NaiveForecaster, an approximation rather than the exact ETS(A,N,N) state-space interval that R’s implementation derives from the SES equivalence proven by Hyndman & Billah (2003), “Unmasking the Theta method”, International Journal of Forecasting 19(2):287-290.
Supported indexes: any index accepted by future_index (PeriodIndex, DatetimeIndex with a regular frequency, RangeIndex, or numeric Index). Minimum sample size: 4 observations, or 2 * seasonal_period when seasonal.
Examples
>>> import pandas as pd >>> from omnicast import ThetaForecaster >>> y = pd.Series([10.0, 12.0, 11.0, 13.0, 15.0, 14.0]) >>> model = ThetaForecaster().fit(y) >>> model.predict(horizon=2).mean.round(2).tolist() [14.05, 14.49]
Pass
seasonal_periodto deseasonalize first (multiplicative decomposition) and reseasonalize the forecast afterward; this requires strictly positive values and at least two full seasonal cycles, e.g.ThetaForecaster(seasonal_period=12)on two years of monthly data.Notes
When to use this model¶ Best for
A strong, fast default before reaching for a full state-space model; a good general-purpose replacement for the baselines
Avoid when
You need exact parity with R’s
forecast::thetafintervals, or a model that supports exogenous regressorsHandles trend
Yes (linear long-term trend line)
Handles seasonality
Yes, via
seasonal_period(multiplicative decomposition)Extra dependencies
None
Min. observations
4, or
2 * seasonal_periodwhen seasonal- Parameters:
seasonal_period (int | None)
- fit(y, X=None)
- Return type:
- Parameters:
- fit_predict(y, horizon, **kwargs)
The package’s first R port: a compatible pure-Python reimplementation of R’s
forecast::thetaf (classical Theta method, Assimakopoulos & Nikolopoulos
2000). It decomposes the series into a linear long-term trend and a
curvature-doubled “theta line” extrapolated with simple exponential
smoothing, then averages the two. Strong, fast, and a good default before
reaching for a full state-space model.
Non-seasonal¶
from omnicast import ThetaForecaster
model = ThetaForecaster().fit(y) # seasonal_period=None
forecast = model.predict(horizon=6)
Seasonal¶
Pass seasonal_period to deseasonalize first (multiplicative classical
decomposition) and reseasonalize the forecast afterward. This requires
strictly positive values and at least two full seasonal cycles.
model = ThetaForecaster(seasonal_period=12).fit(y)
forecast = model.predict(horizon=6, level=[80, 95])
print(forecast.to_frame())
mean lower_80 upper_80 lower_95 upper_95
2026-01 206.46 203.31 209.60 201.65 211.27
2026-02 216.66 212.21 221.11 209.86 223.46
2026-03 222.14 216.69 227.59 213.81 230.47
2026-04 227.11 220.82 233.40 217.49 236.73
2026-05 225.00 217.97 232.03 214.25 235.76
2026-06 219.82 212.11 227.52 208.03 231.60
Unlike the flat DriftForecaster trend, Theta’s forecast tracks both the
upward trend and the yearly seasonal shape. alpha_ is the fitted
exponential-smoothing weight for the theta=2 line:
model.alpha_ # smoothing_level chosen by SES's own MLE, not a hyperparameter you set
Known deviation from R
Prediction intervals here use the same residual-variance random-walk scaling
(sqrt(sigma2 * h)) as NaiveForecaster, not the exact ETS(A,N,N)
state-space interval R’s thetaf derives from the SES equivalence (Hyndman
& Billah 2003). Point forecasts follow the same method; interval widths will
differ slightly. See CONTRIBUTING.md for the parity-fixture policy.
Minimum sample size is 4 observations (or 2 * seasonal_period when
seasonal); fit raises ValueError below that, or if seasonal values are
non-positive.