4
$\begingroup$

I'm trying to find the correct way to update an already fitted ExponentialSmoothing model on new data. My guess was like this:

import numpy as np
from statsmodels.tsa.holtwinters import ExponentialSmoothing

# some simple data (from statsmodels exponential smoothing example)
data = [41.7275,  24.0418,  32.3281,  37.3287,  46.2132,  29.3463, 36.4829,  42.9777,  48.9015,
        31.1802,  37.7179,  40.4202, 51.2069,  31.8872,  40.9783,  43.7725]

model = {'trend': 'add', 'seasonal_periods':4, 'seasonal':'add'}
m = ExponentialSmoothing(data[:15],**model).fit()
m_updated = ExponentialSmoothing(data[:16], **model).fit(**fitted_params)

Here comes the problem: fitted_params contains two unexpected keys: initial_season and lamda. When I delete these from the parameters dictionary the code works, but it seems that the season is recomputed every time. Another proof of this is that if I choose a model without seasonality, e.g. model = {'trend': 'add'}, after removing again initial_season and lamda the last line of the snippet above raises a EstimationWarning: Model has no free parameters to estimate.

So it seems that in this way I can update an ExponentialSmoothing model without seasonality, but I cannot do the same if the model is seasonal. Is this a bug, a feature not already implemented or the desired behaviour? Is there another way to do it for seasonal models (maybe using the HoltWintersResults class)?

$\endgroup$
2

1 Answer 1

1
$\begingroup$

There are two implementations of the exponential smoothing model in the statsmodels library:

statsmodels.tsa.statespace.exponential_smoothing.ExponentialSmoothing
statsmodels.tsa.holtwinters.ExponentialSmoothing

According to the documentation, the former implementation, while having some limitations, allows for updates.

Here's how an update could be performed (based on this documentation page):

import numpy as np
from statsmodels.tsa.statespace.exponential_smoothing import ExponentialSmoothing

# some simple data (from statsmodels exponential smoothing example)
data = [41.7275,  24.0418,  32.3281,  37.3287,  46.2132,  29.3463, 36.4829,  42.9777,  48.9015,
        31.1802,  37.7179,  40.4202, 51.2069,  31.8872,  40.9783,  43.7725]
# slightly changed notation
model = {'trend': True, 'seasonal':4}
m = ExponentialSmoothing(data[:15],**model).fit()

m_updated = m.append(data[15:])

# this shows the effect of the update
m.news(m_updated,start=15,periods=4).summary()

If you investigate the parameters of both models, you'll notice that they are the same.

Second approach

In Feb 2022 the implementation of exponential smoothing model based on state space models has a bug:

RuntimeWarning: ExponentialSmoothing should not be used with seasonal terms. It has a serious bug that has not been fixed. Instead use ETSModel.

After some digging I found out how one would update the model using the other implementation. From here on HW stands for the 'regular' Holt Winters implementation, HW_SS stands for the implementation based on state space models.

from statsmodels.tsa.statespace.exponential_smoothing import ExponentialSmoothing as HW_SS

# some simple data (from statsmodels exponential smoothing example)
data = [41.7275,  24.0418,  32.3281,  37.3287,  46.2132,  29.3463, 36.4829,  42.9777,  48.9015,
        31.1802,  37.7179,  40.4202, 51.2069,  31.8872,  40.9783,  43.7725]
# slightly changed notation
model = {'trend': True, 'seasonal':4}
m_ss = HW_SS(data[:15],**model).fit()
m_ss_forecast = m_ss.get_forecast(steps=5).summary_frame(alpha=0.10)['mean']
m_ss_updated = m_ss.append(data[15:])
m_ss_updated_forecast = m_ss_updated.get_forecast(steps=4).summary_frame(alpha=0.10)['mean']

from statsmodels.tsa.holtwinters import ExponentialSmoothing as HW
model = {'trend': 'add', 'seasonal_periods':4, 'seasonal':'add'}
m = HW(data[:15],**model).fit()
params_initial = {key:value for key, value in m.params.items() if 'initial' in key and 'season' not in key}
params_smoothing = {key:value for key, value in m.params.items() if 'smoothing' in key}

m_forecast = m.forecast(steps = 5)
m_updated = HoltWinters(data,
                        **model,
                        initialization_method='known',
                        **params_initial,
                        initial_seasonal=m.params['initial_seasons'])\
.fit(**params_smoothing,optimized=False)
m_updated_forecast = m_updated.forecast(steps = 4)

Note that m_updated.params and m.params are the same.

Here's a plot of all possible forecasts.

plt.plot(data)
plt.plot(range(15,20),m_forecast, label = 'HW')
plt.plot(range(16,20),m_updated_forecast, label = 'HW updated')
plt.plot(range(15,20),m_ss_forecast, label = 'HW_SS')
plt.plot(range(16,20),m_ss_updated_forecast, label = 'HW_SS updated')
plt.legend()
plt.show()

enter image description here

$\endgroup$
1
  • 1
    $\begingroup$ I'm pretty sure this feature wasn't implemented at the time I asked the question. Anyway, I'm glad this is now possible and thanks for pointing it out! $\endgroup$
    – Alessandro
    Feb 21, 2022 at 17:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.