"""
Notes
-----
Code written using below textbook as a reference.
Results are checked against the expected outcomes in the text book.
Properties:
Hyndman, Rob J., and George Athanasopoulos. Forecasting: principles and
practice. OTexts, 2014.
Author: Terence L van Zyl
Modified: Kevin Sheppard
"""
import numpy as np
import pandas as pd
from scipy.optimize import basinhopping, brute, minimize
from scipy.spatial.distance import sqeuclidean
from scipy.special import inv_boxcox
from scipy.stats import boxcox
from statsmodels.base.model import Results
from statsmodels.base.wrapper import (populate_wrapper, union_dicts,
ResultsWrapper)
from statsmodels.tools.validation import (array_like, bool_like, float_like,
string_like, int_like)
from statsmodels.tsa.base.tsa_model import TimeSeriesModel
from statsmodels.tsa.tsatools import freq_to_period
import statsmodels.tsa._exponential_smoothers as smoothers
def _holt_init(x, xi, p, y, l, b):
"""Initialization for the Holt Models"""
p[xi.astype(np.bool)] = x
alpha, beta, _, l0, b0, phi = p[:6]
alphac = 1 - alpha
betac = 1 - beta
y_alpha = alpha * y
l[:] = 0
b[:] = 0
l[0] = l0
b[0] = b0
return alpha, beta, phi, alphac, betac, y_alpha
def _holt__(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Simple Exponential Smoothing
Minimization Function
(,)
"""
alpha, beta, phi, alphac, betac, y_alpha = _holt_init(x, xi, p, y, l, b)
for i in range(1, n):
l[i] = (y_alpha[i - 1]) + (alphac * (l[i - 1]))
return sqeuclidean(l, y)
def _holt_mul_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Multiplicative and Multiplicative Damped
Minimization Function
(M,) & (Md,)
"""
alpha, beta, phi, alphac, betac, y_alpha = _holt_init(x, xi, p, y, l, b)
if alpha == 0.0:
return max_seen
if beta > alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1]) + (alphac * (l[i - 1] * b[i - 1]**phi))
b[i] = (beta * (l[i] / l[i - 1])) + (betac * b[i - 1]**phi)
return sqeuclidean(l * b**phi, y)
def _holt_add_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Additive and Additive Damped
Minimization Function
(A,) & (Ad,)
"""
alpha, beta, phi, alphac, betac, y_alpha = _holt_init(x, xi, p, y, l, b)
if alpha == 0.0:
return max_seen
if beta > alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1]) + (alphac * (l[i - 1] + phi * b[i - 1]))
b[i] = (beta * (l[i] - l[i - 1])) + (betac * phi * b[i - 1])
return sqeuclidean(l + phi * b, y)
def _holt_win_init(x, xi, p, y, l, b, s, m):
"""Initialization for the Holt Winters Seasonal Models"""
p[xi.astype(np.bool)] = x
alpha, beta, gamma, l0, b0, phi = p[:6]
s0 = p[6:]
alphac = 1 - alpha
betac = 1 - beta
gammac = 1 - gamma
y_alpha = alpha * y
y_gamma = gamma * y
l[:] = 0
b[:] = 0
s[:] = 0
l[0] = l0
b[0] = b0
s[:m] = s0
return alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma
def _holt_win__mul(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Multiplicative Seasonal
Minimization Function
(,M)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha == 0.0:
return max_seen
if gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1] / s[i - 1]) + (alphac * (l[i - 1]))
s[i + m - 1] = (y_gamma[i - 1] / (l[i - 1])) + (gammac * s[i - 1])
return sqeuclidean(l * s[:-(m - 1)], y)
def _holt_win__add(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Additive Seasonal
Minimization Function
(,A)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha == 0.0:
return max_seen
if gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1]) - (alpha * s[i - 1]) + (alphac * (l[i - 1]))
s[i + m - 1] = y_gamma[i - 1] - (gamma * (l[i - 1])) + (gammac * s[i - 1])
return sqeuclidean(l + s[:-(m - 1)], y)
def _holt_win_add_mul_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Additive and Additive Damped with Multiplicative Seasonal
Minimization Function
(A,M) & (Ad,M)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha * beta == 0.0:
return max_seen
if beta > alpha or gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1] / s[i - 1]) + \
(alphac * (l[i - 1] + phi * b[i - 1]))
b[i] = (beta * (l[i] - l[i - 1])) + (betac * phi * b[i - 1])
s[i + m - 1] = (y_gamma[i - 1] / (l[i - 1] + phi *
b[i - 1])) + (gammac * s[i - 1])
return sqeuclidean((l + phi * b) * s[:-(m - 1)], y)
def _holt_win_mul_mul_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Multiplicative and Multiplicative Damped with Multiplicative Seasonal
Minimization Function
(M,M) & (Md,M)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha * beta == 0.0:
return max_seen
if beta > alpha or gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1] / s[i - 1]) + \
(alphac * (l[i - 1] * b[i - 1]**phi))
b[i] = (beta * (l[i] / l[i - 1])) + (betac * b[i - 1]**phi)
s[i + m - 1] = (y_gamma[i - 1] / (l[i - 1] *
b[i - 1]**phi)) + (gammac * s[i - 1])
return sqeuclidean((l * b**phi) * s[:-(m - 1)], y)
def _holt_win_add_add_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Additive and Additive Damped with Additive Seasonal
Minimization Function
(A,A) & (Ad,A)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha * beta == 0.0:
return max_seen
if beta > alpha or gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1]) - (alpha * s[i - 1]) + \
(alphac * (l[i - 1] + phi * b[i - 1]))
b[i] = (beta * (l[i] - l[i - 1])) + (betac * phi * b[i - 1])
s[i + m - 1] = y_gamma[i - 1] - (gamma * (l[i - 1] + phi * b[i - 1])) + (gammac * s[i - 1])
return sqeuclidean((l + phi * b) + s[:-(m - 1)], y)
def _holt_win_mul_add_dam(x, xi, p, y, l, b, s, m, n, max_seen):
"""
Multiplicative and Multiplicative Damped with Additive Seasonal
Minimization Function
(M,A) & (M,Ad)
"""
alpha, beta, gamma, phi, alphac, betac, gammac, y_alpha, y_gamma = _holt_win_init(
x, xi, p, y, l, b, s, m)
if alpha * beta == 0.0:
return max_seen
if beta > alpha or gamma > 1 - alpha:
return max_seen
for i in range(1, n):
l[i] = (y_alpha[i - 1]) - (alpha * s[i - 1]) + \
(alphac * (l[i - 1] * b[i - 1]**phi))
b[i] = (beta * (l[i] / l[i - 1])) + (betac * b[i - 1]**phi)
s[i + m - 1] = y_gamma[i - 1] - \
(gamma * (l[i - 1] * b[i - 1]**phi)) + (gammac * s[i - 1])
return sqeuclidean((l * phi * b) + s[:-(m - 1)], y)
SMOOTHERS = {('mul', 'add'): smoothers._holt_win_add_mul_dam,
('mul', 'mul'): smoothers._holt_win_mul_mul_dam,
('mul', None): smoothers._holt_win__mul,
('add', 'add'): smoothers._holt_win_add_add_dam,
('add', 'mul'): smoothers._holt_win_mul_add_dam,
('add', None): smoothers._holt_win__add,
(None, 'add'): smoothers._holt_add_dam,
(None, 'mul'): smoothers._holt_mul_dam,
(None, None): smoothers._holt__}
PY_SMOOTHERS = {('mul', 'add'): _holt_win_add_mul_dam,
('mul', 'mul'): _holt_win_mul_mul_dam,
('mul', None): _holt_win__mul,
('add', 'add'): _holt_win_add_add_dam,
('add', 'mul'): _holt_win_mul_add_dam,
('add', None): _holt_win__add,
(None, 'add'): _holt_add_dam,
(None, 'mul'): _holt_mul_dam,
(None, None): _holt__}
class HoltWintersResults(Results):
"""
Holt Winter's Exponential Smoothing Results
Parameters
----------
model : ExponentialSmoothing instance
The fitted model instance
params : dict
All the parameters for the Exponential Smoothing model.
Attributes
----------
params: dict
All the parameters for the Exponential Smoothing model.
params_formatted: pd.DataFrame
DataFrame containing all parameters, their short names and a flag
indicating whether the parameter's value was optimized to fit the data.
fittedfcast: ndarray
An array of both the fitted values and forecast values.
fittedvalues: ndarray
An array of the fitted values. Fitted by the Exponential Smoothing
model.
fcastvalues: ndarray
An array of the forecast values forecast by the Exponential Smoothing
model.
sse: float
The sum of squared errors
level: ndarray
An array of the levels values that make up the fitted values.
slope: ndarray
An array of the slope values that make up the fitted values.
season: ndarray
An array of the seasonal values that make up the fitted values.
aic: float
The Akaike information criterion.
bic: float
The Bayesian information criterion.
aicc: float
AIC with a correction for finite sample sizes.
resid: ndarray
An array of the residuals of the fittedvalues and actual values.
k: int
the k parameter used to remove the bias in AIC, BIC etc.
optimized: bool
Flag indicating whether the model parameters were optimized to fit
the data.
mle_retvals: {None, scipy.optimize.optimize.OptimizeResult}
Optimization results if the parameters were optimized to fit the data.
"""
def __init__(self, model, params, **kwargs):
self.data = model.data
super(HoltWintersResults, self).__init__(model, params, **kwargs)
def predict(self, start=None, end=None):
"""
In-sample prediction and out-of-sample forecasting
Parameters
----------
start : int, str, or datetime, optional
Zero-indexed observation number at which to start forecasting, ie.,
the first forecast is start. Can also be a date string to
parse or a datetime type. Default is the the zeroth observation.
end : int, str, or datetime, optional
Zero-indexed observation number at which to end forecasting, ie.,
the first forecast is start. Can also be a date string to
parse or a datetime type. However, if the dates index does not
have a fixed frequency, end must be an integer index if you
want out of sample prediction. Default is the last observation in
the sample.
Returns
-------
forecast : ndarray
Array of out of sample forecasts.
"""
return self.model.predict(self.params, start, end)
def forecast(self, steps=1):
"""
Out-of-sample forecasts
Parameters
----------
steps : int
The number of out of sample forecasts from the end of the
sample.
Returns
-------
forecast : ndarray
Array of out of sample forecasts
"""
try:
Loading ...