Seasonality, Trend, and Irregular Contribution Kit
The greybox.stick module decomposes the variance of a time series
into seasonal, trend and irregular parts based on an Analysis of
Variance (ANOVA) of the series on the seasonal and trend factors, and
measures the contribution of each component. It is a Python port of R’s
greybox::stick() and implements the Seasonality, Trend, and
Irregular (STI) classification of Hans Levenbach.
stick()
- greybox.stick.stick(y, lags=None) StickResult[source]
Seasonality, Trend, and Irregular Contribution Kit.
Decomposes the variance of a time series into seasonal, trend and irregular parts based on an Analysis of Variance (ANOVA) of the series on the seasonal and trend factors, and measures the contribution of each component. This is a Python port of R’s
greybox::stick().A data frame is formed internally with the response
y, a categorical (factor) variable for each of the provided seasonallagsand a “trend” factor. For a monthly series withlags=12, the seasonal factor takes values1..12repeated throughout the sample (the month of the year), while the trend factor takes values1..ceil(T/12), each repeated 12 times (the year). The trend factor is constructed based on the longest of the provided lags. An ANOVA is then applied toy ~ seasonal + trendand the strength of each component is measured as the share of the respective Sum of Squares in the total Sum of Squares. The irregular component corresponds to the share of the residual Sum of Squares; the strengths sum up to one.The function implements the Seasonality, Trend, and Irregular (STI) classification of Hans Levenbach.
- Parameters:
y (array-like) – The time series to analyse.
lags (int or sequence of int) – The seasonal lags (periodicities) in the data, e.g.
lags=[24, 168]for hourly data orlags=12for monthly data. Values not greater than one are dropped.
- Returns:
Object with the original data, the
lagsused, the ANOVA table and thestrengthof each component.- Return type:
References
Levenbach, H. (2021). Four P’s in a Pod: e-Commerce Forecasting and Planning for Supply Chain Practitioners. Independently published. ISBN 979-8461733575.
Examples
>>> import numpy as np >>> t = np.arange(1, 121) >>> y = 100 + 0.3 * t + 20 * np.sin(2 * np.pi * t / 12) >>> result = stick(y, lags=12) >>> bool(np.isclose(result.strength.sum(), 1.0)) True
Example:
import numpy as np
from greybox import stick
# Monthly series with a trend and a seasonal pattern
t = np.arange(1, 121)
y = 100 + 0.3 * t + 20 * np.sin(2 * np.pi * t / 12)
result = stick(y, lags=12)
print(result)
# Seasonality, Trend, and Irregular Contribution Kit
# Seasonal lags: 12
#
# Strength of the components:
# seasonal12 ...
# trend ...
# irregular ...
# Multiple seasonal lags (e.g. hourly data)
result2 = stick(y_hourly, lags=[24, 168])
Accessing the result
The StickResult exposes the strength of each
component as a pandas.Series and the full ANOVA table as a
pandas.DataFrame:
result.strength # one entry per lag, plus trend & irregular
result.strength["trend"] # strength of the trend
result.anova # Df / Sum Sq / Mean Sq / F value / Pr(>F)
The strengths are the shares of the respective Sum of Squares in the total Sum of Squares and sum up to one.
Plotting
StickResult.plot() mirrors R’s plot.stick(). For every seasonal
lag it draws a seasonal plot (the series reshaped into one grey line
per cycle, with the average seasonal profile overlaid as a bold dashed
black line); the final plot is the trend plot (one grey line per
seasonal position drawn across the cycles, with the average level per
cycle – the trend – overlaid as a bold dashed black line). The
which argument selects which panes to draw: 1, ..., k-1 are the
seasonal plots, k is the trend plot; None (the default) draws
all of them:
import matplotlib.pyplot as plt
axes = result.plot() # all panes
ax = result.plot(which=2) # only the trend plot
plt.show()
Result class
- class greybox.stick.StickResult(y: ndarray, lags: ndarray, anova: DataFrame, strength: Series)[source]
Bases:
objectResult of
stick(). Mirrors R’sstickS3 class.- y
The original time series.
- Type:
numpy.ndarray
- lags
The seasonal lags used in the analysis (unique, ascending).
- Type:
numpy.ndarray
- anova
The ANOVA table, with one row per seasonal lag, a
trendrow (when a trend was fitted) and aResidualsrow. Columns areDf,Sum Sq,Mean Sq,F valueandPr(>F).- Type:
pandas.DataFrame
- strength
The strength of each component: one entry per seasonal lag, the
trendand theirregularcomponent. The values are the shares of the respective Sum of Squares in the total Sum of Squares and sum up to one.- Type:
pandas.Series
- plot(which=None, axes=None, **kwargs)[source]
Plot the seasonal and trend components.
Mirrors R’s
plot.stick(). For every seasonal lag a “seasonal plot” is produced (the series reshaped into one grey line per cycle, with the average seasonal profile overlaid as a bold dashed black line); the final plot is the “trend plot” (one grey line per seasonal position drawn across the cycles, with the average level per cycle – the trend – overlaid as a bold dashed black line).- Parameters:
which (int or sequence of int, optional) – Which plots to produce:
1, ..., k-1correspond to the seasonal plots for each of the seasonal lags (in ascending order), whilekcorresponds to the trend plot (the last one). IfNone(the default), all the plots are produced. Values outside the valid range are dropped with a warning.axes (matplotlib.axes.Axes or sequence of Axes, optional) – Axes to draw on, one per requested plot. A new figure with a row of subplots is created if
None.**kwargs – Forwarded to
matplotlib.axes.Axes.plot()for the grey component lines.
- Returns:
The axes containing the plots.
- Return type:
numpy.ndarray of matplotlib.axes.Axes