<?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 ARIMA - OpenForecast</title>
	<atom:link href="https://openforecast.org/tag/arima-en/feed/" rel="self" type="application/rss+xml" />
	<link>https://openforecast.org/tag/arima-en/</link>
	<description>How to look into the future</description>
	<lastBuildDate>Sat, 26 Sep 2026 09:05:08 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/07/cropped-of-logo-short-1-32x32.png&amp;nocache=1</url>
	<title>Archives ARIMA - OpenForecast</title>
	<link>https://openforecast.org/tag/arima-en/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>smooth for Python: Automatic order selection for MSARIMA</title>
		<link>https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/</link>
					<comments>https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 28 Sep 2026 08:00:04 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth]]></category>
		<category><![CDATA[theory]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4654</guid>

					<description><![CDATA[<p>Have you ever tried to identify ARIMA for your data? The old school Box-Jenkins methodology implies analysing ACF/PACF plots and selecting the orders iteratively. This approach has a fundamental flaw I discussed in one of my earlier posts. Brute force does not help either: even p ≤ 3, d ≤ 2, q ≤ 3 gives ... <a title="smooth for Python: Automatic order selection for MSARIMA" class="read-more" href="https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/" aria-label="Read more about smooth for Python: Automatic order selection for MSARIMA">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/">smooth for Python: Automatic order selection for MSARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Have you ever tried to identify ARIMA for your data? The old school Box-Jenkins methodology implies analysing ACF/PACF plots and selecting the orders iteratively. This approach has a fundamental flaw I discussed in one of <a href="/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/">my earlier posts</a>. Brute force does not help either: even p ≤ 3, d ≤ 2, q ≤ 3 gives 48 candidates, and seasonal lags push it into the thousands. So, smart people came up with great heuristics that allow identifying the orders in a short period of time. Let me explain.</p>
<p>One of the widely used approaches was developed by <a href="https://doi.org/10.18637/jss.v027.i03">Hyndman &#038; Khandakar (2008)</a>. It is implemented in auto.arima in R (and in StatsForecast for Python). It mixes two frameworks: stationarity tests (KPSS or ADF) to select the order of differencing d, and then information criteria for p and q. The tests are needed because, in the conventional formulation, models with different d are estimated on different sample sizes, so their information criteria cannot be compared (I explained <a href="/2026/09/12/on-differencing-of-arima-in-state-space/">this here</a>). Although the algorithm works well and showed good performance in several competitions, it mixes hypothesis testing with information theory. The former tests the differences between samples, but assumes that you can make a mistake (based on your significance level), while the latter simply selects the most plausible model given data.</p>
<p>AutoMSARIMA does the selection differently. Because it is formulated in state space, the sample stays the same for any d, so the algorithm can compare all orders directly via information criteria, including the differencing. This follows the approach of <a href="https://doi.org/10.1080/00207543.2019.1600764">Svetunkov &#038; Boylan (2020)</a>, which treats order selection as a component selection problem rather than hypothesis testing. No ADF test, no KPSS test, no pre-screening for stationarity. The IC decides everything. And because nothing in this logic is tied to a specific seasonal period, it extends naturally to multiple seasonalities: each seasonal lag simply adds its own components to the model, and the same criterion decides which of them to keep, with the seasonality remaining stochastic rather than fixed.</p>
<p>The search itself is stepwise: the algorithm inspects the ACF/PACF of the residuals to propose a candidate order, adds it, and keeps it only if the information criterion decreases. When done, it also tries several simple SARIMA models to check whether any of them beats the selected one. The algorithm is explained <a href="/adam/ARIMASelection.html">in detail here</a>.</p>
<p>In Python:</p>
<pre class="decode">from fcompdata import AirPassengers
from smooth import AutoMSARIMA

# Monthly data, automatic order selection up to SARIMA(3,2,3)(3,1,3)[12]
model = AutoMSARIMA(lags=[1, 12])
model.fit(AirPassengers.y)
print(model)</pre>
<p>The same algorithm works for the multiple seasonal ARIMA. The only thing to specify explicitly is the maximum orders to check:</p>
<pre class="decode">from fcompdata import taylor

model = AutoMSARIMA(
lags=[1, 48, 336],
orders={"ar": [3, 2, 2],
"i": [2, 1, 1],
"ma": [3, 2, 2]}
)
model.fit(taylor.y)</pre>
<p>The selected model returns the same attributes and supports the same methods as ADAM.</p>
<p>Does it actually work? I benchmarked AutoMSARIMA against statsforecast AutoARIMA, skforecast and R&#8217;s auto.arima on 5,315 series from the M1, M3 and Tourism datasets. The honest summary: point accuracy is a tie &#8211; RMSSE differs in the third decimal place across all implementations. But smooth produces the best-calibrated prediction intervals of the pack and is 3–5 times faster than the alternatives. <a href="https://github.com/openforecast-org/smooth/blob/master/python/tests/notebooks/2026-09-21-smooth-1-0-8-benchmark_auto_arima.ipynb">Here is the full notebook</a>.</p>
<p>Try smooth now: <code>pip install smooth</code><br />
<a href="https://github.com/openforecast-org/smooth/wiki">smooth wiki</a>.</p>
<p>Message <a href="https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/">smooth for Python: Automatic order selection for MSARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/09/28/smooth-for-python-automatic-order-selection-for-msarima/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth for Python: ETS+ARIMA</title>
		<link>https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/</link>
					<comments>https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 09:05:42 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4621</guid>

					<description><![CDATA[<p>ETS is powerful. ARIMA is powerful. But when you need both, you are typically left with fitting ARMA to the residuals of ETS, which biases the parameters. There is a much neater solution: combine them in one state space model to estimate everything jointly&#8230; The standard practice when ETS is not enough to capture the ... <a title="smooth for Python: ETS+ARIMA" class="read-more" href="https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/" aria-label="Read more about smooth for Python: ETS+ARIMA">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/">smooth for Python: ETS+ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a href="/2026/04/22/smooth-in-python-ets-with-model-selection/">ETS is powerful</a>. ARIMA is powerful. But when you need both, you are typically left with fitting ARMA to the residuals of ETS, which biases the parameters. There is a much neater solution: combine them in one state space model to estimate everything jointly&#8230;</p>
<p>The standard practice when ETS is not enough to capture the dynamics of real demand is to either switch to ARIMA or apply it to the residuals of the model. But it is possible to join the two models in one by using the Single Source of Error (SSOE) state space framework and to estimate the parameters jointly. This reduces the bias in the parameter estimates, letting the two models work as a team rather than one cleaning up after the other.</p>
<p>In the SSOE form, ETS and ARIMA states are stacked one after another in a large vector. For example, an ETS(A,N,A)+AR(2) model has four states: level, seasonal, and two AR components — each updated independently but responding to the same shock. Structurally, this resembles fitting ETS and then modelling its residuals with ARIMA, but with a critical difference: the parameters are estimated jointly, removing the bias that accumulates from sequential fitting.</p>
<p>But this flexibility comes with constraints. Some ETS and ARIMA combinations are actually redundant — they describe the same process from two angles, producing infinitely many parameter combinations with identical fit. For example, ETS(A,N,N) and ARIMA(0,1,1) are equivalent models, and combining them creates an unidentifiable model with no stable solution. So, I have come up with a few practical guidelines, which you can read about in <a href="/adam/ETSAndARIMA.html">Section 9.4 of ADAM</a>.</p>
<p>Practically speaking, this is all handled under the hood of the ADAM function in Python, so you don&#8217;t need to worry about it. Here is an example:</p>
<pre class="decode">from fcompdata import AirPassengers
from smooth import ADAM

# ETS(A,A,N) + SARIMA(0,0,0)(1,1,1)[12]
model = ADAM(
    model="AAN",
    ar_order=[0, 1],
    i_order=[0, 1],
    ma_order=[0, 1],
    lags=[1, 12],
    h=12, holdout=True
)
model.fit(AirPassengers.y)</pre>
<p>Note how the orders are specified: per lag, with the first element referring to lag 1 and the second to lag 12. The code above fits ETS(A,A,N) + SARIMA(0,0,0)(1,1,1)[12], delegating the seasonality to the ARIMA part. The advantage of doing this via ADAM is that the result can be compared with pure ETS or ARIMA directly using information criteria. And if you don&#8217;t want to choose the orders yourself, AutoADAM will select them for you.</p>
<p>But do you even need the full combination? <a href="https://doi.org/10.1002/for.3980040103">Gardner (1985)</a> and <a href="https://doi.org/10.1016/j.ejor.2009.10.003">Taylor (2010)</a> argued that adding AR(1) tends to improve the accuracy of ETS. So, maybe you can get away with the simple ETS+AR(1) model, which is easy to apply using ADAM.</p>
<p><a href="/adam/ARIMAandETS.html">When to use ETS vs ARIMA</a>.<br />
<a href="/adam/ETSAndARIMA.html">How to combine them</a>.</p>
<p>Try it yourself: <code>pip install smooth</code></p>
<p>Message <a href="https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/">smooth for Python: ETS+ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/09/17/smooth-for-python-ets-arima/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>On differencing of ARIMA in state space</title>
		<link>https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/</link>
					<comments>https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Sat, 12 Sep 2026 11:25:01 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[theory]]></category>
		<category><![CDATA[time series]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4641</guid>

					<description><![CDATA[<p>A reader left a comment under the MSARIMA post on LinkedIn saying that in order to compare ARIMAs via information criteria, we need to make sure that the candidate models have the same order of differencing. They are right — for the conventional ARIMA. BUT! In the state space formulation, the problem disappears. Here is ... <a title="On differencing of ARIMA in state space" class="read-more" href="https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/" aria-label="Read more about On differencing of ARIMA in state space">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/">On differencing of ARIMA in state space</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A reader left a comment under the <a href="https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/">MSARIMA post</a> on LinkedIn saying that in order to compare ARIMAs via information criteria, we need to make sure that the candidate models have the same order of differencing. They are right — for the conventional ARIMA. BUT! In the state space formulation, the problem disappears. Here is why.</p>
<p>In the conventional ARIMA, taking differences is treated as a pre-processing step, where we switch from, for example, the sales of a product to the sales increase/decrease. If you had a sample of 100 observations, after first differences you are left with 99 &#8211; the first observation has nothing to be subtracted from. So yes, with the conventional approach, ARIMA(1,0,1) and ARIMA(1,1,1) are estimated on different sample sizes, and their information criteria are not directly comparable.</p>
<p>But! MSARIMA (and ADAM in general) is a state space model, and this pre-processing step is not needed. The differences are embedded in the model as additional components: ARIMA(0,1,1) has one, ARIMA(0,2,1) has two. The sample stays the same; what changes is the number of estimated parameters &#8211; each new component needs its initial value, and the information criterion penalises that. So the comparison stays fair: same sample, same likelihood basis, different number of parameters. I have <a href="/adam/StateSpaceARIMA.html#ADAMARIMAExamplesModels">several examples of how ARIMA is formulated in the ADAM SSOE framework</a>.</p>
<p>What this implies: any ARIMA model of any orders (non-seasonal, seasonal, or multiseasonal) can be compared with other ARIMA models directly via information criteria, on the same sample of data.</p>
<p>This is not a new idea, by the way. I used this approach in the ARIMA algorithm I developed for the DemandWorks company (now part of Netstock) more than ten years ago, and John Boylan and I later built the State Space ARIMA on the same principle (<a href="https://doi.org/10.1080/00207543.2019.1600764">the paper</a>).</p>
<p>So, yes: if you use the conventional ARIMA (auto_arima from pmdarima, ARIMA from aeon or statsforecast in Python; stats or forecast implementations in R), you NEED to make sure that you compare models with the same order of differencing. But if you use the smooth implementation, you don&#8217;t need to worry &#8211; everything is already taken care of for you. This comes as a bonus from the specific formulation of the model.</p>
<p>Message <a href="https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/">On differencing of ARIMA in state space</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/09/12/on-differencing-of-arima-in-state-space/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth in python: Multiple Seasonal ARIMA</title>
		<link>https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/</link>
					<comments>https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Thu, 10 Sep 2026 08:01:44 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[Seasonality]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4632</guid>

					<description><![CDATA[<p>ARIMA has been a workhorse for decades. But the standard implementations quietly hard-code assumptions: one seasonal cycle, Gaussian errors, regressors handled separately etc. This is fine for textbook well-behaved data. But what do you do when you face real data? The conventional implementations (statsmodels or pmdarima in Python, stats in R) do their job well: ... <a title="smooth in python: Multiple Seasonal ARIMA" class="read-more" href="https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/" aria-label="Read more about smooth in python: Multiple Seasonal ARIMA">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/">smooth in python: Multiple Seasonal ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>ARIMA has been a workhorse for decades. But the standard implementations quietly hard-code assumptions: one seasonal cycle, Gaussian errors, regressors handled separately etc. This is fine for textbook well-behaved data. But what do you do when you face real data?</p>
<p>The conventional implementations (statsmodels or pmdarima in Python, stats in R) do their job well: they estimate SARIMA via likelihood, select orders automatically, and produce sensible forecasts for series with trend and one seasonality. Monthly or quarterly demand data is their home ground. The problems start when the data has more than one cycle: the classical SARIMA formulation has a slot for exactly one seasonal lag, so for the half-hourly electricity demand you would have to pick either the hour-of-day or the day-of-week cycle and discard the other.</p>
<p>MSARIMA (Multiple Seasonal ARIMA) solves this by reformulating ARIMA in the Single Source of Error (SSOE) state space form. Each AR, I, and MA element becomes a state in the model, so nothing restricts you to one seasonal lag: you can have as many as you want. In the smooth package, orders are specified per lag, matched to a lags list. Here is an example on the classical taylor series (half-hourly electricity demand in England and Wales), with two seasonal cycles:</p>
<p><code>from fcompdata import taylor<br />
from smooth import MSARIMA</p>
<p># MSARIMA(3,0,1)(0,1,1)[48](0,1,1)[336]
model = MSARIMA(<br />
    orders={"ar": [3, 0, 0], "i": [0, 1, 1], "ma": [1, 1, 1]},<br />
    lags=[1, 48, 336],<br />
    h=336, holdout=True<br />
)<br />
model.fit(taylor.y)<br />
model.predict(h=336, interval="prediction", level=0.95)</code></p>
<p>In Python, it takes only 1.5 seconds for the function to fit the model to the data and estimate its parameters, which is fast for a double-seasonal model fit to roughly four thousand observations:</p>
<figure id="attachment_4634" aria-describedby="caption-attachment-4634" style="width: 290px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/09/2026-04-30-smooth-posts-07-msarima.png&amp;nocache=1"><img fetchpriority="high" decoding="async" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/09/2026-04-30-smooth-posts-07-msarima-300x207.png&amp;nocache=1" alt="Python/R output of the double seasonal ARIMA" width="300" height="207" class="size-medium wp-image-4634" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/09/2026-04-30-smooth-posts-07-msarima-300x207.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/09/2026-04-30-smooth-posts-07-msarima.png&amp;nocache=1 632w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-4634" class="wp-caption-text">Python/R output of the double seasonal ARIMA</figcaption></figure>
<p>But the smooth implementation brings several additional practical benefits.</p>
<p>First, model.fit() accepts a matrix of external regressors via xreg, estimated jointly with the ARIMA components &#8211; no separate pre-filtering step. The logic and the code are identical to ETSX, <a href="/2026/05/05/smooth-in-python-ets-with-explanatory-variables/">which I covered earlier</a>.</p>
<p>Second, you can swap the loss: loss=&#8221;GTMSE&#8221; or &#8220;MSEh&#8221; optimises the model directly on multistep errors, with the same shrinkage mechanism I discussed in <a href="/2026/08/31/smooth-in-python-multistep-losses/">the post on multistep losses</a>.</p>
<p>Third, you don&#8217;t need to stick with the Gaussian distribution &#8211; you can choose other ones if you think that, for example, Laplace is more suitable for the data (<a href="/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/">this post</a>).</p>
<p>And there is one more thing. Because MSARIMA now lives in the same state space framework as ETS, the two models can be combined into one and compared with each other directly via information criteria. But that deserves a post of its own, so stay tuned.</p>
<p>Read <a href="/adam/ADAMARIMA.html">Chapter 9 on ADAM ARIMA</a>.<br />
Or check out the documentation in <a href="https://github.com/openforecast-org/smooth/wiki/MSARIMA">smooth wiki</a>.</p>
<p>An why not try it yourself? <code>pip install smooth</code></p>
<p>Message <a href="https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/">smooth in python: Multiple Seasonal ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/09/10/smooth-in-python-multiple-seasonal-arima/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth in python: Multistep losses</title>
		<link>https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/</link>
					<comments>https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 31 Aug 2026 08:47:20 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[Loss functions]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4605</guid>

					<description><![CDATA[<p>Why train a forecasting model on one-step-ahead errors when you care about 10-step-ahead accuracy? This is the core motivation behind multistep losses in dynamic models. This has connection with the so-called &#8220;direct forecasting strategy&#8221;. And here what it is and how to work with it in Python. Conventional maximum likelihood estimation minimises one-step-ahead errors. It ... <a title="smooth in python: Multistep losses" class="read-more" href="https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/" aria-label="Read more about smooth in python: Multistep losses">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/">smooth in python: Multistep losses</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Why train a forecasting model on one-step-ahead errors when you care about 10-step-ahead accuracy? This is the core motivation behind multistep losses in dynamic models. This has connection with the so-called &#8220;direct forecasting strategy&#8221;. And here what it is and how to work with it in Python.</p>
<p>Conventional maximum likelihood estimation minimises one-step-ahead errors. It works well in many standard situations and produces quite robust models. But in practice, you are rarely interested in just the next observation. Supply chains operate on lead times. Budgets are planned quarterly. The model you trained on one-step-ahed forecast is not the model that minimises your actual decision-relevant error.</p>
<p><strong>Multistep losses address this directly</strong>. Instead of minimising \(\mathrm{MSE}_1\), they minimise errors computed \(h\) steps ahead from every in-sample point. The key theoretical result (<a href="/2023/08/09/multi-step-estimators-and-shrinkage-effect-in-time-series-models/">Svetunkov et al., 2023</a>) is that this implies *shrinkage* of smoothing parameters towards zero — the model becomes less stochastic, less reactive to noise, and more stable across longer horizons. Shrinkage strength grows with \(h\) and weakens as sample size increases.</p>
<p>ADAM supports several multistep losses, each with a different trade-off:</p>
<ul>
<li><strong>MSEh</strong> — minimises the \(h\)-step-ahead variance only; strongest shrinkage, simplest interpretation;</li>
<li><strong>TMSE</strong> — sums \(\mathrm{MSE}_j\) for \(j=1,&#8230;,h\), i.e. sum of the MSEs between 1 and h steps ahead; balances all horizons but is dominated by longer-horizon errors;</li>
<li><strong>GTMSE</strong> — takes the log of each \(\mathrm{MSE}_j\) before summing; equalises the influence of short and long horizons, milder shrinkage;</li>
<li><strong>MSCE</strong> — minimises cumulative forecast error; directly relevant for inventory decisions with lead time \(h\);</li>
<li><strong>GPL</strong> — the full General Predictive Likelihood; accounts for the entire covariance structure of multistep errors and encompasses all the above.</li>
</ul>
<p>All of these are accessible in the Python <code>smooth</code> package with a single parameter change. Here is an example of the code with GTMSE:</p>
<pre class="decode">from fcompdata import AirPassengers
from smooth import ADAM

model = ADAM(model="AAA", lags=12, loss="GTMSE", h=12)
model.fit(AirPassengers.y)
model.predict(h=10)</pre>
<p>The <code>h</code> parameter sets the horizon over which multistep errors are evaluated during estimation. This allows connecting the loss with the specific decision horizon better. The image in the post shows the ETS model fit and forecasts, when estimated with several different losses, including the conventional one.</p>
<p>One practical note: on small samples, MSEh and MSCE can produce noticeably biased parameter estimates (closer to zero) due to strong shrinkage. GTMSE tends to be a safer default for small samples.</p>
<p>Read more about these and other losses in the <a href="/adam/multistepLosses.html">ADAM monograph</a> or in the <a href="https://github.com/openforecast-org/smooth/wiki">wiki of the package</a>.</p>
<p>Message <a href="https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/">smooth in python: Multistep losses</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/08/31/smooth-in-python-multistep-losses/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 ... <a title="smooth v4.4.0" class="read-more" href="https://openforecast.org/2026/02/09/smooth-v4-4-0/" aria-label="Read more about smooth v4.4.0">Read more</a></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">OpenForecast</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>
<figure id="attachment_4002" aria-describedby="caption-attachment-4002" style="width: 290px" 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 decoding="async" 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><figcaption id="caption-attachment-4002" class="wp-caption-text">Mean computational time vs mean RMSSE</figcaption></figure>
<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">OpenForecast</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>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 ... <a title="smooth v4.3.0 in R: what&#8217;s new and what&#8217;s next?" class="read-more" href="https://openforecast.org/2025/07/04/smooth-v4-3-0-in-r-what-s-new-and-what-s-next/" aria-label="Read more about smooth v4.3.0 in R: what&#8217;s new and what&#8217;s next?">Read more</a></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">OpenForecast</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>
<figure id="attachment_3908" aria-describedby="caption-attachment-3908" style="width: 290px" 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" 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><figcaption id="caption-attachment-3908" class="wp-caption-text">Nemenyi test for the smooth functions</figcaption></figure>
<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">OpenForecast</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>Fundamental Flaw of the Box-Jenkins Methodology</title>
		<link>https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/</link>
					<comments>https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Tue, 13 May 2025 11:57:07 +0000</pubDate>
				<category><![CDATA[ARIMA]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[Univariate models]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[Information criteria]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=3838</guid>

					<description><![CDATA[<p>If you have taken a course on forecasting or time series analysis, you’ve probably heard of ARIMA and the Box–Jenkins methodology. In my opinion, this methodology has a fundamental flaw and should not be used in practice. Here&#8217;s why. When Box and Jenkins wrote their book back in the 1960s, it was a very different ... <a title="Fundamental Flaw of the Box-Jenkins Methodology" class="read-more" href="https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/" aria-label="Read more about Fundamental Flaw of the Box-Jenkins Methodology">Read more</a></p>
<p>Message <a href="https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/">Fundamental Flaw of the Box-Jenkins Methodology</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>If you have taken a course on forecasting or time series analysis, you’ve probably heard of ARIMA and the Box–Jenkins methodology. In my opinion, <strong>this methodology has a fundamental flaw</strong> and should not be used in practice. Here&#8217;s why.</p>
<p>When Box and Jenkins wrote their book back in the 1960s, it was a very different era: computers were massive, and people worked with punch cards. To make their approach viable, Box and Jenkins developed a methodology for selecting the appropriate orders of AR and MA based on the values of the autocorrelation and partial autocorrelation functions (ACF and PACF, respectively). Their idea was that if an ARMA process generates a specific ACF/PACF pattern, then it could be identified by analysing those functions in the data. At the time, it wasn’t feasible to do cross-validation or rolling origin evaluation, and even using information criteria for model selection was a challenge. So, the Box–Jenkins approach was a sensible option, producing adequate results with limited computational resources, and was considered state of the art.</p>
<p>Unfortunately, as the M1 competition later showed (see my <a href="/2024/03/14/the-role-of-m-competitions-in-forecasting/">earlier post</a>), the methodology didn’t work well in practice. Simpler methods that didn’t rely on rigorous model selection actually performed better. But in fact, the winning model in the competition was ARARMA by Emanuel Parzen (https://doi.org/10.1002/for.3980010108). His idea was to make the series stationary by applying a low-order, non-stationary AR to the data, then extract residuals and select appropriate ARMA orders using AIC. Parzen ignored the Box–Jenkins methodology entirely &#8211; he didn’t analyse ACF or PACF and instead relied fully on automated selection. And it worked!</p>
<p>So why didn’t the Box–Jenkins methodology perform as expected? In my monograph <a href="/adam">Forecasting and Analytics with ADAM</a>, I use the following example to explain the main issue: “All birds have wings. Sarah has wings. Thus, Sarah is a bird.” But Sarah, as shown in the image attached to this post, is a butterfly.</p>
<p>The fundamental issue with the Box–Jenkins methodology lies in its logic: if a process generates a specific ACF/PACF, that doesn’t mean that an observed ACF/PACF must come from that process. Many ARMA and even non-ARMA processes can generate exactly the same autocorrelation structure.</p>
<p>Further developments in ARIMA modelling have shown that ACF and PACF can only be used as general guidelines for order selection. To assess model performance properly, we need other tools. All modern approaches rely on information criteria for ARIMA order selection, and they consistently perform well in forecasting competitions. For example, <a href="https://doi.org/10.18637/jss.v027.i03">Hyndman &#038; Khandakar (2008)</a> use AIC for ARMA order selection, while <a href="https://doi.org/10.1080/00207543.2019.1600764">Svetunkov &#038; Boylan (2020)</a> apply AIC after reformulating ARIMA in a state space form. The former is implemented in the forecast package in R and the StatsForecast library in Python (thanks to Nixtla and Azul Garza); the latter is available in the smooth package in R. I also discuss another ARIMA order selection approach in <a href="/adam/ARIMASelection.html">Section 15.2 of my book</a>.</p>
<p>Long story short: don’t use the Box–Jenkins methodology for order selection. Use more modern tools, such as information criteria.</p>
<p>P.S. <a href="/2024/03/21/what-s-wrong-with-arima/">See also my early post on ARIMA</a>, discussing what is wrong with it.</p>
<p>Message <a href="https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/">Fundamental Flaw of the Box-Jenkins Methodology</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2025/05/13/fundamental-flaw-of-the-box-jenkins-methodology/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[Loss functions]]></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 ... <a title="Multistep loss functions: Geometric Trace MSE" class="read-more" href="https://openforecast.org/2024/06/04/multistep-loss-functions-geometric-trace-mse/" aria-label="Read more about Multistep loss functions: Geometric Trace MSE">Read more</a></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">OpenForecast</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>
<figure id="attachment_3595" aria-describedby="caption-attachment-3595" style="width: 290px" 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 loading="lazy" decoding="async" 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="auto, (max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-3595" class="wp-caption-text">ETS(A,A,A) estimated using different loss functions applied to the data with multiplicative seasonality</figcaption></figure>
<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">OpenForecast</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 ... <a title="Detecting patterns in white noise" class="read-more" href="https://openforecast.org/2024/04/10/detecting-patterns-in-white-noise/" aria-label="Read more about Detecting patterns in white noise">Read more</a></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">OpenForecast</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">OpenForecast</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>
	</channel>
</rss>
