-
Notifications
You must be signed in to change notification settings - Fork 89
/
exp_smoothing.py
230 lines (211 loc) · 9.13 KB
/
exp_smoothing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""Implements Holt-Winters exponential smoothing."""
__all__ = ["ExponentialSmoothing"]
__author__ = ["mloning", "big-o"]
from aeon.forecasting.base.adapters import _StatsModelsAdapter
class ExponentialSmoothing(_StatsModelsAdapter):
"""Holt-Winters exponential smoothing forecaster.
Direct interface for `statsmodels.tsa.holtwinters`.
Default settings use simple exponential smoothing without trend and
seasonality components.
Parameters
----------
trend : {"add", "mul", "additive", "multiplicative", None}, default=None
Type of trend component.
damped_trend : bool, default=False
Should the trend component be damped.
seasonal : {"add", "mul", "additive", "multiplicative", None}, default=None
Type of seasonal component.Takes one of
sp : int or None, default=None
The number of seasonal periods to consider.
initial_level : float or None, default=None
The alpha value of the simple exponential smoothing, if the value
is set then this value will be used as the value.
initial_trend : float or None, default=None
The beta value of the Holt's trend method, if the value is
set then this value will be used as the value.
initial_seasonal : float or None, default=None
The gamma value of the holt winters seasonal method, if the value
is set then this value will be used as the value.
use_boxcox : {True, False, 'log', float}, default=None
Should the Box-Cox transform be applied to the data first?
If 'log' then apply the log. If float then use lambda equal to float.
initialization_method:{'estimated','heuristic','legacy-heuristic','known',None},
default='estimated'
Method for initialize the recursions.
If 'known' initialization is used, then `initial_level` must be
passed, as well as `initial_trend` and `initial_seasonal` if
applicable.
'heuristic' uses a heuristic based on the data to estimate initial
level, trend, and seasonal state. 'estimated' uses the same heuristic
as initial guesses, but then estimates the initial states as part of
the fitting process.
smoothing_level : float, optional
The alpha value of the simple exponential smoothing, if the value
is set then this value will be used as the value.
smoothing_trend : float, optional
The beta value of the Holt's trend method, if the value is
set then this value will be used as the value.
smoothing_seasonal : float, optional
The gamma value of the holt winters seasonal method, if the value
is set then this value will be used as the value.
damping_trend : float, optional
The phi value of the damped method, if the value is
set then this value will be used as the value.
optimized : bool, optional
Estimate model parameters by maximizing the log-likelihood.
remove_bias : bool, optional
Remove bias from forecast values and fitted values by enforcing
that the average residual is equal to zero.
start_params : array_like, optional
Starting values to used when optimizing the fit. If not provided,
starting values are determined using a combination of grid search
and reasonable values based on the initial values of the data. See
the notes for the structure of the model parameters.
method : str, default "L-BFGS-B"
The minimizer used. Valid options are "L-BFGS-B" , "TNC",
"SLSQP" (default), "Powell", "trust-constr", "basinhopping" (also
"bh") and "least_squares" (also "ls"). basinhopping tries multiple
starting values in an attempt to find a global minimizer in
non-convex problems, and so is slower than the others.
minimize_kwargs : dict[str, Any]
A dictionary of keyword arguments passed to SciPy's minimize
function if method is one of "L-BFGS-B", "TNC",
"SLSQP", "Powell", or "trust-constr", or SciPy's basinhopping
or least_squares functions. The valid keywords are optimizer
specific. Consult SciPy's documentation for the full set of
options.
use_brute : bool, optional
Search for good starting values using a brute force (grid)
optimizer. If False, a naive set of starting values is used.
random_state : int, RandomState instance or None, optional ,
default=None – If int, random_state is the seed used by the random
number generator; If RandomState instance, random_state is the random
number generator; If None, the random number generator is the
RandomState instance used by np.random.
References
----------
[1] Hyndman, Rob J., and George Athanasopoulos. Forecasting: principles
and practice. OTexts, 2014.
Examples
--------
>>> from aeon.datasets import load_airline
>>> from aeon.forecasting.exp_smoothing import ExponentialSmoothing
>>> y = load_airline()
>>> forecaster = ExponentialSmoothing(
... trend='add', seasonal='multiplicative', sp=12
... ) # doctest: +SKIP
>>> forecaster.fit(y) # doctest: +SKIP
ExponentialSmoothing(...)
>>> y_pred = forecaster.predict(fh=[1,2,3]) # doctest: +SKIP
"""
_fitted_param_names = (
"initial_level",
"initial_slope",
"initial_seasons",
"aic",
"bic",
"aicc",
)
def __init__(
self,
trend=None,
damped_trend=False,
seasonal=None,
sp=None,
initial_level=None,
initial_trend=None,
initial_seasonal=None,
use_boxcox=None,
initialization_method="estimated",
smoothing_level=None,
smoothing_trend=None,
smoothing_seasonal=None,
damping_trend=None,
optimized=True,
remove_bias=False,
start_params=None,
method=None,
minimize_kwargs=None,
use_brute=True,
random_state=None,
):
# Model params
self.trend = trend
self.damped_trend = damped_trend
self.seasonal = seasonal
self.sp = sp
self.use_boxcox = use_boxcox
self.initial_level = initial_level
self.initial_trend = initial_trend
self.initial_seasonal = initial_seasonal
self.initialization_method = initialization_method
self.smoothing_level = smoothing_level
self.smoothing_trend = smoothing_trend
self.smoothing_seasonal = smoothing_seasonal
self.damping_trend = damping_trend
self.optimized = optimized
self.remove_bias = remove_bias
self.start_params = start_params
self.method = method
self.minimize_kwargs = minimize_kwargs
self.use_brute = use_brute
super().__init__(random_state=random_state)
def _fit_forecaster(self, y, X=None):
from statsmodels.tsa.holtwinters import (
ExponentialSmoothing as _ExponentialSmoothing,
)
self._forecaster = _ExponentialSmoothing(
y,
trend=self.trend,
damped_trend=self.damped_trend,
seasonal=self.seasonal,
seasonal_periods=self.sp,
use_boxcox=self.use_boxcox,
initial_level=self.initial_level,
initial_trend=self.initial_trend,
initial_seasonal=self.initial_seasonal,
initialization_method=self.initialization_method,
)
self._fitted_forecaster = self._forecaster.fit(
smoothing_level=self.smoothing_level,
smoothing_trend=self.smoothing_trend,
smoothing_seasonal=self.smoothing_seasonal,
damping_trend=self.damping_trend,
optimized=self.optimized,
remove_bias=self.remove_bias,
start_params=self.start_params,
method=self.method,
minimize_kwargs=self.minimize_kwargs,
use_brute=self.use_brute,
)
@classmethod
def get_test_params(cls, parameter_set="default"):
"""Return testing parameter settings for the estimator.
Parameters
----------
parameter_set : str , default = "default"
Name of the set of test parameters to return, for use in tests. If no
special parameters are defined for a value, will return `"default"` set.
There are currently no reserved values for forecasters.
Returns
-------
params :dict or list of dict , default = {}
arameters to create testing instances of the class
Each dict are parameters to construct an "interesting" test instance, i.e.,
`MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
`create_test_instance` uses the first (or only) dictionary in `params
"""
params1 = {}
params2 = {
"trend": "mul",
"damped_trend": True,
"seasonal": "mul",
"sp": 2,
"use_boxcox": False,
"initialization_method": "heuristic",
"smoothing_level": 0.1,
"smoothing_trend": 0.1,
"damping_trend": 0.42,
"method": "least_squares",
}
return [params1, params2]