<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Archives ETS - Open Forecasting</title>
	<atom:link href="https://openforecast.org/tag/ets-en/feed/" rel="self" type="application/rss+xml" />
	<link>https://openforecast.org/tag/ets-en/</link>
	<description>How to look into the future</description>
	<lastBuildDate>Thu, 09 Apr 2026 09:40:57 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2015/08/cropped-usd-05-32x32.png&amp;nocache=1</url>
	<title>Archives ETS - Open Forecasting</title>
	<link>https://openforecast.org/tag/ets-en/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>smooth forecasting with the smooth package in Python</title>
		<link>https://openforecast.org/2026/04/09/smooth-forecasting-with-the-smooth-package-in-python/</link>
					<comments>https://openforecast.org/2026/04/09/smooth-forecasting-with-the-smooth-package-in-python/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Thu, 09 Apr 2026 09:15:02 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Univariate models]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3961</guid>

					<description><![CDATA[<p>Here is another piece of news I have been hoping to deliver for quite some time now (since January 2026 actually). We have finally created the first release of the smooth package for Python and it is available on PyPI! Anyone interested? Read more! On this page: Why does &#8220;smooth&#8221; exist? A bit of history [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2026/04/09/smooth-forecasting-with-the-smooth-package-in-python/">smooth forecasting with the smooth package in Python</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Here is another piece of news I have been hoping to deliver for quite some time now (since January 2026 actually). We have finally created the first release of the smooth package for Python and it is available on PyPI! Anyone interested? Read more!</p>
<p>On this page:</p>
<ul>
<li><a href="#whySmooth">Why does &#8220;smooth&#8221; exist?</a></li>
<li><a href="#history">A bit of history</a></li>
<li><a href="#install">How to install</a></li>
<li><a href="#whatWorks">What works</a></li>
<li><a href="#example">An example</a></li>
<li><a href="#evaluation">Evaluation</a></li>
<ul>
<li><a href="#evaluationSetup">Setup</a></li>
<li><a href="https://github.com/config-i1/smooth/blob/master/python/tests/notebooks/benchmark_exponential_smoothing.ipynb">Jupiter notebook</a></li>
<li><a href="#sktime">sktime non-collaborative stance</a></li>
<li><a href="#evaluationResults">Results</a></li>
</ul>
<li><a href="#whatsNext">What&#8217;s next?</a></li>
<li><a href="#summary">Summary</a></li>
</ul>
<h2 id="whySmooth">Why does &#8220;smooth&#8221; exist?</h2>
<p>There are lots of implementations of ETS and ARIMA (dynamic models) out there, both in Python and in R (and also in Julia now, <a href="https://github.com/taf-society/Durbyn.jl">see Durbyn</a>). So, why bother creating yet another one?</p>
<p>The main philosophy of the smooth package in R is flexibility. It is not just an implementation of ETS or ARIMA &#8211; it is there to give you more control over what you can do with these models in different situations. The main function in the package is called <a href="https://openforecast.org/adam/">&#8220;ADAM&#8221; &#8211; the Augmented Dynamic Adaptive Model</a>. It is the single source of error state space model that unites ETS, ARIMA, and regression, and supports the following list of features (taken from the Introduction in <a href="https://openforecast.org/adam/">the book</a>):</p>
<ol>
<li>ETS;</li>
<li>ARIMA;</li>
<li>Regression;</li>
<li>TVP regression;</li>
<li>Combination of (1), (2), and either (3) or (4). i.e. ARIMAX/ETSX;</li>
<li>Automatic selection/combination of states for ETS;</li>
<li>Automatic orders selection for ARIMA;</li>
<li>Variables selection for regression;</li>
<li>Normal and non-normal distributions;</li>
<li>Automatic selection of most suitable distributions;</li>
<li>Multiple seasonality;</li>
<li>Occurrence part of the model to handle zeroes in data (intermittent demand);</li>
<li>Modelling scale of distribution (GARCH and beyond);</li>
<li>A variety of ways to produce forecasts for different situation;</li>
<li>Advanced loss functions for model estimation;</li>
<li>&#8230;</li>
</ol>
<p>All of these features come with the ability to fine tune the optimiser (e.g. how the parameters are estimated) and to manually adjust any model parameters you want. This allows, for example, fitting iETSX(M,N,M) with multiple frequencies with Gamma distribution to better model <a href="/2023/05/10/story-of-probabilistic-forecasting-of-hourly-emergency-department-arrivals/">hourly emergency department arrivals (intermittent demand) and thus producing more accurate forecasts of it</a>. There is no other package either in R or Python that could give such flexibility with the dynamic models to users.</p>
<p>And at the same time, over the years, I have managed to iron out the R functions so much that they handle almost any real life situation and do not break. And at the same time, they work quite fast and produce accurate forecasts, <a href="/2026/02/09/smooth-v4-4-0/#evaluationResults">sometimes outperforming the other existing R implementations</a>.</p>
<p>So, here we are. We want to bring this flexibility, robustness, and speed to <strong>Python</strong>.</p>
<h2 id="history">A bit of history</h2>
<p>The smooth package for R was first released on CRAN in 2016, when I finished my PhD. It went from v1.4.3 to v4.4.0 over the last 10 years. It saw a rise in popularity, but then an inevitable decline due to the decreasing number of R users in business. So, back in 2021, Rebecca Killick and I applied for an EPSRC grant to develop Python packages for forecasting and time series analysis. The idea was to translate what we have in R (including greybox, smooth, forecast, and changepoint detection packages) to Python with the help of professional programmers. Unfortunately, we did not receive the funding (it went to sktime for a good reason &#8211; they already had an existing codebase in Python).</p>
<p>In the beginning of 2023, Leonidas Tsaprounis got in touch with me, suggesting some help with the development and translation of the smooth package to Python. The idea was to use the existing C++ core and simply create a wrapper in Python. &#8220;Simply&#8221; is actually an oversimplification here, because I am not a programmer, so my functions are messy and hard to read. Nonetheless, we started cooking. Leo helped in setting up pybind11 and carma, and creating the necessary files for the compilation of the C++ code. Just to test whether that worked, we managed to create a basic function for the simple moving average based on the <code>sma()</code> from the R version. Our progress was a bit slow, because we were both busy with other projects. All of that changed when in July 2023 Filotas Theodosiou joined our small team and started working on the translation. We decided to implement the hardest thing first &#8211; <a href="/adam/">ADAM</a>.</p>
<p>What Fil did was use LLMs to translate the code from R to Python. Fil will write about this work at some point; the only thing I can say here is that it was not an easy process, because thousands of lines of R code needed to be translated to Python and then refactored. I only helped with suggestions and explanations of what is happening inside, and Leo provided guidance regarding the coding philosophy. It was Fil and his AI tools that did the main heavy lifting. By Summer 2025, we had a basic working ADAM() function, but it worked slightly differently from the one in R due to differences in initialisation and optimisation. He presented his work at the <a href="/2025/06/30/iif-open-source-forecasting-software-workshop-and-smooth/">IIF Open Source Forecasting Software Workshop</a>, explaining his experience with LLMs and code translation. Because everyone was pretty busy, it took a bit more time to reach the first proper release of smooth in Python.</p>
<p>In December 2025, I bought a Claude AI subscription and started vibe coding my way through the existing Python code. Between the three of us, we managed to progress the project, and finally in January 2026 we reached v1.0.0 of smooth in Python. Now ADAM works in exactly the same way in both R and Python: if you give it the same time series, it will select the same model and produce the same parameter estimates across languages. It took us time and effort to reach this, but we feel it is a critically important step &#8211; ensuring that users working in different languages have the same experience.</p>
<h2 id="install">How to install</h2>
<p>We are entirely grateful to Gustavo Niemeyer, who gave us the name <code>smooth</code> on PyPI. It belonged to him since 2020, but the project was abandoned, and he agreed to transfer it to us. So, now you can install smooth simply by running:</p>
<pre class="decode">pip install smooth</pre>
<p>There is also a development version of the package, which you can install by following the instructions in our <a href="https://github.com/config-i1/smooth/wiki/Installation#python">Installation wiki</a> on GitHub.</p>
<h2 id="whatWorks">What works</h2>
<p>The package currently does not have the full functionality, but there are already some things:</p>
<ol>
<li><code>ADAM()</code> &#8211; the main function that supports ETS with:
<ul>
<li>components selection via <code>model="ZXZ"</code> or other pools (see <a href="https://github.com/config-i1/smooth/wiki/ADAM">wiki on GitHub for details</a>);</li>
<li>forecast combination using AIC weights via <code>model="CCC"</code> or other pools (again, explained in the wiki);</li>
<li>multiple seasonalities via <code>lags=[24,168]</code>;</li>
<li>ability to provide some smoothing parameters or initial values (e.g. only alpha), letting the function estimate the rest;</li>
<li>different distributions;</li>
<li>advanced <a href="https://github.com/config-i1/smooth/wiki/Loss-Functions">loss functions</a>;</li>
<li>several options for <a href="https://github.com/config-i1/smooth/wiki/Initialisation">model initialisation</a>;</li>
<li>fine-tuning of the optimiser via <code>nlopt_kargs</code> (<a href="https://github.com/config-i1/smooth/wiki/Model-Estimation">read more in the wiki</a>);</li>
</ul>
</li>
<li><code>ES()</code> &#8211; the wrapper of <code>ADAM()</code> with the normal distribution. Supports the same functionality, but is a simplification of ADAM.</li>
</ol>
<p>We also have the standard methods for fitting and forecasting, and many attributes that allow extracting information from the model, all of which are <a href="https://github.com/config-i1/smooth/wiki/Fitted-Values-and-Forecasts">explained in the wiki of the project</a>.</p>
<h2 id="example">An example</h2>
<p>Here is an example of how to work with ADAM in Python. For this example to work, you will need to install the <code>fcompdata</code> package from pip:<br />
<code>pip install fcompdata</code></p>
<p>The example:</p>
<pre class="decode">from smooth import ADAM
from fcompdata import M3

model = ADAM(model="ZXZ", lags=12)
model.fit(M3[2568].x)

print(model)</pre>
<p>This is what you should see as the result of print:</p>
<pre>Time elapsed: 0.21 seconds
Model estimated using ADAM() function: ETS(MAM)
With backcasting initialisation
Distribution assumed in the model: Gamma
Loss function type: likelihood; Loss function value: 868.7085
Persistence vector g:
 alpha   beta  gamma
0.0205 0.0203 0.1568
Damping parameter: 1.0000
Sample size: 116
Number of estimated parameters: 4
Number of degrees of freedom: 112
Information criteria:
      AIC      AICc       BIC      BICc
1745.4170 1745.7774 1756.4314 1757.2879</pre>
<p>which is exactly the same output as in R (see, for example, some explanations <a href="https://openforecast.org/adam/ADAMETSPureAdditiveExamples.html">here</a>). We can then produce a forecast from this model:</p>
<pre class="decode">predict(model, h=18, interval="prediction", level=[0.9,0.95])</pre>
<p>The predict method currently supports analytical (aka &#8220;parametric&#8221;/&#8221;approximate&#8221;) and simulated prediction intervals. The <code>interval="prediction"</code> will tell the function to choose between the two depending on the type of model (multiplicative ETS models do not have analytical formulae for the multistep conditional variance and, as a result, do not have proper analytical prediction intervals). The <code>level</code> parameter can accept either a vector (which will produce several quantiles) or a scalar. What I get after running this is:</p>
<pre>             mean    lower_0.05   lower_0.025    upper_0.95   upper_0.975
116  11234.592643  10061.150811   9853.119370  12443.904763  12686.143870
117   8050.810544   7228.709864   7064.356429   8896.078713   9080.867866
118   7658.608163   6886.165498   6746.881596   8475.545469   8633.149796
119  10552.382933   9452.306261   9236.493206  11679.816814  11892.381046
120  10889.816551   9768.665327   9559.580233  12066.628218  12313.872205
121   7409.545388   6643.378080   6495.237349   8232.558913   8363.550598
122   7591.183726   6800.878319   6650.514904   8425.167556   8576.257297
123  14648.452226  13089.346824  12793.226771  16263.206997  16582.786939
124   6953.045603   6206.829418   6079.126523   7730.260301   7892.917098
125  11938.941882  10650.759513  10427.172989  13307.563498  13579.662925
126   8299.626845   7379.550080   7200.075336   9280.095498   9468.327654
127   8508.558884   7530.987557   7367.541698   9534.117611   9734.218257
128  11552.654541  10162.615284   9907.770755  13039.710057  13313.534412
129   8286.727505   7273.279560   7100.450038   9401.374461   9627.922116
130   7889.999721   6879.771040   6710.494741   8948.052817   9186.279622
131  10860.671447   9438.536335   9174.365147  12353.444279  12664.983138
132  11218.330395   9690.780430   9453.114931  12849.545829  13201.679610
133   7620.922782   6564.497975   6391.080974   8748.561119   8977.120673</pre>
<p>The separate wiki on <a href="https://github.com/config-i1/smooth/wiki/Fitted-Values-and-Forecasts#level-parameter">Fitted Values and Forecasts</a> explains all the parameters accepted by the predict method and what is returned by it.</p>
<h2 id="evaluation">Evaluation</h2>
<p>To see how the developed function works, I decided to conduct exactly the same evaluation that <a href="/2026/02/09/smooth-v4-4-0/">I did for the recent R release of the smooth package</a>, running the functions on the M1, M3, and Tourism competition data (5,315 time series) using the <a href="/2026/01/26/forecasting-competitions-datasets-in-python/">fcompdata</a> package.</p>
<h3 id="evaluationSetup">Setup</h3>
<p>I have selected the same set of models for Python as I did in R. Here are several options for the ADAM <code>model</code> parameter to see how the specific pools impact accuracy (this is discussed in detail in <a href="https://openforecast.org/adam/ETSSelection.html">Section 15.1 of ADAM</a>):</p>
<ol>
<li>XXX &#8211; select between pure additive ETS models only;</li>
<li>ZZZ &#8211; select from the pool of all 30 models, but use branch-and-bound to remove the less suitable models;</li>
<li>ZXZ &#8211; same as (2), but without the multiplicative trend models. This is used in the <code>smooth</code> functions <strong>by default</strong>;</li>
<li>FFF &#8211; select from the pool of all 30 models (exhaustive search);</li>
<li>SXS &#8211; the pool of models used by default in <code>ets()</code> from the <code>forecast</code> package in R.</li>
</ol>
<p>I also tested three types of ETS initialisation (read more about them <a href="https://github.com/config-i1/smooth/wiki/Initialisation">here</a>):</p>
<ol>
<li>Back &#8211; <code>initial="backcasting"</code> &#8211; this is the default initialisation method;</li>
<li>Opt &#8211; <code>initial="optimal"</code>;</li>
<li>Two &#8211; <code>initial="two-stage"</code>.</li>
</ol>
<p>I have also found the following implementations of ETS in Python and included them in my evaluation:</p>
<ol>
<li><a href="https://www.sktime.net/en/stable/api_reference/auto_generated/sktime.forecasting.ets.AutoETS.html">sktime AutoETS</a></li>
<li><a href="https://skforecast.org/0.20.0/api/stats#skforecast.stats._ets.Ets">skforecast AutoETS</a></li>
<li><a href="https://nixtlaverse.nixtla.io/statsforecast/docs/models/autoets.html">statsforecast AutoETS</a></li>
</ol>
<p>There is also a <a href="https://unit8co.github.io/darts/generated_api/darts.models.forecasting.sf_auto_ets.html">darts implementation of AutoETS</a>, which is actually a wrapper of the statsforecast one. So I ran it just to check how it works, and found that it failed in 1,518 cases. <a href="https://github.com/unit8co/darts/issues/3001">I filed the issue</a>, and it turned out that their implementation does not deal with short time series (10 observations or fewer), which is their design decision. They are now considering what to do about that, if anything.</p>
<p>I used RMSSE (<a href="https://www.doi.org/10.1016/j.ijforecast.2021.11.013">M5 competition</a>, motivated by <a href="https://www.doi.org/10.1016/j.ijforecast.2022.08.003">Athanasopoulos &#038; Kourentzes (2023)</a>) and SAME error measure together with the computational time for each time series:<br />
\begin{equation*}<br />
\mathrm{RMSSE} = \frac{1}{\sqrt{\frac{1}{T-1} \sum_{t=1}^{T-1} \Delta_t^2}} \mathrm{RMSE},<br />
\end{equation*}<br />
where \(\mathrm{RMSE} = \sqrt{\frac{1}{h} \sum_{j=1}^h e^2_{t+j}}\) is the Root Mean Squared Error of the point forecasts, and \(\Delta_t\) is the first differences of the in-sample actual values.</p>
<p>\begin{equation*}<br />
\mathrm{SAME} = \frac{1}{\frac{1}{T-1} \sum_{t=1}^{T-1} |\Delta_t|} \mathrm{AME},<br />
\end{equation*}<br />
where \(\mathrm{AME}= \left| \frac{1}{h} \sum_{j=1}^h e_{t+j} \right|\).</p>
<p>All of this was implemented in a Jupyter notebook, which is <a href="https://github.com/config-i1/smooth/blob/master/python/tests/notebooks/benchmark_exponential_smoothing.ipynb">available here</a> in case you want to reproduce the results.</p>
<h3 id="sktime">sktime non-collaborative stance</h3>
<p>In the first run of this (on 28th January 2026), I encountered several errors in AutoETS in sktime: it took an extremely long time to compute (see the table below &#8211; on average around 30 seconds per time series) and produced ridiculous forecasts (mean RMSSE was 106,951). I <a href="https://github.com/sktime/sktime/issues/9291">filed an issue</a> in their repo and sent a courtesy message to Franz Kiraly on LinkedIn the same day, saying that I would be happy to rerun the results if this was fixed. I then received an insulting email from him, blaming me for not collaborating and trying to diminish sktime. They then closed the issue, claiming that it was fixed. I reran the experiment with their development version from GitHub on 21st March (two months later!), only to get exactly the same results. I do not think their fix is working, but given Franz&#8217;s toxic behaviour, I am not going to rerun this any further or help him in any way. The Jupyter notebook with the experiment is <a href="https://github.com/config-i1/smooth/blob/master/python/tests/notebooks/benchmark_exponential_smoothing.ipynb">here</a>, so he can investigate on his own.</p>
<h3 id="evaluationResults">Results</h3>
<p>So, here are the summary results for the tested models:</p>
<pre>====================================================================================
EVALUATION RESULTS for RMSSE (All Series)
====================================================================================
               Method       Min        Q1       Med        Q3       Max      Mean
        ADAM ETS Back  0.018252  <strong>0.663358</strong>  <strong>1.161473</strong>  <strong>2.301861</strong>  <strong>50.25854</strong>  1.928086
         ADAM ETS Opt  0.024155  0.670682  1.185932  2.365498  51.61599  1.943729
         ADAM ETS Two  0.024599  0.669522  1.182516  2.342385  51.61599  1.947715
              ES Back  0.018252  0.667225  1.160971  2.313932  <strong>50.25854</strong>  1.927436
               ES Opt  0.024155  0.673575  1.185756  2.364915  51.61599  1.947180
               ES Two  0.024467  0.671771  1.187368  2.346343  51.61599  1.955076
               ES XXX  0.018252  0.677717  1.170823  2.306197  <strong>50.25854</strong>  1.961318
               ES ZZZ  <strong>0.011386</strong>  0.670211  1.179916  2.353334  115.5442  2.053459
               ES FFF  <strong>0.011386</strong>  0.680956  1.211736  2.449626  115.5442  2.100899
               ES SXS  0.018252  0.674537  1.169187  2.353334  <strong>50.25854</strong>  1.939847
statsforecast AutoETS  0.024468  0.673157  1.189209  2.326650  51.61597  <strong>1.923925</strong>
   skforecast AutoETS  0.074744  0.747200  1.344916  2.721083  50.54339  2.273724
       sktime AutoETS  0.024467  0.676191  1.190093  2.456184 565753200  106951.7</pre>
<p>Things to note:</p>
<ol>
<li>The best performing ETS on average is from Nixtla&#8217;s statsforecast package. The second best is our implementation (ADAM/ES) with backcasting;</li>
<li>Given that I <a href="/2026/02/09/smooth-v4-4-0/">ran exactly the same experiment for the R packages</a>, we can conclude that Nixtla&#8217;s implementation is even better than the one in the forecast package in R;</li>
<li>In terms of median RMSSE, ES with backcasting outperforms all other implementations;</li>
<li>ADAM ETS is the best in terms of the first and third quartiles of RMSSE;</li>
<li>ADAM ETS and ES perform quite similarly. This is expected, because ES is a wrapper of ADAM ETS, which assumes normality for the error term. ADAM ETS switches between Normal and Gamma distributions based on the type of error term;</li>
<li>Backcasting leads to the most accurate forecasts on these datasets. This does not mean it is a universal rule, and I am sure the situation will change for other datasets;</li>
<li>ES XXX gives exactly the same results as the one implemented in <a href="/2026/02/09/smooth-v4-4-0/#evaluationResults">the R version of the package</a>. This is important because we were aiming to reproduce results between R and Python with 100% precision, and we did. The reason why other ETS flavours differ between R and Python is that since smooth 4.4.0 for R, we changed how point forecasts are calculated for multiplicative component models: previously, they relied on simulations; now we simply use point forecasts. While this is not entirely statistically accurate, it is pragmatic because it avoids explosive trajectories.</li>
</ol>
<p>The results for SAME are qualitatively similar to those for RMSSE:</p>
<pre>===================================================================================
EVALUATION RESULTS for SAME (All Series)
===================================================================================
               Method       Min        Q1       Med        Q3       Max      Mean
        ADAM ETS Back  0.001142  0.374466  0.995272  <strong>2.402342</strong>  52.34177  1.951070
         ADAM ETS Opt  0.000106  0.373551  1.021661  2.485596  55.10179  1.962040
         ADAM ETS Two  0.000782  0.380398  1.029629  2.451008  55.10186  1.970422
              ES Back  0.001142  0.372777  <strong>0.994547</strong>  2.412503  53.45041  1.946517
               ES Opt  0.000217  <strong>0.372725</strong>  1.024666  2.478435  54.68603  1.967217
               ES Two  0.000095  0.384795  1.028561  2.454543  54.68558  1.982261
               ES XXX  0.000094  0.373315  1.005006  2.425682  <strong>53.16973</strong>  1.992656
               ES ZZZ  0.000760  0.386673  1.017732  2.467912  145.7604  2.107522
               ES FFF  0.000597  0.401426  1.048395  2.566151  145.7604  2.173559
               ES SXS  0.000597  0.375438  1.005603  2.450490  53.45041  1.964322
statsforecast AutoETS  0.000228  0.374821  1.015205  2.434952  53.61359  <strong>1.938682</strong>
   skforecast AutoETS  0.000993  0.457066  1.217751  2.954003  59.84443  2.409900
       sktime AutoETS  0.000433  0.392802  1.029433  2.571555 385286900  72931.90</pre>
<p>Finally, I also measured the computational time and got the following summary. Note that I moved ES flavours below because they are not directly comparable with the others (they have special pools of models):</p>
<pre>================================================================================
EVALUATION RESULTS for Computational Time in seconds (All Series)
================================================================================
               Method       Min        Q1       Med        Q3        Max      Mean
        ADAM ETS Back  0.008679  0.086219  0.140929  0.241768   <strong>0.923601</strong>  0.181546
         ADAM ETS Opt  0.051302  0.229400  0.315379  0.792827   2.638193  0.550378
         ADAM ETS Two  0.051314  0.287382  0.455149  1.080276   3.535120  0.715090
              ES Back  0.009274  0.085299  0.139511  0.247114   0.958868  0.182547
               ES Opt  0.053176  0.224293  0.312200  0.772161   2.888662  0.541243
               ES Two  0.048539  0.279598  0.446404  1.058847   3.553156  0.703183
statsforecast AutoETS  <strong>0.001770</strong>  <strong>0.007702</strong>  <strong>0.081189</strong>  <strong>0.167040</strong>   1.271202  <strong>0.102575</strong>
   skforecast AutoETS  0.021553  0.243102  0.302078  1.478667   7.482101  0.820576
       sktime AutoETS  0.128021  6.227344 19.170921 41.494513 229.793067 30.712191

================================================================================
               ES XXX  0.008793  0.054139  0.093792  0.144012   0.480976  0.104800
               ES ZZZ  0.010502  0.127297  0.184561  0.447649   1.794031  0.313157
               ES FFF  0.046921  0.215119  1.110657  1.557724   3.489375  1.071004
               ES SXS  0.024909  0.116427  0.434594  0.564973   1.251257  0.403988</pre>
<p>Things to note:</p>
<ol>
<li>Nixtla&#8217;s implementation is actually the fastest and very hard to beat. As far as I understand, they did great work implementing some code in C++ and then using numba (I do not know what that means yet);</li>
<li>Our implementation does not use numba, but our ETS with backcasting is still faster than the skforecast and sktime implementations;</li>
<li>ADAM ETS with backcasting also has the lowest maximum time, implying that in difficult situations it finds a solution relatively quickly compared with the others;</li>
</ol>
<p>So, overall, I would argue that the smooth implementation of ETS is competitive with other implementations. But it has one important benefit: it supports more features. And we plan to expand it further to make it even more useful across a wider variety of cases.</p>
<h2 id="whatsNext">What&#8217;s next</h2>
<p>There are still a lot of features that we have not managed to implement yet. Here is a non-exhaustive list:</p>
<ol>
<li><a href="/adam/ADAMX.html">Explanatory variables to have ETSX/ARIMAX</a>;</li>
<li><a href="/adam/ADAMARIMA.html">ARIMA</a>;</li>
<li><a href="/adam/ADAMIntermittent.html">Occurrence model</a> for intermittent demand forecasting;</li>
<li><a href="/adam/ADAMscaleModel.html">Scale model</a> for ADAM;</li>
<li><a href="/adam/ADAMUncertaintySimulation.html">Simulation functions</a>;</li>
<li><a href="/adam/diagnostics.html">Model diagnostics</a>;</li>
<li>CES, MSARIMA, SSARIMA, GUM, and SMA &#8211; functions that are available in R and not yet ported to Python.</li>
</ol>
<p>So, lots of work to do. I am sure we will be quite busy well into 2026.</p>
<h2 id="summary">Summary</h2>
<p>It has been a long and winding road, but Filotas and Leo did an amazing job to make this happen. The existing ETS implementation in <code>smooth</code> already works quite well and quite fast. It does not fail as some other implementations do, and it is quite reliable. I have actually spent many years testing the R version on different time series to make sure that it produces something sensible no matter what. The code was translated to Python one-to-one, so I am fairly confident that the function will work as expected in 99.9% of cases (there is always a non-zero probability that something will go wrong). Both ADAM and ES already support a variety of features that you might find useful.</p>
<p>One thing I will kindly ask of you is that if you find a bug or an issue when running experiments on your datasets, please file it in our GitHub repo <a href="https://github.com/config-i1/smooth/issues">here</a> &#8211; we will try to find the time to fix it. Also, if you would like to contribute by translating some features from R to Python or implementing something additional, please get in touch with me.</p>
<p>Finally, I am always glad to hear success stories. If you find the <code>smooth</code> package useful in your work, please let us know. One way of doing that is via the <a href="https://github.com/config-i1/smooth/discussions">Discussions</a> on GitHub, or you can simply send me an email.</p>
<p>Message <a href="https://openforecast.org/2026/04/09/smooth-forecasting-with-the-smooth-package-in-python/">smooth forecasting with the smooth package in Python</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/04/09/smooth-forecasting-with-the-smooth-package-in-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth v4.4.0</title>
		<link>https://openforecast.org/2026/02/09/smooth-v4-4-0/</link>
					<comments>https://openforecast.org/2026/02/09/smooth-v4-4-0/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 09 Feb 2026 09:02:21 +0000</pubDate>
				<category><![CDATA[Package smooth for R]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[CES]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[GUM]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3959</guid>

					<description><![CDATA[<p>Great news, everyone! smooth package for R version 4.4.0 is now on CRAN. Why is this a great news? Let me explain! On this page: What&#8217;s new? Evaluation Setup Results What&#8217;s next? Here is what&#8217;s new since 4.3.0: First, I have worked on tuning the initialisation in adam() in case of backcasting, and improved the [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2026/02/09/smooth-v4-4-0/">smooth v4.4.0</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Great news, everyone! smooth package for R version 4.4.0 is now on CRAN. Why is this a great news? Let me explain!</p>
<p>On this page:</p>
<ul>
<li><a href="#whatsNew">What&#8217;s new?</a></li>
<li><a href="#evaluation">Evaluation</a></li>
<ul>
<li><a href="#evaluationSetup">Setup</a></li>
<li><a href="#evaluationResults">Results</a></li>
</ul>
<li><a href="#whatsNext">What&#8217;s next?</a></li>
</ul>
<h3 id="whatsNew">Here is what&#8217;s new since 4.3.0:</h3>
<p>First, I have worked on tuning the initialisation in <code>adam()</code> in case of backcasting, and improved the <code>msdecompose()</code> function a bit to get more robust results. This was necessary to make sure that when the smoothing parameters are close to zero, initial values would still make sense. This is already in <code>adam</code> (use <code>smoother="global"</code> to test), but will become the default behaviour in the next version of the package, when we iron everything out. This is all a part of a larger work with Kandrika Pritularga on a paper about the initialisation of dynamic models.</p>
<p>Second, I have fixed a long standing issue of the eigenvalues calculation inside the dynamic models, which is applicable only in case of <code>bounds="admissible"</code> and might impact ARIMA, CES and GUM. The parameter restriction are now done consistently across all functions, guaranteeing that they will not fail and will produce stable/invertible estimates of parameters.</p>
<p>Third, I have added the Sparse ARMA function, which constructs ARMA(p,q) of the specific orders, dropping all the elements from 1 to those. e.g. SpARMA(2,3) would have the following form:<br />
\begin{equation*}<br />
y_t = \phi_2 y_{t-2} + \theta_3 \epsilon_{t-3} + \epsilon_{t}<br />
\end{equation*}<br />
This weird model is needed for a project I am working on together with Devon Barrow, Nikos Kourentzes and Yves Sagaert. I&#8217;ll explain more when we get the final draft of the paper.</p>
<p>And something very important, which you will not notice: I refactored the C++ code in the package so that it is available not only for R, but also for Python&#8230; Why? I&#8217;ll explain in the next post :). But this also means that the old functions that relied on the previous generation of the C++ code are now discontinued, and all the smooth functions use the new core. This applies to <code>es()</code>, <code>ssarima()</code>, <code>msarima()</code>, <code>ces()</code>, <code>gum()</code> and <code>sma()</code>. You will not notice any change, except that some of them should become a bit faster and probably more robust. And this also means that all of them will now be able to use methods for the <code>adam()</code> function. For example, the <code>summary()</code> will produce the proper output with standard errors and confidence intervals for all estimated parameters.</p>
<h2 id="evaluation">Evaluation</h2>
<p><strong>DISCLAIMER</strong>: The previous evaluation was for smooth v4.3.0, you can find it <a href="/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/">here</a>. I have changed one of error measures (sCE to SAME), but the rest is the same, so the results are widely comparable between the versions.</p>
<h3 id="evaluationSetup">The setup</h3>
<p>As usual, in situations like this, I have run the evaluation on the M1, M3 and Tourism competition data. This time, I have added more flavours of the ETS model selection so that you can see how the models pool impacts the forecasting accuracy. Short description:</p>
<ol>
<li>XXX &#8211; select between pure additive ETS models only;</li>
<li>ZZZ &#8211; select from the pool of all 30 models, but use branch-and-bound to kick out the less suitable models;</li>
<li>ZXZ &#8211; same as (2), but without the multiplicative trend models. This is used in the <code>smooth</code> functions <strong>by default</strong>;</li>
<li>FFF &#8211; select from the pool of all 30 models (exhaustive search);</li>
<li>SXS &#8211; the pool of models that is used by default in <code>ets()</code> from the <code>forecast</code> package in R.</li>
</ol>
<p>I also tested three types of the ETS initialisation:</p>
<ol>
<li>Back &#8211; <code>initial="backcasting"</code></li>
<li>Opt &#8211; <code>initial="optimal"</code></li>
<li>Two &#8211; <code>initial="two-stage"</code></li>
</ol>
<p>Backcasting is now the default method of initialisation, and does well in many cases, but I found that optimal initials (if done correctly) help in some difficult situations, as long a you have enough of computational time.</p>
<p>I used two error measures and computational time to check how functions work. The first error measure is called RMSSE (Root Mean Squared Scaled Error) from <a href="http://dx.doi.org/10.1016/j.ijforecast.2021.11.013">M5 competition</a>, motivated by <a href="http://dx.doi.org/10.1016/j.ijforecast.2022.08.003">Athanasopoulos &#038; Kourentzes (2023)</a>:</p>
<p>\begin{equation*}<br />
\mathrm{RMSSE} = \frac{1}{\sqrt{\frac{1}{T-1} \sum_{t=1}^{T-1} \Delta_t^2}} \mathrm{RMSE},<br />
\end{equation*}<br />
where \(\mathrm{RMSE} = \sqrt{\frac{1}{h} \sum_{j=1}^h e^2_{t+j}}\) is the Root Mean Squared Error of the point forecasts, and \(\Delta_t\) is the first differences of the in-sample actual values.</p>
<p>The second measure does not have a standard name in the literature, but the idea of it is to the measure the bias of forecasts and to get rid of the sign to make sure that positively biased forecasts on some time series are not cancelled out by the negative ones on the other ones. I call this measure &#8220;Scaled Absolute Mean Error&#8221; (SAME):</p>
<p>\begin{equation*}<br />
\mathrm{SAME} = \frac{1}{\frac{1}{T-1} \sum_{t=1}^{T-1} |\Delta_t|} \mathrm{AME},<br />
\end{equation*}<br />
where \(\mathrm{AME}= \left| \frac{1}{h} \sum_{j=1}^h e_{t+j} \right|\).</p>
<p>For both of these measures, the lower value is better than the higher one. As for the computational time, I have measured it for each model and each series, and this time I provided distribution of times to better see how methods perform.</p>
<div class="su-spoiler su-spoiler-style-fancy su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>Boring code in R</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode">library(Mcomp)
library(Tcomp)
library(forecast)
library(smooth)

library(doMC)
registerDoMC(detectCores())

# Create a small but neat function that will return a vector of error measures
errorMeasuresFunction <- function(object, holdout, insample){
        holdout <- as.vector(holdout);
        insample <- as.vector(insample);
	# RMSSE and SAME are defined in greybox v2.0.7
        return(c(RMSSE(holdout, object$mean, mean(diff(insample^2)),
                 SAME(holdout, object$mean, mean(abs(diff(insample)))),
                 object$timeElapsed))
}

datasets <- c(M1,M3,tourism)
datasetLength <- length(datasets)

# Method configuration list
# Each method specifies: fn (function name), pkg (package), model, initial,
methodsConfig <- list(
	# ETS and Auto ARIMA from the forecast package in R
	"ETS" = list(fn = "ets", pkg = "forecast", use_x_only = TRUE),
	"Auto ARIMA" = list(fn = "auto.arima", pkg = "forecast", use_x_only = TRUE),
	# ADAM with different initialisation schemes
	"ADAM ETS Back" = list(fn = "adam", pkg = "smooth", model = "ZXZ", initial = "back"),
	"ADAM ETS Opt" = list(fn = "adam", pkg = "smooth", model = "ZXZ", initial = "opt"),
	"ADAM ETS Two" = list(fn = "adam", pkg = "smooth", model = "ZXZ", initial = "two"),
	# ES, which is a wrapper of ADAM. Should give very similar results to ADAM on regular data
	"ES Back" = list(fn = "es", pkg = "smooth", model = "ZXZ", initial = "back"),
	"ES Opt" = list(fn = "es", pkg = "smooth", model = "ZXZ", initial = "opt"),
	"ES Two" = list(fn = "es", pkg = "smooth", model = "ZXZ", initial = "two"),
	# Several flavours for model selection in ES
	"ES XXX" = list(fn = "es", pkg = "smooth", model = "XXX", initial = "back"),
	"ES ZZZ" = list(fn = "es", pkg = "smooth", model = "ZZZ", initial = "back"),
	"ES FFF" = list(fn = "es", pkg = "smooth", model = "FFF", initial = "back"),
	"ES SXS" = list(fn = "es", pkg = "smooth", model = "SXS", initial = "back"),
	# ARIMA implementations in smooth
	"MSARIMA" = list(fn = "auto.msarima", pkg = "smooth", initial = "back"),
	"SSARIMA" = list(fn = "auto.ssarima", pkg = "smooth", initial = "back"),
	# Complex Exponential Smoothing
	"CES" = list(fn = "auto.ces", pkg = "smooth", initial = "back"),
	# Generalised Univeriate Model (experimental)
	"GUM" = list(fn = "auto.gum", pkg = "smooth", initial = "back")
)

methodsNames <- names(methodsConfig)
methodsNumber <- length(methodsNames)

measuresNames <- c("RMSSE","SAME","Time")
measuresNumber <- length(measuresNames)

testResults <- array(NA, c(methodsNumber, datasetLength, measuresNumber),
                     dimnames = list(methodsNames, NULL, measuresNames))

# Unified loop over all methods
for(j in seq_along(methodsConfig)){
	cfg <- methodsConfig[[j]]
	cat("Running method:", methodsNames[j], "\n")

	result <- foreach(i = 1:datasetLength, .combine = "cbind",
	                  .packages = c("smooth", "forecast")) %dopar% {
		startTime <- Sys.time()

		# Build model call based on method type
		if(isTRUE(cfg$use_x_only)){
			# forecast package methods: ets, auto.arima
			test <- do.call(cfg$fn, list(datasets[[i]]$x))
		}else if(cfg$fn %in% c("adam", "es")) {
			# adam and es take dataset and model
			test <- do.call(cfg$fn, list(datasets[[i]], model=cfg$model, initial = cfg$initial))
		}else{
			# auto.msarima, auto.ssarima, auto.ces, auto.gum
			test <- do.call(cfg$fn, list(datasets[[i]], initial = cfg$initial))
		}

		# Build forecast call
		forecast_args <- list(test, h = datasets[[i]]$h)
		testForecast <- do.call(forecast, forecast_args)
		testForecast$timeElapsed <- Sys.time() - startTime

		return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x))
	}
	testResults[j,,] <- t(result)
}

</pre>
</div></div>
<h3 id="evaluationResults">Results</h3>
<p>And here are the results for the smooth functions in v4.4.0 for R. First, we summarise the RMSSEs. I produce quartiles of distribution of RMSSE together with the mean.</p>
<pre class="decode">cbind(t(apply(testResults[,,"RMSSE"],1,quantile, na.rm=T)),
      mean=apply(testResults[,,"RMSSE"],1,mean)) |> round(4)</pre>
<pre>                  0%    25%    50%    75%      100%   mean
ETS           0.0245 0.6772 1.1806 2.3765   51.6160 1.9697
Auto ARIMA    0.0246 0.6802 1.1790 2.3583   51.6160 1.9864
ADAM ETS Back 0.0183 <strong>0.6647</strong> <strong>1.1620</strong> <strong>2.3023</strong>   <strong>50.2585</strong> <strong>1.9283</strong>
ADAM ETS Opt  0.0242 0.6714 1.1868 2.3623   51.6160 1.9432
ADAM ETS Two  0.0246 0.6690 1.1875 2.3374   51.6160 1.9480
ES Back       0.0183 0.6674 1.1647 2.3164   <strong>50.2585</strong> 1.9292
ES Opt        0.0242 0.6740 1.1858 2.3644   51.6160 1.9469
ES Two        0.0245 0.6717 1.1874 2.3463   51.6160 1.9538
ES XXX        0.0183 0.6777 1.1708 2.3062   <strong>50.2585</strong> 1.9613
ES ZZZ        <strong>0.0108</strong> 0.6682 1.1816 2.3611  201.4959 2.0841
ES FFF        0.0145 0.6795 1.2170 2.4575 5946.1858 3.3033
ES SXS        0.0183 0.6754 1.1709 2.3539   <strong>50.2585</strong> 1.9448
MSARIMA       0.0278 0.6988 1.1898 2.4208   51.6160 2.0750
SSARIMA       0.0277 0.7371 1.2544 2.4425   51.6160 2.0625
CES Back      0.0450 0.6761 1.1741 2.3205   51.0571 1.9650
GUM Back      0.0333 0.7077 1.2073 2.4533   51.6184 2.0461
</pre>
<p>The worst performing models are the ETS with the multiplicative trend (ES ZZZ and ES FFF). This is because there are outliers in some time series, and the multiplicative trend reacts to them by amending the trend value to something large (e.g. 2, i.e. twice increase in level for each step), and then can never return to a reasonable level (see explanation of this phenomenon in <a href="https://openforecast.org/adam/ADAMETSMultiplicativeAlternative.html">Section 6.6 of ADAM book</a>). As expected, ADAM ETS does very similar to the ES, and we can see that the default initialisation (backcasting) is pretty good in terms of RMSSE values. To be fair, if the models are tested on a different dataset, it might be the case that the optimal initialisation would do better.</p>
<p>Here is a table with the SAME results:</p>
<pre class="decode">cbind(t(apply(testResults[,,"SAME"],1,quantile, na.rm=T)),
      mean=apply(testResults[,,"SAME"],1,mean)) |> round(4)</pre>
<pre>                 0%    25%    50%    75%      100%   mean
ETS           8e-04 0.3757 1.0203 2.5097   54.6872 1.9983
Auto ARIMA    <strong>0e+00</strong> 0.3992 1.0429 2.4565   53.2710 2.0446
ADAM ETS Back 1e-04 0.3752 0.9965 <strong>2.4047</strong>   <strong>52.3418</strong> 1.9518
ADAM ETS Opt  5e-04 0.3733 1.0212 2.4848   55.1018 1.9618
ADAM ETS Two  8e-04 0.3780 1.0316 2.4511   55.1019 1.9712
ES Back       <strong>0e+00</strong> 0.3733 <strong>0.9945</strong> 2.4122   53.4504 <strong>1.9485</strong>
ES Opt        2e-04 <strong>0.3727</strong> 1.0255 2.4756   54.6860 1.9673
ES Two        1e-04 0.3855 1.0323 2.4535   54.6856 1.9799
ES XXX        1e-04 0.3733 1.0050 2.4257   53.1697 1.9927
ES ZZZ        3e-04 0.3824 1.0135 2.4885  229.7626 2.1376
ES FFF        3e-04 0.3972 1.0489 2.6042 3748.4268 2.9501
ES SXS        6e-04 0.3750 1.0125 2.4627   53.4504 1.9725
MSARIMA       1e-04 0.3960 1.0094 2.5409   54.7916 2.1227
SSARIMA       1e-04 0.4401 1.1222 2.5673   52.5023 2.1248
CES Back      6e-04 0.3767 1.0079 2.4085   54.9026 2.0052
GUM Back      0e+00 0.3803 1.0575 2.6259   63.0637 2.0858
</pre>
<p>In terms of bias, smooth implementations of ETS are doing well again, and we can see the same issue with the multiplicative trend here as before. Another thing to note is that MSARIMA and SSARIMA are not as good as the Auto ARIMA from the forecast package on these datasets in terms of RMSSE and SAME (at least, in terms of mean error measures). And actually, GUM and CES are now better than those in terms of both error measures.</p>
<p>Finally, here is a table with the computational time:</p>
<pre class="decode">cbind(t(apply(testResults[,,"Time"],1,quantile, na.rm=T)),
      mean=apply(testResults[,,"Time"],1,mean)) |> round(4)</pre>
<pre>                  0%    25%    50%     75%    100%   mean
ETS           <strong>0.0032</strong> <strong>0.0117</strong> 0.1660  0.6728  1.6400 0.3631
Auto ARIMA    0.0100 0.1184 0.3618  1.0548 54.3652 1.4760
ADAM ETS Back 0.0162 0.1062 0.1854  0.4022  2.5109 0.2950
ADAM ETS Opt  0.0319 0.1920 0.3103  0.6792  3.8933 0.5368
ADAM ETS Two  0.0427 0.2548 0.4035  0.8567  3.7178 0.6331
ES Back       0.0153 0.0896 <strong>0.1521</strong>  0.3335  2.1128 0.2476
ES Opt        0.0303 0.1667 0.2565  0.5910  3.5887 0.4522
ES Two        0.0483 0.2561 0.4016  0.8626  3.5892 0.6309
MSARIMA Back  0.0614 0.3418 0.6947  0.9868  3.9677 0.7534
SSARIMA Back  0.0292 0.2963 0.8988  2.1729 13.7635 1.6581
CES Back      0.0146 0.0400 0.1834  <strong>0.2298</strong>  <strong>1.2099</strong> <strong>0.1713</strong>
GUM Back      0.0165 0.2101 1.5221  3.0543  9.5380 1.9506

# Separate table for special pools of ETS.
# The time is proportional to the number of models here
=========================================================
                  0%    25%    50%     75%    100%   mean
ES XXX        0.0114 0.0539 0.0782  0.1110  0.8163 0.0859
ES ZZZ        0.0147 0.1371 0.2690  0.4947  2.2049 0.3780
ES FFF        0.0529 0.2775 1.1539  1.5926  3.8552 1.1231
ES SXS        0.0323 0.1303 0.4491  0.6013  2.2170 0.4581
</pre>
<p><em><br />
I have manually moved the specific ES model pools flavours below because there is no point in comparing their computational time with the time of the others (they have different pools of models and thus are not really comparable with the rest).</em></p>
<p>What we can see from this, is that the ES with backcasting is faster in comparison with the other models in this setting (in terms of mean and median computational time). CES is very fast in terms of mean computational time, which is probably because of the very short pool of models to choose from (only four). SSARIMA is pretty slow, which is due to the nature of its order selection algorithm (I don't plan to update it any time soon, but if someone wants to contribute - let me know). But the interesting thing is that Auto ARIMA, while being relatively fine in terms of median time, has the highest maximum one, meaning that for some time series, it failed for some unknown reason. The series that caused the biggest issue for Auto ARIMA is N389 from the M1 competition. I'm not sure what the issue was, and I don't have time to investigate this.</p>
<div id="attachment_4002" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/02/smoot-4-4-0-time-vs-RMSSE.png&amp;nocache=1"><img fetchpriority="high" decoding="async" aria-describedby="caption-attachment-4002" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/02/smoot-4-4-0-time-vs-RMSSE-300x180.png&amp;nocache=1" alt="Mean computational time vs mean RMSSE" width="300" height="180" class="size-medium wp-image-4002" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/02/smoot-4-4-0-time-vs-RMSSE-300x180.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/02/smoot-4-4-0-time-vs-RMSSE-768x461.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/02/smoot-4-4-0-time-vs-RMSSE.png&amp;nocache=1 1000w" sizes="(max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-4002" class="wp-caption-text">Mean computational time vs mean RMSSE</p></div>
<p>Comparing the mean computational time with mean RMSSE value (image above), it looks like the overall tendency in the <code>smooth</code> + <code>forecast</code> functions for the M1, M3 and Tourism datasets is that additional computational time does not improve the accuracy. But it also looks like a simpler pool of pure additive models (ETS(X,X,X)) harms the accuracy in comparison with the branch-and-bound based one of the default <code>model="ZXZ"</code>. There seems to be a sweet spot in terms of the pool of models to choose from (no multiplicative trend, allow mixed models). This aligns well with the papers of <a href="https://doi.org/10.1080/01605682.2024.2421339">Petropoulos et al. (2025)</a>, who investigated the accuracy of arbitrary short pools of models and <a href="https://doi.org/10.1016/j.ijpe.2018.05.019">Kourentzes et al. (2019)</a>, who showed how pooling (if done correctly) can improve the accuracy on average.</p>
<h3 id="whatsNext">What's next?</h3>
<p>For R, the main task now is to rewrite the <code>oes()</code> function and substitute it with the <code>om()</code> one - "Occurrence Model". This should be equivalent to <code>adam()</code> in functionality, allowing to introduce ETS, ARIMA and explanatory variables for the occurrence part of the model. This is a huge work, which I hope to progress slowly throughout the 2026 and finish by the end of the year. Doing that will also allow me removing the last bits of the old C++ code and switch to the ADAM core completely, introducing more functionality for capturing patterns on intermittent demand. The minor task, is to test the <code>smoother="global"</code> more for the ETS initialisation and roll it out as the default in the next release for both R and Python.</p>
<p>For Python,... What Python? Ah! You'll see soon :)</p>
<p>Message <a href="https://openforecast.org/2026/02/09/smooth-v4-4-0/">smooth v4.4.0</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/02/09/smooth-v4-4-0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ITISE2025: Beyond summary performance metrics for forecast selection and combination</title>
		<link>https://openforecast.org/2025/07/21/itise2025-beyond-summary-performance-metrics-for-forecast-selection-and-combination/</link>
					<comments>https://openforecast.org/2025/07/21/itise2025-beyond-summary-performance-metrics-for-forecast-selection-and-combination/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 21 Jul 2025 10:11:48 +0000</pubDate>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[combinations]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[presentations]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3917</guid>

					<description><![CDATA[<p>This year, I couldn&#8217;t attend the International Symposium on Forecasting (organised by the International Institute of Forecasters), which I usually do, so instead I went to Gran Canaria for the International Conference on Time Series and Forecasting (aka ITISE). The location was fantastic, and I enjoyed several talks. I was also glad to catch up [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2025/07/21/itise2025-beyond-summary-performance-metrics-for-forecast-selection-and-combination/">ITISE2025: Beyond summary performance metrics for forecast selection and combination</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>This year, I couldn&#8217;t attend the International Symposium on Forecasting (organised by the International Institute of Forecasters), which I usually do, so instead I went to Gran Canaria for the International Conference on Time Series and Forecasting (aka <a href="https://itise.ugr.es/">ITISE</a>). The location was fantastic, and I enjoyed several talks. I was also glad to catch up and spend time with my friends and colleagues Juan Trapero, Devon Barrow, Kostas Nikolopoulos, Vasilios Bougakis, Livio Fenga, and Vittorio Maniezzo, all of whom delivered great presentations.</p>
<p>As for my contribution, I presented a paper that Nikos Kourentzes and I have been working on since around 2018. It focuses on pooling using point information criteria. The core idea is to combine forecasts based on a smaller pool of models, which we propose creating by comparing the distributions of information criteria across forecasting models. We&#8217;re planning to finish a new version of the paper by September and submit it to a peer-reviewed journal. I’ll share more details when the draft that I can share is ready. In the meantime, you can check out the slides that summarise the main points of the paper. <a href="https://openforecast.org/wp-content/uploads/2025/07/ITISE2025-Svetunkov-pAIC.pdf">Here they are</a>.</p>
<p>Message <a href="https://openforecast.org/2025/07/21/itise2025-beyond-summary-performance-metrics-for-forecast-selection-and-combination/">ITISE2025: Beyond summary performance metrics for forecast selection and combination</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2025/07/21/itise2025-beyond-summary-performance-metrics-for-forecast-selection-and-combination/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth v4.3.0 in R: what&#8217;s new and what&#8217;s next?</title>
		<link>https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/</link>
					<comments>https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Fri, 04 Jul 2025 10:02:17 +0000</pubDate>
				<category><![CDATA[Package smooth for R]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[CES]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[GUM]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3898</guid>

					<description><![CDATA[<p>Good news! The smooth package v4.3.0 is now on CRAN. And there are several things worth mentioning, so I have written this post. New default initialisation mechanism Since the beginning of the package, the smooth functions supported three ways for initialising the state vector (the vector that includes level, trend, seasonal indices): optimisation, backcasting and [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/">smooth v4.3.0 in R: what&#8217;s new and what&#8217;s next?</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Good news! The smooth package v4.3.0 is now on CRAN. And there are several things worth mentioning, so I have written this post.</p>
<h3>New default initialisation mechanism</h3>
<p>Since the beginning of the package, the <code>smooth</code> functions supported three ways for initialising the state vector (the vector that includes level, trend, seasonal indices): optimisation, backcasting and values provided by user. The former has been considered the standard way of estimating ETS, while the backcasting was originally proposed by Box &#038; Jenkins (1970) and was only implemented in the <code>smooth</code> (at least, I haven&#8217;t seen it anywhere else). The main advantage of the latter is in computational time, because you do not need to estimate every single value of the state vector. The new ADAM core that I developed during COVID lockdown, had some improvements for the backcasting, and I noticed that <code>adam()</code> produced more accurate forecasts with it than with the optimisation. But I needed more testing, so I have not changed anything back then.</p>
<p>However, my recent work with Kandrika Pritularga on capturing uncertainty in ETS, have demonstrated that backcasting solves some fundamental problems with the variance of states &#8211; the optimisation cannot handle so many parameters, and asymptotic properties of ETS do not make sense in that case (we&#8217;ll release the paper as soon as we finish the experiments). So, with this evidence on hands and additional tests, I have made a decision to switch from the optimisation to backcasting as the default initialisation mechanism for all the <strong>smooth</strong> functions.</p>
<p>The final users should not feel much difference, but it should work faster now and (hopefully) more accurately. If this is not the case, please get in touch or <a href="https://github.com/config-i1/smooth/issues">file an issue on github</a>.</p>
<p>Also, rest assured the <code>initial="optimal"</code> is available and will stay available as an option in all the <code>smooth</code> functions, so, you can always switch back to it if you don&#8217;t like backcasting.</p>
<p>Finally, I have introduce a new initialisation mechanism called &#8220;two-stage&#8221;, the idea of which is to apply backcasting first and then to optimise the obtained state values. It is slower, but is supposed to be better than the standard optimisation.</p>
<h3>ADAM core</h3>
<p>Every single function in the <code>smooth</code> package now uses ADAM C++ core, and the old core will be discontinued starting from v4.5.0 of the package. This applies to the functions: <code>es()</code>, <code>ssarima()</code>, <code>msarima()</code>, <code>ces()</code>, <code>gum()</code>, <code>sma()</code>. There are now the legacy versions of these functions in the package with the prefix &#8220;_old&#8221; (e.g. <code>es_old()</code>), which will be removed in the smooth v4.5.0. The new engine also helped <code>ssarima()</code>, which now became slightly more accurate than before. Unfortunately, there are still some issues with the initialisation of the seasonal <code>ssarima()</code>, which I have failed to solve completely. But I hope that over time this will be resolved as well.</p>
<h3>smooth performance update</h3>
<p>I have applied all the smooth functions together with the <code>ets()</code> and <code>auto.arima()</code> from the <code>forecast</code> package to the M1, M3 and Tourism competition data and have measured their performances in terms of RMSSE, scaled Cumulative Error (sCE) and computational time. I used the following R code for that:</p>
<div class="su-spoiler su-spoiler-style-fancy su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>Long and boring code in R</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode">library(Mcomp)
library(Tcomp)

library(forecast)
library(smooth)

# I work on Linux and use doMC. Substitute this with doParallel if you use Windows
library(doMC)
registerDoMC(detectCores())

# Create a small but neat function that will return a vector of error measures
errorMeasuresFunction <- function(object, holdout, insample){
	holdout <- as.vector(holdout);
	insample <- as.vector(insample);
	return(c(measures(holdout, object$mean, insample),
			 mean(holdout < object$upper &#038; holdout > object$lower),
			 mean(object$upper-object$lower)/mean(insample),
			 pinball(holdout, object$upper, 0.975)/mean(insample),
			 pinball(holdout, object$lower, 0.025)/mean(insample),
			 sMIS(holdout, object$lower, object$upper, mean(insample),0.95),
			 object$timeElapsed))
}

# Datasets to use
datasets <- c(M1,M3,tourism)
datasetLength <- length(datasets)
# Types of models to try
methodsNames <- c("ETS", "Auto ARIMA",
				  "ADAM ETS Back", "ADAM ETS Opt", "ADAM ETS Two",
				  "ES Back", "ES Opt", "ES Two",
				  "ADAM ARIMA Back", "ADAM ARIMA Opt", "ADAM ARIMA Two",
				  "MSARIMA Back", "MSARIMA Opt", "MSARIMA Two",
				  "SSARIMA Back", "SSARIMA Opt", "SSARIMA Two",
				  "CES Back", "CES Opt", "CES Two",
				  "GUM Back", "GUM Opt", "GUM Two");
methodsNumber <- length(methodsNames);
test <- adam(datasets[[125]]);

testResults20250603 <- array(NA,c(methodsNumber,datasetLength,length(test$accuracy)+6),
                             dimnames=list(methodsNames, NULL,
                                           c(names(test$accuracy),
                                             "Coverage","Range",
                                             "pinballUpper","pinballLower","sMIS",
                                             "Time")));

#### ETS from forecast package ####
j <- 1;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="forecast") %dopar% {
  startTime <- Sys.time()
  test <- ets(datasets[[i]]$x);
  testForecast <- forecast(test, h=datasets[[i]]$h, level=95);
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### AUTOARIMA ####
j <- 2;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="forecast") %dopar% {
    startTime <- Sys.time()
    test <- auto.arima(datasets[[i]]$x);
    testForecast <- forecast(test, h=datasets[[i]]$h, level=95);
    testForecast$timeElapsed <- Sys.time() - startTime;
    return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ETS Backcasting ####
j <- 3;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- adam(datasets[[i]],"ZXZ", initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ETS Optimal ####
j <- 4;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- adam(datasets[[i]],"ZXZ", initial="opt");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ETS Two-stage ####
j <- 5;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- adam(datasets[[i]],"ZXZ", initial="two");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ES Backcasting ####
j <- 6;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- es(datasets[[i]],"ZXZ", initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ES Optimal ####
j <- 7;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- es(datasets[[i]],"ZXZ", initial="opt");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ES Two-stage ####
j <- 8;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- es(datasets[[i]],"ZXZ", initial="two");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ARIMA Backcasting ####
j <- 9;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.adam(datasets[[i]], "NNN", initial="back", distribution=c("dnorm"));
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ARIMA Optimal ####
j <- 10;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.adam(datasets[[i]], "NNN", initial="opt", distribution=c("dnorm"));
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### ADAM ARIMA Two-stage ####
j <- 11;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.adam(datasets[[i]], "NNN", initial="two", distribution=c("dnorm"));
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="pred");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### MSARIMA Backcasting ####
j <- 12;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.msarima(datasets[[i]], initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### MSARIMA Optimal ####
j <- 13;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.msarima(datasets[[i]], initial="opt");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### MSARIMA Two-stage ####
j <- 14;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.msarima(datasets[[i]], initial="two");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### SSARIMA Backcasting ####
j <- 15;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.ssarima(datasets[[i]], initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### SSARIMA Optimal ####
j <- 16;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="forecast") %dopar% {
    startTime <- Sys.time()
    test <- auto.ssarima(datasets[[i]], initial="opt");
    testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
    testForecast$timeElapsed <- Sys.time() - startTime;
    return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### SSARIMA Two-stage ####
j <- 17;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="forecast") %dopar% {
    startTime <- Sys.time()
    test <- auto.ssarima(datasets[[i]], initial="two");
    testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
    testForecast$timeElapsed <- Sys.time() - startTime;
    return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### CES Backcasting ####
j <- 18;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.ces(datasets[[i]], initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### CES Optimal ####
j <- 19;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.ces(datasets[[i]], initial="opt");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### CES Two-stage ####
j <- 20;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.ces(datasets[[i]], initial="two");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### GUM Backcasting ####
j <- 21;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.gum(datasets[[i]], initial="back");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### GUM Optimal ####
j <- 22;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.gum(datasets[[i]], initial="opt");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);

#### GUM Two-stage ####
j <- 23;
result <- foreach(i=1:datasetLength, .combine="cbind", .packages="smooth") %dopar% {
  startTime <- Sys.time()
  test <- auto.gum(datasets[[i]], initial="two");
  testForecast <- forecast(test, h=datasets[[i]]$h, interval="parametric");
  testForecast$timeElapsed <- Sys.time() - startTime;
  return(errorMeasuresFunction(testForecast, datasets[[i]]$xx, datasets[[i]]$x));
}
testResults20250603[j,,] <- t(result);</pre>
<p># Summary of results<br />
cbind(t(apply(testResults20250603[c(1:8,12:23),,"RMSSE"],1,quantile)),<br />
	  mean=apply(testResults20250603[c(1:8,12:23),,"RMSSE"],1,mean),<br />
	  sCE=apply(testResults20250603[c(1:8,12:23),,"sCE"],1,mean),<br />
	  Time=apply(testResults20250603[c(1:8,12:23),,"Time"],1,mean)) |> round(3)<br />
</div></div>
<p>The table below shows the distribution of RMSSE, the mean sCE and mean Time. The boldface shows the best performing model.</p>
<pre>                    min   Q1  median   Q3     max  mean    sCE  Time
ETS                0.024 0.677 1.181 2.376  51.616 1.970  0.299 0.385
Auto ARIMA         0.025 0.680 1.179 2.358  51.616 1.986  0.124 1.467

ADAM ETS Back      <strong>0.015</strong> <strong>0.666</strong> 1.175 <strong>2.276</strong>  51.616 <strong>1.921</strong>  0.470 0.218
ADAM ETS Opt       0.020 <strong>0.666</strong> 1.190 2.311  51.616 1.937  0.299 0.432
ADAM ETS Two       0.025 <strong>0.666</strong> 1.179 2.330  51.616 1.951  0.330 0.579

ES Back            <strong>0.015</strong> 0.672 <strong>1.174</strong> 2.284  51.616 <strong>1.921</strong>  0.464 0.219
ES Opt             0.020 0.672 1.186 2.316  51.616 1.943  0.302 0.497
ES Two             0.024 0.668 1.181 2.346  51.616 1.952  0.346 0.562

MSARIMA Back       0.025 0.710 1.188 2.383  51.616 2.028  <strong>0.067</strong> 0.780
MSARIMA Opt        0.025 0.724 1.242 2.489  51.616 2.083  0.088 1.905
MSARIMA Two        0.025 0.718 1.250 2.485  51.906 2.075  0.083 2.431

SSARIMA Back       0.045 0.738 1.248 2.383  51.616 2.063  0.167 1.747
SSARIMA Opt        0.025 0.774 1.292 2.413  51.616 2.040  0.178 7.324
SSARIMA Two        0.025 0.742 1.241 2.414  51.616 2.027  0.183 8.096

CES Back           0.046 0.695 1.189 2.355  51.342 1.981  0.125 <strong>0.185</strong>
CES Opt            0.030 0.698 1.218 2.327  <strong>49.480</strong> 2.001 -0.135 0.834
CES Two            0.025 0.696 1.207 2.343  51.242 1.993 -0.078 1.006

GUM Back           0.046 0.707 1.215 2.399  51.134 2.049 -0.285 3.575
GUM Opt            0.026 0.795 1.381 2.717 240.143 2.932 -0.549 4.668
GUM Two            0.026 0.803 1.406 2.826 240.143 3.041 -0.593 4.703</pre>
<p>Several notes:</p>
<ul>
<li>ES is a wrapper of ADAM ETS. The main difference between them is that the latter uses the Gamma distribution for the multiplicative error models, while the former relies on the Normal one.</li>
<li>MSARIMA is a wrapper for ADAM ARIMA, which is why I don't report the latter in the results.</li>
</ul>
<p>One thing you can notice from the output above, is that the models with backcasting consistently produce more accurate forecasts across all measures. I explain this with the idea that they tend not to overfit the data as much as the optimal initialisation does.</p>
<p>To see the stochastic dominance of the forecasting models, I conducted the modification of the MCB/Nemenyi test, explained in <a href="/2020/08/17/accuracy-of-forecasting-methods-can-you-tell-the-difference/">this post</a>:</p>
<pre class="decode">par(mar=c(10,3,4,1))
greybox::rmcb(t(testResults20250603[c(1:8,12:23),,"RMSSE"]), outplot="mcb")</pre>
<div id="attachment_3908" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0.png&amp;nocache=1"><img decoding="async" aria-describedby="caption-attachment-3908" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0-300x175.png&amp;nocache=1" alt="Nemenyi test for the smooth functions" width="300" height="175" class="size-medium wp-image-3908" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0-300x175.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0-1024x597.png&amp;nocache=1 1024w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0-768x448.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2025/07/2025-07-04-smooth-v4-3-0.png&amp;nocache=1 1200w" sizes="(max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-3908" class="wp-caption-text">Nemenyi test for the smooth functions</p></div>
<p>The image shows mean ranks for each of the models and whether the performance of those is significant on the 5% level or not. It is apparent that ADAM ETS has the lowest rank, no matter what the initialisation is used, but its performance does not differ significantly from the <code>es()</code>, <code>ets()</code> and <code>auto.arima()</code>. Also, <code>auto.arima()</code> significantly outperforms <code>msarima()</code> and <code>ssarima()</code> on this data, which could be due to their initialisation. Still, backcasting seems to help all the functions in terms of accuracy in comparison with the "optimal" and "two-stage" initials.</p>
<h3>What's next?</h3>
<p>I am now working on a modified formulation for ETS, which should fix some issues with the multiplicative trend and make the ETS safer. This is based on <a href="https://openforecast.org/adam/ADAMETSMultiplicativeAlternative.html">Section 6.6</a> of the online version of the ADAM monograph (it is not in the printed version). I am not sure whether this will improve the accuracy further, but I hope that it will make some of the ETS models more resilient than they are right now. I specifically need the multiplicative trend model, which sometimes behave like crazy due to its formulation.</p>
<p>I also plan to translate all the simulation functions to the ADAM core. This applies to <code>sim.es()</code>, <code>sim.ssarima()</code>, <code>sim.gum()</code> and <code>sim.ces()</code>. Currently they rely on the older one, and I want to get rid of it. Having said that, the method <code>simulate()</code> applied to the new <code>smooth</code> functions already uses the new core. It just lacks the flexibility that the other functions have.</p>
<p>Furthermore, I want to rewrite the <code>oes()</code> function and substitute it with <code>oadam()</code>, which would use a better engine, supporting more features, such as multiple frequencies and ARIMA for the occurrence. This is a lot of work, and I probably will need help with that.</p>
<p>Finally, Filotas Theodosiou, Leonidas Tsaprounis, and I are working on the translation of the R code of the <code>smooth</code> to Python. You can read a bit more about this project <a href="/2025/06/30/iif-open-source-forecasting-software-workshop-and-smooth/">here</a>. There are several other people who decided to help us, but the progress so far has been a bit slow, because of the code translation. If you want to help, please get in touch.</p>
<p>Message <a href="https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/">smooth v4.3.0 in R: what&#8217;s new and what&#8217;s next?</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multistep loss functions: Geometric Trace MSE</title>
		<link>https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/</link>
					<comments>https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Tue, 04 Jun 2024 09:05:56 +0000</pubDate>
				<category><![CDATA[Social media]]></category>
		<category><![CDATA[Theory of forecasting]]></category>
		<category><![CDATA[Univariate models]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[estimators]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[statistics]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3594</guid>

					<description><![CDATA[<p>While there is a lot to say about multistep losses, I&#8217;ve decided to write the final post on one of them and leave the topic alone for a while. Here it goes. Last time, we discussed MSEh and TMSE, and I mentioned that both of them impose shrinkage and have some advantages and disadvantages. One [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/">Multistep loss functions: Geometric Trace MSE</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>While there is a lot to say about multistep losses, I&#8217;ve decided to write the final post on one of them and leave the topic alone for a while. Here it goes.</p>
<p>Last time, we discussed <a href="/2024/05/25/recursive-vs-direct-forecasting-strategy/">MSEh</a> and <a href="/2024/06/01/multistep-loss-functions-trace-mse/">TMSE</a>, and I mentioned that both of them impose shrinkage and have some advantages and disadvantages. One of the main advantages of TMSE was in reducing computational time in comparison with MSEh: you just fit one model with it instead of doing it h times. However, the downside of TMSE is that it averages things out, and we end up with model parameters that minimize the h-steps-ahead forecast error to a much larger extent than those that are close to the one-step-ahead. For example, if the one-step-ahead MSE was 500, while the six-steps-ahead MSE was 3000, the impact of the latter in TMSE would be six times higher than that of the former, and the estimator would prioritize the minimization of the longer horizon one.</p>
<p>A more balanced version of this was introduced in <a href="/2023/08/09/multi-step-estimators-and-shrinkage-effect-in-time-series-models/">our paper</a> and was called &#8220;Geometric Trace MSE&#8221; (GTMSE). The main idea of GTMSE is to take the geometric mean or, equivalently, the sum of logarithms of MSEh instead of taking the arithmetic mean. Because of that, the impact of MSEh on the loss becomes comparable with the effect of MSE1, and the model performs well throughout the whole horizon from 1 to h. For the same example of MSEs as above, the logarithm of 500 is approximately 2.7, while the logarithm of 3000 is 3.5. The difference between the two is much smaller, reducing the impact of the long-term forecast uncertainty. As a result, GTMSE has the following features:</p>
<ul>
<li>It imposes shrinkage on models parameters.</li>
<li>The strength of shrinkage is proportional to the forecast horizon.</li>
<li>But it is much milder than in case of MSEh or TMSE.</li>
<li>It leads to more balanced forecasts, performing well on average across the whole horizon.</li>
</ul>
<p>In that paper, we did extensive simulations to see how different estimators behave, and we found that:</p>
<ol>
<li>If an analyst is interested in parameters of models, they should stick with the conventional loss functions (based on one-step-ahead forecast error) because the multistep ones tend to produce biased estimates of parameters.</li>
<li>On the other hand, multistep losses kick off the redundant parameters faster than the conventional one, so there might be a benefit in the case of overparameterized models.</li>
<li>At the same time, if forecasting is of the main interest, then multistep losses might bring benefits, especially on larger samples.</li>
</ol>
<div id="attachment_3595" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example.png&amp;nocache=1"><img decoding="async" aria-describedby="caption-attachment-3595" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example-300x200.png&amp;nocache=1" alt="ETS(A,A,A) estimated using different loss functions applied to the data with multiplicative seasonality" width="300" height="200" class="size-medium wp-image-3595" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example-300x200.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example-1024x681.png&amp;nocache=1 1024w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example-768x511.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/06/2024-06-04-Multistep-Example.png&amp;nocache=1 1049w" sizes="(max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-3595" class="wp-caption-text">ETS(A,A,A) estimated using different loss functions applied to the data with multiplicative seasonality</p></div>
<p>The image above shows an example from our paper, where we applied the additive model to the data, which exhibits apparent multiplicative seasonality. Despite that, we can see that multistep losses did a much better job than the conventional MSE, compensating for the misspecification.</p>
<p>Message <a href="https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/">Multistep loss functions: Geometric Trace MSE</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Detecting patterns in white noise</title>
		<link>https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/</link>
					<comments>https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Wed, 10 Apr 2024 08:16:58 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3413</guid>

					<description><![CDATA[<p>Back in 2015, when I was working on my paper on Complex Exponential Smoothing, I conducted a simple simulation experiment to check how ARIMA and ETS select components/orders in time series. And I found something interesting&#8230; One of the important steps in forecasting with statistical models is identifying the existing structure. In the case of [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/">Detecting patterns in white noise</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Back in 2015, when I was working on my paper on <a href="/2022/08/02/complex-exponential-smoothing/">Complex Exponential Smoothing</a>, I conducted a simple simulation experiment to check how ARIMA and ETS select components/orders in time series. And I found something interesting&#8230;</p>
<p>One of the important steps in forecasting with statistical models is identifying the existing structure. In the case of ETS, it comes to selecting trend/seasonal components, while for ARIMA, it&#8217;s about order selection. In R, several functions automatically handle this based on information criteria (<a href="https://doi.org/10.18637/jss.v027.i03">Hyndman &#038; Khandakar, 2006</a>; <a href="https://doi.org/10.1080/00207543.2019.1600764">Svetunkov &#038; Boylan (2017)</a>; <a href="https://openforecast.org/adam/ADAMSelection.html">Chapter 15 of ADAM</a>). I decided to investigate how this mechanism works.</p>
<p>I generated data from the Normal distribution with a fixed mean of 5000 and a standard deviation of 50. Then, I asked ETS and ARIMA (from the forecast package in R) to automatically select the appropriate model for each of 1000 time series. Here is the R code for this simple experiment:</p>
<div class="su-accordion su-u-trim"><div class="su-spoiler su-spoiler-style-default su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>Some R code</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode"># Set random seed for reproducibility
set.seed(41, kind="L'Ecuyer-CMRG")
# Number of iterations
nsim <- 1000
# Number of observations
obsAll <- 120
# Generate data from N(5000, 50)
rnorm(nsim*obsAll, 5000, 50) |>
  matrix(obsAll, nsim) |>
  ts(frequency=12) -> x

# Load forecast package
library(forecast)
# Load doMC for parallel calculations
# doMC is only available on Linux and Max
# Use library(doParallel) on Windows
library(doMC)
registerDoMC(detectCores())

# A loop for ARIMA, recording the orders
matArima <- foreach(i=1:nsim, .combine=cbind, .packages=c("forecast")) %dopar% {
    testModel <- auto.arima(x&#091;,i&#093;)
    # The element number 5 is just m, period of seasonality
    return(c(testModel$arma&#091;-5&#093;,(!is.na(testModel$coef&#091;"drift"&#093;))*1))
}
rownames(matArima) <- c("AR","MA","SAR","SMA","I","SI","Drift")

# A loop for ETS, recording the model types
matEts <- foreach(i=1:nsim, .combine=cbind, .packages=c("forecast")) %dopar% {
    testModel <- ets(x&#091;,i&#093;, allow.multiplicative.trend=TRUE)
    return(testModel&#091;13&#093;$method)
}
</pre>
</div></div></div>
<p>The findings of this experiment are summarised using the following chunk of the R code:</p>
<div class="su-accordion su-u-trim"><div class="su-spoiler su-spoiler-style-default su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>R code for the analysis of the results</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode">
#### Auto ARIMA ####
# Non-seasonal ARIMA elements
mean(apply(matArima[c("AR","MA","I","Drift"),]!=0, 2, any))
# Seasonal ARIMA elements
mean(apply(matArima[c("SAR","SMA","SI"),]!=0, 2, any))

#### ETS ####
# Trend in ETS
mean(substr(matEts,7,7)!="N")
# Seasonality in ETS
mean(substr(matEts,nchar(matEts)-1,nchar(matEts)-1)!="N")</pre>
</div></div></div>
<p>I summarised them in the following table:</p>
<table>
<thead>
<tr>
<td></td>
<td><strong>ARIMA</strong></td>
<td><strong>ETS</strong></td>
</tr>
</thead>
<tr>
<td>Non-seasonal elements</td>
<td>24.8%</td>
<td>2.3%</td>
</tr>
<tr>
<td>Seasonal elements</td>
<td>18.0%</td>
<td>0.2%</td>
</tr>
<tr>
<td>Any type of structure</td>
<td>37.9%</td>
<td>2.4%</td>
</tr>
</table>
<p>So, ARIMA detected some structure (had non-zero orders) in almost 40% of all time series, even though the data was designed to have no structure (just white noise). It also captured non-seasonal orders in a quarter of the series and identified seasonality in 18% of them. ETS performed better (only 0.2% of seasonal models identified on the white noise), but still captured trends in 2.3% of cases.</p>
<p>Does this simple experiment suggest that ARIMA is a bad model and ETS is a good one? No, it does not. It simply demonstrates that ARIMA tends to overfit the data if allowed to select whatever it wants. How can we fix that?</p>
<p>My solution: restrict the pool of ARIMA models to check, preventing it from going crazy. My personal pool includes ARIMA(0,1,1), (1,1,2), (0,2,2), along with the seasonal orders of (0,1,1), (1,1,2), and (0,2,2), and combinations between them. This approach is motivated by the connection between <a href="https://openforecast.org/adam/ARIMAandETS.html">ARIMA and ETS</a>. Additionally, we can check whether the addition of AR/MA orders detected by ACF/PACF analysis of the best model reduces the AICc. If not, they shouldn't be included.</p>
<p>This algorithm can be written in the following simple function that uses <code>msarima()</code> function from the smooth package in R (note that the reason why this function is used is because all ARIMA models implemented in the function are directly comparable via information criteria):</p>
<div class="su-accordion su-u-trim"><div class="su-spoiler su-spoiler-style-default su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>R code for the compact ARIMA function</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode">arimaCompact <- function(y, lags=c(1,frequency(y)), ic=c("AICc","AIC","BIC","BICc"), ...){

    # Start measuring the time of calculations
    startTime <- Sys.time();

    # If there are no lags for the basic components, correct this.
    if(sum(lags==1)==0){
        lags <- c(1,lags);
    }

    orderLength <- length(lags);
    ic <- match.arg(ic);
    IC <- switch(ic,
                 "AIC"=AIC,
                 "AICc"=AICc,
                 "BIC"=BIC,
                 "BICc"=BICc);

    # We consider the following list of models:
    # ARIMA(0,1,1), (1,1,2), (0,2,2),
    # ARIMA(0,0,0)+c, ARIMA(0,1,1)+c,
    # seasonal orders (0,1,1), (1,1,2), (0,2,2)
    # And all combinations between seasonal and non-seasonal parts
    # 
    # Encode all non-seasonal parts
    nNonSeasonal <- 5
    arimaNonSeasonal <- matrix(c(0,1,1,0, 1,1,2,0, 0,2,2,0, 0,0,0,1, 0,1,1,1), nNonSeasonal,4,
                               dimnames=list(NULL, c("ar","i","ma","const")), byrow=TRUE)
    # Encode all seasonal parts ()
    nSeasonal <- 4
    arimaSeasonal <- matrix(c(0,0,0, 0,1,1, 1,1,2, 0,2,2), nSeasonal,3,
                               dimnames=list(NULL, c("sar","si","sma")), byrow=TRUE)

    # Check all the models in the pool
    testModels <- vector("list", nSeasonal*nNonSeasonal);
    m <- 1;
    for(i in 1:nSeasonal){
        for(j in 1:nNonSeasonal){
            testModels&#091;&#091;m&#093;&#093; <- msarima(y, orders=list(ar=c(arimaNonSeasonal&#091;j,1&#093;,arimaSeasonal&#091;i,1&#093;),
                                                      i=c(arimaNonSeasonal&#091;j,2&#093;,arimaSeasonal&#091;i,2&#093;),
                                                      ma=c(arimaNonSeasonal&#091;j,3&#093;,arimaSeasonal&#091;i,3&#093;)),
                                       constant=arimaNonSeasonal&#091;j,4&#093;==1, lags=lags, ...);
            m&#091;&#093; <- m+1;
        }
    }

    # Find the best one
    m <- which.min(sapply(testModels, IC));
    # Amend computational time
    testModels&#091;&#091;m&#093;&#093;$timeElapsed <- Sys.time()-startTime;

    return(testModels&#091;&#091;m&#093;&#093;);
}</pre>
</div></div></div>
<p>Additionally, we can check whether the addition of AR/MA orders detected by ACF/PACF analysis of the best model reduces the AICc. If not, they shouldn't be included. I have not added that part in the code above. Still, this algorithm brings some improvements:</p>
<div class="su-accordion su-u-trim"><div class="su-spoiler su-spoiler-style-default su-spoiler-icon-plus su-spoiler-closed" data-scroll-offset="0" data-anchor-in-url="no"><div class="su-spoiler-title" tabindex="0" role="button"><span class="su-spoiler-icon"></span>R code for the application of compact ARIMA to the data</div><div class="su-spoiler-content su-u-clearfix su-u-trim">
<pre class="decode">#### Load the smooth package
library(smooth)

# A loop for the compact ARIMA, recording the orders
matArimaCompact <- foreach(i=1:nsim, .packages=c("smooth")) %dopar% {
    testModel <- arimaCompact(x&#091;,i&#093;)
    return(orders(testModel))
}

#### Auto MSARIMA from smooth ####
# Non-seasonal ARIMA elements
mean(sapply(sapply(matArimaCompact, "&#091;&#091;", "ar"), function(x){x&#091;1&#093;!=0}) |
  sapply(sapply(matArimaCompact, "&#091;&#091;", "i"), function(x){x&#091;1&#093;!=0}) |
  sapply(sapply(matArimaCompact, "&#091;&#091;", "ma"), function(x){x&#091;1&#093;!=0}))

# Seasonal ARIMA elements
mean(sapply(sapply(matArimaSmooth, "&#091;&#091;", "ar"), function(x){length(x)==2 &#038;&#038; (x&#091;2&#093;!=0)}) |
  sapply(sapply(matArimaSmooth, "&#091;&#091;", "i"), function(x){length(x)==2 &#038;&#038; (x&#091;2&#093;!=0)}) |
  sapply(sapply(matArimaSmooth, "&#091;&#091;", "ma"), function(x){length(x)==2 &#038;&#038; (x&#091;2&#093;!=0)}))
</pre>
</div></div></div>
<p>In my case, it resulted in the following:</p>
<table>
<thead>
<tr>
<td></td>
<td><strong>ARIMA</strong></td>
<td><strong>ETS</strong></td>
<td style="text-align: center"><strong>Compact ARIMA</strong></td>
</tr>
</thead>
<tr>
<td>Non-seasonal elements</td>
<td>24.8%</td>
<td>2.3%</td>
<td style="text-align: center">2.4%</td>
</tr>
<tr>
<td>Seasonal elements</td>
<td>18.0%</td>
<td>0.2%</td>
<td style="text-align: center">0.0%</td>
</tr>
<tr>
<td>Any type of structure</td>
<td>37.9%</td>
<td>2.4%</td>
<td style="text-align: center">2.4%</td>
</tr>
</table>
<p>As we see, when we impose restrictions on order selection in ARIMA, it avoids fitting seasonal models to non-seasonal data. While it still makes minor mistakes in terms of non-seasonal structure, it's nothing compared to the conventional approach. What about accuracy? I don't know. I'll have to write another post on this :).</p>
<p>Note that the models were applied to samples of 120 observations, which is considered "small" in statistics, while in real life is sometimes a luxury to have...</p>
<p>Message <a href="https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/">Detecting patterns in white noise</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>What does &#8220;lower error measure&#8221; really mean?</title>
		<link>https://openforecast.org/2024/03/27/what-does-lower-error-measure-really-mean/</link>
					<comments>https://openforecast.org/2024/03/27/what-does-lower-error-measure-really-mean/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Wed, 27 Mar 2024 18:29:03 +0000</pubDate>
				<category><![CDATA[Forecast evaluation]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[CES]]></category>
		<category><![CDATA[error measures]]></category>
		<category><![CDATA[ETS]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3380</guid>

					<description><![CDATA[<p>&#8220;My amazing forecasting method has a lower MASE than any other method!&#8221; You&#8217;ve probably seen claims like this on social media or in papers. But have you ever thought about what it really means? Many forecasting experiments come to applying several approaches to a dataset, calculating error measures for each method per time series and [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/03/27/what-does-lower-error-measure-really-mean/">What does &#8220;lower error measure&#8221; really mean?</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>&#8220;My amazing forecasting method has a lower MASE than any other method!&#8221; You&#8217;ve probably seen claims like this on social media or in papers. But have you ever thought about what it really means?</p>
<p>Many forecasting experiments come to applying several approaches to a dataset, calculating error measures for each method per time series and aggregating them to get a neat table like this one (based on the M and tourism competitions, with R code similar to the one from <a href="/2021/01/13/the-creation-of-adam-next-step-in-statistical-forecasting/">this post</a>):</p>
<pre>           RMSSE    sCE
ADAM ETS   <strong>1.947</strong>  0.319
ETS        1.970  0.299
ARIMA      1.986  0.125
CES        1.960 <strong>-0.011</strong></pre>
<p>Typically, the conclusion drawn from such tables is that the approach with the measure closest to zero performs the best, on average. I&#8217;ve done this myself many times because it&#8217;s a simple way to present results. So, what&#8217;s the issue?</p>
<p>Well, almost any error measure has a skewed distribution because it cannot be lower than zero, and its highest value is infinity (this doesn&#8217;t apply to bias). Let me show you:</p>
<div id="attachment_3381" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-03-vioplot.png&amp;nocache=1"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-3381" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-03-vioplot-300x150.png&amp;nocache=1" alt="Distribution of RMSSE for ADAM ETS on M and tourism competitions data" width="300" height="150" class="size-medium wp-image-3381" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-03-vioplot-300x150.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-03-vioplot-768x384.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-03-vioplot.png&amp;nocache=1 800w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-3381" class="wp-caption-text">Distribution of RMSSE for ADAM ETS on M and tourism competitions data</p></div>
<p>This figure shows the distribution of 5,315 RMSSE values for ADAM ETS. As seen from the violin plot, the distribution has a peak close to zero and a very long tail. This suggests that the model performed well on many series but generated inaccurate forecasts for just a few, or perhaps only one of them. The mean RMSSE is 1.947 (vertical red line on the plot). However, this single value alone does not provide full information about the model&#8217;s performance. Firstly, it tries to represent the entire distribution with just one number. Secondly, as we know from statistics, the mean is influenced by outliers: if your method performed exceptionally well on 99% of cases but poorly on just 1%, the mean will be higher than in the case of a poorly performing method that did not do that badly on 1%.</p>
<p>So, what should we do?</p>
<p>At the very least, provide both mean and median error measures: the distance between them shows how skewed the distribution is. But an even better approach would be to report mean and several quantiles of error measures (not just median). For instance, we could present the 1st, 2nd (median), and 3rd quartiles together with mean, min and max to offer a clearer understanding of the spread and variability of the error measure:</p>
<pre>             min    1Q  median    3Q     max  mean
ADAM ETS   <strong>0.024</strong> <strong>0.670</strong>   1.180 2.340  51.616 <strong>1.947</strong>
ETS        <strong>0.024</strong> 0.677   1.181 2.376  51.616 1.970
ARIMA      0.025 0.681   1.179 2.358  51.616 1.986
CES        0.045 0.675   <strong>1.171</strong> <strong>2.330</strong>  <strong>51.201</strong> 1.960</pre>
<p>This table provides better insights: ETS models consistently perform well in terms of mean, min, and first quartile RMSSE, while ARIMA outperformed them in terms of median. CES did better than the others in terms of median, third quartile, and the maximum. This means that there were some time series, where ETS struggled a bit more than CES, but in the majority of cases it performed well.</p>
<p>So, next time you see a table with error measures, keep in mind that the best method on average might not be the best consistently. Having more details helps in understanding the situation better.</p>
<p>You can read more about error measures for forecasting in the &#8220;<a href="/category/forecasting-theory/forecast-evaluation/">forecast evaluation</a>&#8221; category.</p>
<p>Message <a href="https://openforecast.org/2024/03/27/what-does-lower-error-measure-really-mean/">What does &#8220;lower error measure&#8221; really mean?</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/03/27/what-does-lower-error-measure-really-mean/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>The role of M competitions in forecasting</title>
		<link>https://openforecast.org/2024/03/14/the-role-of-m-competitions-in-forecasting/</link>
					<comments>https://openforecast.org/2024/03/14/the-role-of-m-competitions-in-forecasting/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Thu, 14 Mar 2024 14:53:03 +0000</pubDate>
				<category><![CDATA[Applied forecasting]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[stories]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3358</guid>

					<description><![CDATA[<p>If you are interested in forecasting, you might have heard of M-competitions. They played a pivotal role in developing forecasting principles, yet also sparked controversy. In this short post, I&#8217;ll briefly explain their historical significance and discuss their main findings. Before M-competitions, only few papers properly evaluated forecasting approaches. Statisticians assumed that if a model [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/03/14/the-role-of-m-competitions-in-forecasting/">The role of M competitions in forecasting</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>If you are interested in forecasting, you might have heard of M-competitions. They played a pivotal role in developing forecasting principles, yet also sparked controversy. In this short post, I&#8217;ll briefly explain their historical significance and discuss their main findings.</p>
<p>Before M-competitions, only few papers properly evaluated forecasting approaches. Statisticians assumed that if a model had solid theoretical backing, it should perform well. One of the first papers to conduct a proper evaluation was <a href="https://doi.org/10.2307/2344546">Newbold &#038; Granger (1974)</a>, who compared exponential smoothing (ES), ARIMA, and stepwise AR on 106 economic time series. Their conclusions were:</p>
<p>1. ES performed well on short time series;<br />
2. Stepwise AR did well on the series with more than 30 observations;<br />
3. Box-Jenkins methodology was recommended for series longer than 50 observations.</p>
<p>Statistical community received the results favourably, as they aligned with their expectations.</p>
<p>In 1979, <a href="https://doi.org/10.2307/2345077">Makridakis &#038; Hibon</a> conducted a similar analysis on 111 time series, including various ES methods and ARIMA. However, they found that &#8220;simpler methods perform well in comparison to the more complex and statistically sophisticated ARMA models&#8221;. This is because ARIMA performed slightly worse than ES, which contradicted the findings of Newbold &#038; Granger. Furthermore, their paper faced heavy criticism, with some claiming that Makridakis did not correctly utilize Box-Jenkins methodology.</p>
<p>So, in 1982, <a href="https://doi.org/10.1002/for.3980010202">Makridakis et al.</a> organized a competition on 1001 time series, inviting external participants to submit their forecasts. It was won by&#8230; the <a href="https://doi.org/10.1002/for.3980010108">ARARMA model by Emmanuel Parzen</a>. This model used information criteria for ARMA order selection instead of Box-Jenkins methodology. The main conclusion drawn from this competition was that &#8220;<strong>Statistically sophisticated or complex methods do not necessarily provide more accurate forecasts than simpler ones</strong>.&#8221; Note that this does not mean that simple methods are always better, because that was not even the case in the first competition: it was won by a quite complicated statistical model based on ARMA. This only means that the complexity does not necessarily translate into accuracy.</p>
<p>The M2 competition focused on judgmental forecasting, and is not discussed here.</p>
<p>We then arrive to <a href="https://doi.org/10.1016/S0169-2070(00)00057-1">M3 competition</a> with 3003 time series and, once again, open submission for anyone. The results widely confirmed the previous findings, with <a href="https://doi.org/10.1016/S0169-2070(00)00066-2">Theta</a> by Vasilious Assimakopoulos and Kostas Nikolopoulos outperforming all the other methods. Note that ARIMA with order selection based on Box-Jenkins methodology performed fine, but could not beat its competitors.</p>
<p>Finally, we arrive to <a href="https://doi.org/10.1016/j.ijforecast.2019.04.014">M4 competition</a>, which had 100,000 time series and was open to even wider audience. While I have <a href="https://openforecast.org/2020/03/01/m-competitions-from-m4-to-m5-reservations-and-expectations/">my reservations about the competition itself</a>, there were several curious findings, including the fact that ARIMA implemented by <a href="https://doi.org/10.18637/jss.v027.i03">Hyndman &#038; Khandakar (2008)</a> performed on average better than ETS (Theta outperformed both of them), and that the more complex methods won the competition.</p>
<p>It was also the first paper to show that the accuracy tends to increase on average with the increase of the computational time spent for training. This means that if you want to have more accurate forecasts, you need to spend more resources. The only catch is that this happens with the decreasing return effect. So, the improvements become smaller and smaller the more time you spend on training.</p>
<p>The competition was followed by M5 and M6, and now they plan to have another one. I don&#8217;t want to discuss all of them &#8211; they are beyond the scope of this short post (see details on the <a href="https://mofc.unic.ac.cy/history-of-competitions/">website of the competitions</a>). But I personally find the first competitions very impactful and useful.</p>
<p>And here are my personal takeaways from these competitions:</p>
<p>1. Simple forecasting methods perform well and should be included as benchmarks in experiments;<br />
2. Complex methods can outperform simple ones, especially if used intelligently, but you might need to spend more resources to gain in accuracy;<br />
3. ARIMA is effective, but Box-Jenkins methodology may not be practical. Using information criteria for order selection is a better approach (as evidenced from ARARMA example and Hydnman &#038; Khandakar implementation).</p>
<p>Finally, I like the following <a href="https://robjhyndman.com/hyndsight/m4comp/">quote from Rob J. Hyndman about the competitions</a> that gives some additional perspective: &#8220;The &#8220;M&#8221; competitions organized by Spyros Makridakis have had an enormous influence on the field of forecasting. They focused attention on what models produced good forecasts, rather than on the mathematical properties of those models&#8221;.</p>
<div id="attachment_3360" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-13-M3-competition.png&amp;nocache=1"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-3360" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-13-M3-competition-300x182.png&amp;nocache=1" alt="Table with the results of the M3 competition" width="300" height="182" class="size-medium wp-image-3360" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-13-M3-competition-300x182.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-13-M3-competition-768x467.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-13-M3-competition.png&amp;nocache=1 923w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-3360" class="wp-caption-text">Table with the results of the M3 competition</p></div>
<p>Message <a href="https://openforecast.org/2024/03/14/the-role-of-m-competitions-in-forecasting/">The role of M competitions in forecasting</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/03/14/the-role-of-m-competitions-in-forecasting/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Why you should not use Holt-Winters method</title>
		<link>https://openforecast.org/2024/03/07/why-you-should-not-use-holt-winters-method/</link>
					<comments>https://openforecast.org/2024/03/07/why-you-should-not-use-holt-winters-method/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Thu, 07 Mar 2024 11:52:30 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[Univariate models]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3350</guid>

					<description><![CDATA[<p>Whenever I see results of an experiment that include Holt-Winters method, I shrug. You should not use it, and here is why. Holt-Winters was developed in 1960 by a student of Charles Holt, Peter Winters (Winters, 1960). He extended Holt&#8217;s exponential smoothing method (the method that introduced a trend component) to include a seasonal component. [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/03/07/why-you-should-not-use-holt-winters-method/">Why you should not use Holt-Winters method</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Whenever I see results of an experiment that include Holt-Winters method, I shrug. You should not use it, and here is why.</p>
<p>Holt-Winters was developed in 1960 by a student of Charles Holt, Peter Winters (<a href="https://doi.org/10.1287/mnsc.6.3.324">Winters, 1960</a>). He extended Holt&#8217;s exponential smoothing method (the method that introduced a trend component) to include a seasonal component. The method has performed well in many situations, but it was originally developed for a specific type of data: trend-seasonal. Furthermore, the original method implied that the noise had an additive form, while the seasonality was multiplicative (which is a weird combination).</p>
<p>Since then, lots of things have happened in forecasting, one of the biggest being the development of the ETS framework by <a href="https://link.springer.com/book/10.1007/978-3-540-71918-2">Hyndman et al. (2008)</a>. That framework covers 30 possible models for time series with different types of Error, Trend, and Seasonal components. Holt-Winters is a method that aligns with only one of the models from the framework.</p>
<p>Now, if you have data without a trend, we know that the trend-based models will perform poorly, overfitting the data. Similarly, with seasonal models on non-seasonal data or other misalignments between the model and the data. This is a &#8220;horses for courses&#8221; sort of thing: you should use the model that suits your data, not the one you can easily find in a default package. And this means that Holt-Winters works as intended on one out of roughly 30 possible types of time series. Sticking only to it is similar to applying SARIMA(0,2,2)(0,2,2) to all your data, and then wondering why it performed poorly.</p>
<p>Interestingly enough, data scientists that do not know forecasting very well typically compare their ML approach with ARIMA with automatic order selection (usually the one developed by <a href="https://doi.org/10.18637/jss.v027.i03">Hyndman &#038; Khandakar, 2008</a>) and explicitly with Holt-Winters instead of trying ETS. I don&#8217;t know why they do that. The only explanation I have is that they probably just are not aware of the more modern approach to exponential smoothing.</p>
<p>If you want to learn more about exponential smoothing, my book is <a href="https://openforecast.org/adam/">available online</a>, and Kandrika Pritularga and I will deliver <a href="https://isf.forecasters.org/program/workshops/">a workshop on that topic at ISF</a>.</p>
<div id="attachment_3351" style="width: 310px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-07-HW.png&amp;nocache=1"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-3351" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-07-HW-300x167.png&amp;nocache=1" alt="Example of application of Holt-Winters method on Air Passengers data" width="300" height="167" class="size-medium wp-image-3351" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-07-HW-300x167.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-07-HW-768x427.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2024/03/2024-03-07-HW.png&amp;nocache=1 900w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><p id="caption-attachment-3351" class="wp-caption-text">Example of application of Holt-Winters method on Air Passengers data</p></div>
<p><strong>Correction</strong>: Rob Hyndman correctly pointed out that &#8220;<em>the seasonal method now known as Holt-Winters was actually developed by Holt, and is described in <a href="https://doi.org/10.1016/j.ijforecast.2003.09.015">Holt (1957)</a></em>&#8220;, not as I stated above in Winters (1960). Winters (1960) was the published paper describing the method, while Holt (1957) was an unpublished report for the Logistics Branch of the Office of Naval Research (ONR).</p>
<p>Message <a href="https://openforecast.org/2024/03/07/why-you-should-not-use-holt-winters-method/">Why you should not use Holt-Winters method</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/03/07/why-you-should-not-use-holt-winters-method/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Staying Positive: Challenges and Solutions in Using Pure Multiplicative ETS Models</title>
		<link>https://openforecast.org/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/</link>
					<comments>https://openforecast.org/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Wed, 10 Jan 2024 13:34:51 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Papers]]></category>
		<category><![CDATA[Stories]]></category>
		<category><![CDATA[Univariate models]]></category>
		<category><![CDATA[ADAM]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3315</guid>

					<description><![CDATA[<p>Authors: Ivan Svetunkov, John E. Boylan Journal: IMA Journal of Management Mathematics Abstract: Exponential smoothing in state space form (ETS) is a popular forecasting technique, widely used in research and practice. While the additive error ETS models have been well studied, the multiplicative error ones have received much less attention in forecasting literature. Still, these [&#8230;]</p>
<p>Message <a href="https://openforecast.org/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/">Staying Positive: Challenges and Solutions in Using Pure Multiplicative ETS Models</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><strong>Authors</strong>: Ivan Svetunkov, <a href="/en/2023/07/21/john-e-boylan/">John E. Boylan</a></p>
<p><strong>Journal</strong>: <a href="https://academic.oup.com/imaman">IMA Journal of Management Mathematics</a></p>
<p><strong>Abstract</strong>: Exponential smoothing in state space form (ETS) is a popular forecasting technique, widely used in research and practice. While the additive error ETS models have been well studied, the multiplicative error ones have received much less attention in forecasting literature. Still, these models can be useful in cases, when one deals with positive data, because they are supposed to work in such situations. Unfortunately, the classical assumption of normality for the error term might break this property and lead to non-positive forecasts on positive data. In order to address this issue we propose using Log-Normal, Gamma and Inverse Gaussian distributions, which are defined for positive values only. We demonstrate what happens with ETS(M,*,*) models in this case, discuss conditional moments of ETS with these distribution and show that they are more natural for the models than the Normal one. We conduct the simulation experiments in order to study the bias introduced by point forecasts in these models and then compare the models with different distributions. We finish the paper with an example of application, showing how pure multiplicative ETS with a positive distribution works.</p>
<p><strong>DOI</strong>: <a href="https://doi.org/10.1093/imaman/dpad028">10.1093/imaman/dpad028</a>.</p>
<p><a href="https://openforecast.org/wp-content/uploads/2023/12/Svetunkov-Boylan-2023-Staying-positive.pdf">Working paper</a>.</p>
<h1>About the paper</h1>
<p><strong>DISCLAIMER</strong>: This is quite a technical paper focusing on solving a small problem of the ETS model that would allow using it in specific non-standard situations. It acts as a building block for the <a href="/en/2023/09/08/iets-state-space-model-for-intermittent-demand-forecasting/">iETS paper</a>. But the latter does not work without this paper, so while it seems small, it is an important brick in the wall.</p>
<p>The conventional ETS works great for regular demand, where the volume of the data is high. In that case, a forecaster can decide which of the 30 models to select for the data, not worrying too much about the assumption of normality for the error term and about forecast trajectories from the selected model. The situation changes when one needs to work with the positive low volume data. One would think that pure multiplicative ETS should work fine in that case, however, due to the normality assumption, the model might produce negative prediction intervals and in some situations even point forecasts. Trying to fix this issue, we considered several distributions for the error term in the multiplicative error ETS:</p>
<ol>
<li>\( 1 + \epsilon_t \sim \mathcal{N}\left(1, \sigma^2\right) \) &#8211; the conventional assumption of Normality;</li>
<li>\( 1 + \epsilon_t \sim \mathcal{IG}\left(1, \sigma^2\right) \) &#8211; the error term follows the Inverse Gaussian distribution with the expectation of one and the variance of \(\sigma^2\);</li>
<li>\( 1 + \epsilon_t \sim \mathrm{log}\mathcal{N} \left(-\frac{\sigma^2}{2}, \sigma^2 \right) \) &#8211; the error term follows the Log-Normal distribution with the location of \(-\frac{\sigma^2}{2}\) and the scale of \( \sigma^2 \);</li>
<li>\( 1 + \epsilon_t \sim \Gamma\left(\sigma^{-2}, \sigma^2\right) \) &#8211; the error term follows the Gamma distribution with the shape parameter \(\sigma^{-2}\) and the scale \( \sigma^2 \).</li>
</ol>
<p>The restrictions imposed on the parameters of distributions above are necessary to make sure that the expectation of the error term \(1 + \epsilon_t \) is zero. If it isn&#8217;t then the ETS model would need to be modified to cater for the non-zero mean, otherwise the model will produce incorrect forecasts.</p>
<p>In the paper, we show how ETS works with these assumptions, what forecasting trajectories it produces and how it can be estimated. We also demonstrate that the distribution selection can be easily automated using AIC. All these aspects of the model are already implemented and supported in the <code>adam()</code> function from the <code>smooth</code> package in R (read more <a href="/tag/adam/">here</a> and <a href="/category/r-en/smooth/">here</a>).</p>
<h1>Story of the paper</h1>
<p>John Boylan and I started working on this paper after getting a rejection from the IJF for the other paper of ours, &#8220;<a href="/en/2023/09/08/iets-state-space-model-for-intermittent-demand-forecasting/">iETS: State space model for intermittent demand forecasting</a>&#8220;. The rejection showed us that we need to take a completely new look at the paper, and it became apparent that the pure multiplicative ETS is not well studied in the literature. At the same time, its discussion would be outside of the scope of the original paper, so, we decided to write a separate one, focusing on the non-intermittent, but low volume demand.</p>
<p>We needed to discuss two points in the paper, which were then used in the <a href="/en/2023/09/08/iets-state-space-model-for-intermittent-demand-forecasting/">iETS</a> one:</p>
<ol>
<li>The conventional ETS assumes that the demand follows Normal distribution. In case of low volume demand this assumption may lead to negative forecasts, which makes the model inappropriate;</li>
<li>Point forecasts from multiplicative ETS models does not coincide with the conditional expectations. <a href="https://www.doi.org/10.1007/978-3-540-71918-2">Hyndman et al. (2008)</a> discuss this in their book, but not to the extent we needed. We thought that lots of people do not understand the implications of this, so we added that discussion to the paper.</li>
</ol>
<p>The paper was written over the period of 2021 &#8211; 2023 and was ready in Spring 2023. John and I discussed it several times, and we agreed to have a final look at it in May 2023 before submitting it to <a href="https://academic.oup.com/imaman">IMA Journal of Management Mathematics</a>. When I found out that <a href="/en/2023/07/21/john-e-boylan/">John was ill</a>, I decided not to wait for his comments further and just submitted it. The paper went through a couple of rounds, changed its name to reflect concerns of one of reviewers (the new name is objectively better than the old one) and was accepted for publication in November 2023. This is the last paper that John and I wrote together.</p>
<p>Message <a href="https://openforecast.org/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/">Staying Positive: Challenges and Solutions in Using Pure Multiplicative ETS Models</a> first appeared on <a href="https://openforecast.org">Open Forecasting</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
