<?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 Python - OpenForecast</title>
	<atom:link href="https://openforecast.org/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>https://openforecast.org/tag/python/</link>
	<description>How to look into the future</description>
	<lastBuildDate>Fri, 30 Oct 2026 12:29:24 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</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 Python - OpenForecast</title>
	<link>https://openforecast.org/tag/python/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>smooth in Python: iETS and the Croston&#8217;s Method</title>
		<link>https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/</link>
					<comments>https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 02 Nov 2026 09:00:52 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Intermittent demand]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[intermittent demand]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4706</guid>

					<description><![CDATA[<p>Is there a statistical model underlying Croston’s method? This question has been bothering researchers for decades. Croston proposed his method back in 1972, and some even argued that there is no sensible model behind it (Shenstone &#038; Hyndman, 2005). Yet there is one! Let me explain. But first, why bother? Because a method just gives ... <a title="smooth in Python: iETS and the Croston&#8217;s Method" class="read-more" href="https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/" aria-label="Read more about smooth in Python: iETS and the Croston&#8217;s Method">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/">smooth in Python: iETS and the Croston&#8217;s Method</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Is there a statistical model underlying Croston’s method? This question has been bothering researchers for decades. Croston proposed his method back in 1972, and some even argued that there is no sensible model behind it (<a href="https://doi.org/10.1002/for.963">Shenstone &#038; Hyndman, 2005</a>). Yet there is one! Let me explain.</p>
<p>But first, why bother? Because a method just gives you a number, while a model gives you a likelihood, and with it flexibility: you can select components and features via information criteria, get predictive distributions, and extend the model however you like. So, if you derive a model underlying a method, you bring that method to another level.</p>
<p>A quick recap. <a href="/2026/10/14/the-main-idea-behind-the-croston-s-method/">Croston split intermittent demand into two parts</a>: how often the product sells (demand intervals) and how much it sells when it does (demand sizes), and applied Simple Exponential Smoothing (SES) to each of them. <a href="/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/">In my previous post</a>, I showed the simplest model for the &#8220;how often&#8221; part, which assumes that the chance of a sale does not change over time.</p>
<p>A more complicated statistical model was proposed by John Boylan and me in <a href="/2023/09/08/iets-state-space-model-for-intermittent-demand-forecasting/">our iETS paper</a>. It assumes that the occurrence of sales follows a Bernoulli distribution with a time-varying probability. In the “inverse-odds-ratio” version of the model, we track the expected number of periods without sales: if the gap between sales is 4 periods then the probability of a sale is 1/4. Every new zero or one is converted into an error term, and this expected number is updated via ETS(M,N,N), the model underlying SES. When the probability does not change much over time, this model can be estimated with Croston’s method, which is why it can be considered as the model underlying it.</p>
<p>In Python, the basic model is done like this:</p>
<pre class="decode">from smooth import OM, ADAM

occ = OM(model="MNN", occurrence="inverse-odds-ratio")
occ.fit(y)
model = ADAM(model="MNN")
model.fit(y, occurrence=occ)
model.predict(h=10, interval="prediction")</pre>
<p>In the paper, we also showed that this model is especially suitable for new products with demand building up. This means that the gaps between sales become shorter and shorter, i.e. the demand transforms from intermittent to regular. If you expect this to continue in the holdout period, you can use the trend model, and here how to do that in Python:</p>
<pre class="decode">occ = OM(model="MMN", occurrence="inverse-odds-ratio")</pre>
<p>This way, the expected gap between sales would decline in the holdout period, and the probability of a sale would grow. The image in this post shows how this works on an artificial example.</p>
<p>And if you know what drives sales (e.g. promotions or price changes), you can include this information in the occurrence model as well.</p>
<p>So, yes, there is a model underlying Croston’s method, and it allows doing things that the method itself cannot do.</p>
<p>Try smooth for Python: <code>pip install smooth</code><br />
<a href="https://github.com/openforecast-org/smooth/wiki/OM">Occurrence model on wiki</a></p>
<p>Message <a href="https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/">smooth in Python: iETS and the Croston&#8217;s Method</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/11/02/smooth-in-python-iets-and-the-croston-s-method/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth in Python: fixed probability model for demand occurrence</title>
		<link>https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/</link>
					<comments>https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 26 Oct 2026 08:48:42 +0000</pubDate>
				<category><![CDATA[Intermittent demand]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[intermittent demand]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4695</guid>

					<description><![CDATA[<p>When demand is intermittent, one of the most useful forecasts you can produce is not &#8220;how much will we sell&#8221;, but &#8220;how likely are we to sell anything at all&#8221;. And this probability can be modelled, just like demand itself. This idea goes back to Croston, who suggested splitting intermittent demand into demand occurrence and ... <a title="smooth in Python: fixed probability model for demand occurrence" class="read-more" href="https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/" aria-label="Read more about smooth in Python: fixed probability model for demand occurrence">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/">smooth in Python: fixed probability model for demand occurrence</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>When demand is intermittent, one of the most useful forecasts you can produce is not &#8220;how much will we sell&#8221;, but &#8220;how likely are we to sell anything at all&#8221;. And this probability can be modelled, just like demand itself.</p>
<p>This idea goes <a href="/2026/10/14/the-main-idea-behind-the-croston-s-method/">back to Croston</a>, who suggested splitting intermittent demand into demand occurrence and demand sizes. The main image in the post shows a typical intermittent demand series, and the one below shows its occurrence part: &#8220;1&#8221; when someone bought the product, and &#8220;0&#8221; otherwise.</p>
<figure id="attachment_4697" aria-describedby="caption-attachment-4697" style="width: 1014px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-02.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/10/2026-10-02-smooth-posts-11-occurrence-model-02-1024x474.png&amp;nocache=1" alt="Demand occurrence part of the data" width="1024" height="474" class="size-large wp-image-4697" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-02-1024x474.png&amp;nocache=1 1024w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-02-300x139.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-02-768x356.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-02.png&amp;nocache=1 1080w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption id="caption-attachment-4697" class="wp-caption-text">Demand occurrence part of the data</figcaption></figure>
<p>We now have a series that can be modelled on its own, and there are many ways to do that, which come down to your preferences and experience: ETS, ARIMA, logistic regression, GBM, Neural Network &#8211; you name it!</p>
<p>One thing to avoid, though, is trying to predict the exact periods when sales will happen: when demand is truly intermittent, you can’t. What you can forecast is the probability. <a href="/2024/12/11/intermittent-demand-don-t-try-to-predict-when-it-will-happen/">I had a post on that topic</a>.</p>
<p>In the smooth package, there is a separate function for this, OM, which allows using ETS, ARIMA and regression elements to model demand occurrence. It supports several types of occurrence, including the odds-ratio, the inverse-odds-ratio (related to Croston&#8217;s method) and the direct one (similar to TSB). But the simplest of all is the fixed probability, which assumes that the probability of sale does not change over time. It simply takes the share of periods with sales: if the product sold in 30 out of 100 weeks, the probability is 0.3. If you read my previous post, you might notice that <a href="/2026/10/19/what-the-average-demand-interval-adi-hides/">this is essentially 1/ADI</a> turned into a forecast, with the same limitation of ignoring how demand evolves. Still, it is a good benchmark and a starting point for intermittent demand forecasting.</p>
<p>In Python, this is done like this:</p>
<pre class="decode">from smooth import OM, ADAM

model = OM(occurrence="fixed")
model.fit(y)
fc = model.predict(h=10)</pre>
<p>The resulting probability forecast is shown in the following image:</p>
<figure id="attachment_4698" aria-describedby="caption-attachment-4698" style="width: 1014px" class="wp-caption aligncenter"><a href="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03.png&amp;nocache=1"><img decoding="async" src="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03-1024x474.png&amp;nocache=1" alt="Forecast for the demand occurrence" width="1024" height="474" class="size-large wp-image-4698" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03-1024x474.png&amp;nocache=1 1024w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03-300x139.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03-768x356.png&amp;nocache=1 768w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/10/2026-10-02-smooth-posts-11-occurrence-model-03.png&amp;nocache=1 1080w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption id="caption-attachment-4698" class="wp-caption-text">Forecast for the demand occurrence</figcaption></figure>
<p>We can also use this directly in ADAM, which splits the demand into two parts on its own. Here is the model with ETS(M,N,N) for demand sizes and the fixed probability of occurrence, iETS(M,N,N)_F:</p>
<pre class="decode">model = ADAM(model="MNN", occurrence="fixed")
model.fit(y)
model.predict(h=10)</pre>
<p>The last command takes the probability from the occurrence model and multiplies it by the forecast of demand sizes. The result is the mean demand per period, just like from any other forecasting method.</p>
<p>Next time, I&#8217;ll tell you about the more advanced occurrence models, where the probability changes over time. So, stay tuned!</p>
<p>Try smooth for Python: <code>pip install smooth</code><br />
<a href="https://github.com/openforecast-org/smooth/wiki/OM">Occurrence model on wiki</a>.</p>
<p>Message <a href="https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/">smooth in Python: fixed probability model for demand occurrence</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/10/26/smooth-in-python-fixed-probability-model-for-demand-occurrence/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<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>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 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>ISF2026: PTS Taxonomy of Multiple Source of Error State Space Models for Demand Forecasting</title>
		<link>https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/</link>
					<comments>https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 06 Jul 2026 11:15:48 +0000</pubDate>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[MUSE]]></category>
		<category><![CDATA[presentations]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[R]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4164</guid>

					<description><![CDATA[<p>This time, at ISF2026, I presented the paper that I have worked on together with Juan Ramon Trapero and Diego Pedregal. The idea of the paper is to introduce a taxonomy of the models in the Multiple Sources of Error (MSOE) framework. In the Single Source of Errors one, there is ETS, in the MSOE, ... <a title="ISF2026: PTS Taxonomy of Multiple Source of Error State Space Models for Demand Forecasting" class="read-more" href="https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/" aria-label="Read more about ISF2026: PTS Taxonomy of Multiple Source of Error State Space Models for Demand Forecasting">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/">ISF2026: PTS Taxonomy of Multiple Source of Error State Space Models for Demand Forecasting</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>This time, at ISF2026, I presented the paper that I have worked on together with Juan Ramon Trapero and Diego Pedregal. The idea of the paper is to introduce a taxonomy of the models in the Multiple Sources of Error (MSOE) framework. In the Single Source of Errors one, there is ETS, in the MSOE, there is nothing. So, we have united the existing research in one taxonomy of &#8220;Power transform, Trend, and Seasonal&#8221; model &#8211; analogue of ETS, but in the MSOE world. We are now finalising the paper about this, hoping to submit to a peer reviewed journal soon.</p>
<p><strong>Abstract</strong>: State space models for time series forecasting have been dominated by the single source of error (SSOE) framework, most notably the ETS family of models. The idea of SSOE is to use the same error across all equations in the model. Multiple source of error (MSOE) models, by contrast, assign independent stochastic disturbances to each component &#8211; level, trend, and seasonality &#8211; offering a richer and more flexible representation of uncertainty, yet they lack a systematic, unifying taxonomy. This paper introduces the PTS taxonomy, a structured classification of MSOE state space models tailored to the specific properties of the MSOE setting. The taxonomy organises models along three dimensions: P (Power transform, based on the Box-Cox transformation), T (Trend, with options for none, local, global, or damped), and S (Seasonality, with options for none, discrete, or trigonometric), yielding up to 24 well-defined model variants. All models are cast within a general state space system and estimated via the Kalman filter using maximum likelihood, with model selection performed through standard information criteria. The framework also incorporates a robust outlier detection procedure covering additive outliers, level shifts, and slope changes, as well as natural handling of missing observations through the Kalman smoother. We illustrate the practical utility of the taxonomy through empirical experiments on the real life dataset, demonstrating that the PTS family is both theoretically coherent and empirically competitive with established alternatives.</p>
<p><a href="https://openforecast.org/wp-content/uploads/2026/07/2026-ISF-Svetunkov-PTS.pdf">Slides are here</a>.<br />
Package that implements PTS: <a href="https://github.com/openforecast-org/muse">muse</a></p>
<p>Message <a href="https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/">ISF2026: PTS Taxonomy of Multiple Source of Error State Space Models for Demand Forecasting</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/07/06/isf2026-pts-taxonomy-of-multiple-source-of-error-state-space-models-for-demand-forecasting/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>stick function for the EDA in time series</title>
		<link>https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/</link>
					<comments>https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 11:24:43 +0000</pubDate>
				<category><![CDATA[Applied forecasting]]></category>
		<category><![CDATA[greybox in Python]]></category>
		<category><![CDATA[Package greybox for R]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[EDA]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[time series]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4157</guid>

					<description><![CDATA[<p>You have probably seen my post about the STI classification of Hans Levenbach (this one). Well, I&#8217;ve decided to implement it, and it has landed in the greybox package for R/Python. What&#8217;s greybox? It is a package for statistical modelling focusing on forecasting and time series analysis. I created it back in 2018 to split ... <a title="stick function for the EDA in time series" class="read-more" href="https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/" aria-label="Read more about stick function for the EDA in time series">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/">stick function for the EDA in time series</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>You have probably seen my post about the STI classification of Hans Levenbach (<a href="/2026/05/18/hans-levenbach-s-classification-scheme-for-trend-seasonal-components/">this one</a>). Well, I&#8217;ve decided to implement it, and it has landed in the greybox package for R/Python.</p>
<p>What&#8217;s greybox? It is a package for statistical modelling focusing on forecasting and time series analysis. I created it back in 2018 to split the static models (such as linear regression) from the dynamic ones that landed in the smooth package. Greybox has evolved since then, and now has linear regression (alm), regression selection (stepwise) and combinations (calm), a variety of tools for feature generation, diagnostics, forecast evaluation (e.g. rolling origin) etc. You can <a href="https://github.com/config-i1/greybox/wiki">read more about it here</a>. Originally, the package was available for R only, but Claude and I ported its main functions to Python back in February.</p>
<p>The Exploratory Data Analysis techniques for time series fit the package quite well, although I don&#8217;t have many of those yet. So, I&#8217;ve implemented the main idea of the STI of Hans Levenbach in a function called &#8220;stick&#8221; (Seasonal, Trend, Irregular Contribution Kit) in the greybox package for R/Python. The idea is straightforward: apply stick to a time series, it will use ANOVA, and give you the strength of each component. Here, for example, is how to apply the function to the AirPassengers data (everyone&#8217;s favourite toy time series) in R:</p>
<pre class="decode">library(greybox)
stick(AirPassengers)</pre>
<p>and in Python:</p>
<pre class="decode">from fcompdata import AirPassengers
from greybox import stick

result = stick(AirPassengers.y, lags=12)
print(result)</pre>
<p>which gives exactly the same result:</p>
<pre>Strength of the components:
seasonal12      trend  irregular
    0.1061     0.8613     0.0326</pre>
<p>So, trend dominates the time series, explaining 86.13% of its variability, meaning that if you capture it correctly, you solve a big chunk of the problem. This split also gives you a rough idea about the structure-versus-noise breakdown in the time series, although it assumes that the seasonal component does not evolve over time.</p>
<p>The function supports several seasonal components, and I might extend it to include external information (e.g. promotions) in the future if there is demand for it.</p>
<p>Message <a href="https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/">stick function for the EDA in time series</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/06/26/stick-function-for-the-eda-in-time-series/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth in python: Non-normal distributions in ETS/ARIMA</title>
		<link>https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/</link>
					<comments>https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Wed, 27 May 2026 14:17:46 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[ADAM]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4152</guid>

					<description><![CDATA[<p>So, you know quite well that the normal distribution is one of the most popular distributions in statistics. The reasons are manifold, including convenience for the academic community and the fact that it is taught in every single statistics course in the world. But what if we don&#8217;t want to be normal? There are situations ... <a title="smooth in python: Non-normal distributions in ETS/ARIMA" class="read-more" href="https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/" aria-label="Read more about smooth in python: Non-normal distributions in ETS/ARIMA">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/">smooth in python: Non-normal distributions in ETS/ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>So, you know quite well that the normal distribution is one of the most popular distributions in statistics. The reasons are manifold, including convenience for the academic community and the fact that it is taught in every single statistics course in the world. But what if we don&#8217;t want to be normal?</p>
<p>There are situations where non-normal distributions fit considerably better. The main candidate for substitution is the conditional distribution of the response variable. For example, sales of engines cannot follow the normal distribution by definition: they are intermittent and integer-based — you cannot sell 1.78 engines. More generally, while demand can be fractional, it cannot be negative. It is therefore only logical to use distributions that support positive values only in these situation. Examples include Log-Normal, Gamma, and Inverse Gaussian, among many others.</p>
<p>In my last paper with John Boylan (<a href="/2024/01/10/staying-positive-challenges-and-solutions-in-using-pure-multiplicative-ets-models/">this one</a>), we discussed how ETS can be extended to use these three distributions instead of the normal one. I implemented this functionality (together with the others, such as Laplace and Generalised Normal) in ADAM. It supports any of these distributions with any ETS/ARIMA/regression model, for both additive and multiplicative error terms. There is some maths involved, which you can find <a href="https://openforecast.org/adam/ADAMETSAdditiveDistributions.html">here</a> and <a href="https://openforecast.org/adam/ADAMETSMultiplicativeDistributions.html">here</a>.</p>
<p>Why bother? The main is in the predictive distribution. If the data is not normal, we may end up with poorly calibrated forecasts and misleading prediction intervals. Using a more appropriate distribution can resolve this.</p>
<p>But how do we choose the right distribution for our data?</p>
<p>A possible solution (similar to selecting ETS components) is to fit models with different distributions and pick the one with the lowest information criterion. This is implemented in the ADAM function from the smooth package. We can do this manually, or use AutoADAM (called auto.adam in R) to select the most suitable distribution based on AICc automatically:</p>
<pre class="decode">from fcompdata import AirPassengers
from smooth import AutoADAM

model = AutoADAM(lags=[12], h=12, holdout=True, orders=None, verbose=True)
model.fit(AirPassengers.y)
model.summary()</pre>
<p>The <code>orders=None</code> line stops the function from trying different ARIMA orders &#8211; something we will come back to in a future post. For this example, the output is:</p>
<pre>Model estimated using ADAM() function: ETS(MAM)
Response variable: y
Distribution used in the estimation: Normal
Loss function type: likelihood; Loss function value: 523.2756
Coefficients:
       Estimate  Std. Error  Lower 2.5%  Upper 97.5%   
alpha    0.7575      0.0895      0.5807       0.9343  *
beta     0.0000      0.0080      0.0000       0.0158   
gamma    0.0000      0.0503      0.0000       0.0994   
Error standard deviation: 0.0358
Sample size: 144
Number of estimated parameters: 4
Number of degrees of freedom: 140
Information criteria:
      AIC     AICc       BIC      BICc
1054.5512 1054.839 1066.4305 1067.1455</pre>
<p>Boring&#8230; the function found that the Normal distribution has the lowest AICc among those tested &#8211; the Air Passengers data is too well-behaved.</p>
<p>Oh, and don&#8217;t forget to produce the forecasts:</p>
<pre class="decode">model.predict(h=18, interval="prediction")</pre>
<p>Smooth forecasting!</p>
<p>Install smooth: <code>pip install smooth</code></p>
<p>Message <a href="https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/">smooth in python: Non-normal distributions in ETS/ARIMA</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/05/27/smooth-in-python-non-normal-distributions-in-ets-arima/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>smooth in python: multiple seasonal ETS</title>
		<link>https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/</link>
					<comments>https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/#respond</comments>
		
		<dc:creator><![CDATA[Ivan Svetunkov]]></dc:creator>
		<pubDate>Mon, 11 May 2026 08:08:38 +0000</pubDate>
				<category><![CDATA[ETS]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[smooth for Python]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[extrapolation methods]]></category>
		<category><![CDATA[smooth]]></category>
		<guid isPermaLink="false">https://openforecast.org/?p=4140</guid>

					<description><![CDATA[<p>Another interesting case in demand forecasting is the high frequency data. For example, if you work with demand on daily level, you might notice that demand increases every Monday but also exhibits proper seasonal fluctuations (e.g. decline every Winter). What do you do in this case? One of the solutions (old but gold) is the ... <a title="smooth in python: multiple seasonal ETS" class="read-more" href="https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/" aria-label="Read more about smooth in python: multiple seasonal ETS">Read more</a></p>
<p>Message <a href="https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/">smooth in python: multiple seasonal ETS</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Another interesting case in demand forecasting is the high frequency data. For example, if you work with demand on daily level, you might notice that demand increases every Monday but also exhibits proper seasonal fluctuations (e.g. decline every Winter). What do you do in this case?</p>
<p>One of the solutions (old but gold) is the multiple seasonal ETS model, which was originally developed by James Taylor (<a href="https://doi.org/10.1057/palgrave.jors.2601589">2003</a>) for the pure additive exponential smoothing. The idea was quite simple: to model multiple seasonal cycles, one can add multiple seasonal components, i.e. to capture the day-of-week (frequency 7) and the day-of-year (frequency 365) effects. While it worked fine for some examples, the main issue with it has been its computational speed (or rather slowness): the original ETS needs to estimate all smoothing parameters + all the initial values for seasonal indices and other components. Both ADAM and ES in the smooth package support multiple seasonalities and avoid the whole issue by using a different model initialisation called &#8220;backcasting&#8221;.</p>
<p>Here is a classical example from James&#8217; paper on the half-hourly electricity demand (see the image in the post). It is clear that there is a half-hour-of-day and the day-of-week effects. In ES, this means that we need to provide the vector for the lags variable:</p>
<pre class="decode">from smooth import ES
from fcompdata import taylor

# Fit ES with automatic ETS model selection
model = ES(lags=[48, 336], h=336, holdout=True)
model.fit(taylor.y)
model.predict(h=336)
print(model)</pre>
<p>This is the output I get from the function:</p>
<pre>Time elapsed: 2.03 seconds
Model estimated using ES() function: ETS(MNM)
With backcasting initialisation
Distribution assumed in the model: Normal
Loss function type: likelihood; Loss function value: 25391.1773
Persistence vector g:
 alpha gamma1 gamma2
0.2899 0.1283 0.5270
Sample size: 3696
Number of estimated parameters: 4
Number of degrees of freedom: 3692
Information criteria:
      AIC      AICc       BIC      BICc
50790.3546 50790.3654 50815.2146 50815.2591

Forecast errors:
ME: 829.1195; MAE: 942.1447; RMSE: 1065.1127
sCE: 941.5012%; Asymmetry: 9.2%; sMAE: 3.1841%; sMSE: 0.1296%
MASE: 1.4491; RMSSE: 1.1286; rMAE: 0.1408; rRMSE: 0.1300</pre>
<p>The computational time on this data was only 2.03 second. In this time, the function tried several possible ETS models and selected the best one based on the AICc value. The resulting best model is ETS(M,N,M), which makes perfect sense for this data.</p>
<p>Is there a way to improve this model? Yes! Taylor mentions that adding AR(1) to the cocktail tends to improve the accuracy in case of multiple seasonal series. We can try that if we switch to ADAM:</p>
<pre class="decode">from smooth import ADAM

# Fit ADAM ETS(MNM)+AR(1) model
model = ADAM(model="MNM", ar_orders=1, lags=[48, 336], h=336, holdout=True)
model.fit(taylor.y)
print(model)
model.plot(7)</pre>
<p>Here is the output:</p>
<pre>Time elapsed: 1.04 seconds
Model estimated using ADAM() function: ETS(MNM)+ARIMA(1,0,0)
With backcasting initialisation
Distribution assumed in the model: Gamma
Loss function type: likelihood; Loss function value: 24157.2473
Persistence vector g:
 alpha gamma1 gamma2
0.1097 0.2225 0.3481
ARMA parameters of the model:
             Lag 1
AR(1)       0.6852
Sample size: 3696
Number of estimated parameters: 5
Number of degrees of freedom: 3691
Information criteria:
      AIC      AICc       BIC      BICc
48324.4947 48324.5109 48355.5697 48355.6365

Forecast errors:
ME: 276.4061; MAE: 462.5092; RMSE: 588.5957
sCE: 313.8711%; Asymmetry: 2.1%; sMAE: 1.5631%; sMSE: 0.0396%
MASE: 0.7114; RMSSE: 0.6237; rMAE: 0.0691; rRMSE: 0.0719</pre>
<p>The resulting model has lower AICc, but also produces more accurate point forecasts (compare RMSSE values) for the holdout set. The following image shows the data and the point forecasts for it:</p>
<figure id="attachment_4142" aria-describedby="caption-attachment-4142" 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/05/2026-04-17-smooth-posts-04-Multiple-seasonal-ETS-02.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/2026/05/2026-04-17-smooth-posts-04-Multiple-seasonal-ETS-02-300x214.png&amp;nocache=1" alt="Double seasonal ETS(M,N,M) applied to the half-hourly electricity demand data" width="300" height="214" class="size-medium wp-image-4142" srcset="https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/05/2026-04-17-smooth-posts-04-Multiple-seasonal-ETS-02-300x214.png&amp;nocache=1 300w, https://openforecast.org/wp-content/webpc-passthru.php?src=https://openforecast.org/wp-content/uploads/2026/05/2026-04-17-smooth-posts-04-Multiple-seasonal-ETS-02.png&amp;nocache=1 700w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-4142" class="wp-caption-text">Double seasonal ETS(M,N,M) applied to the half-hourly electricity demand data</figcaption></figure>
<p>What else can we do here? Actually, quite a lot: multistep losses, seasonal ARIMA, explanatory variables &#8211; things can get only more complicated from here. Have a look <a href="https://openforecast.org/adam/ADAMMultipleFrequenciesExamples.html">at this</a>.</p>
<p>Do I hear someone shouting &#8220;TBATS&#8221;? TBATS is the exponential smoothing with additional bells and whistles (ETS + adapted Fourier terms + ARMA errors). I don&#8217;t have it as a separate function in the smooth just yet, but you can reproduce it, for example, <a href="https://openforecast.org/adam/ETSXMultipleSeasonality.html">like this</a>.</p>
<p>So, what are you waiting for? Dive in and see how it works for yourself!</p>
<p>Install smooth: pip install smooth</p>
<p>Message <a href="https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/">smooth in python: multiple seasonal ETS</a> first appeared on <a href="https://openforecast.org">OpenForecast</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://openforecast.org/2026/05/11/smooth-in-python-multiple-seasonal-ets/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
