-
Notifications
You must be signed in to change notification settings - Fork 8
EDA
stick(y, lags=frequency(y), ...)
# Methods on the returned "stick" object
print(x, digits=4, ...)
plot(x, which=NULL, ask, ...)def stick(y, lags=None) -> StickResult: ...
# Methods on the returned StickResult object
StickResult.__str__(self) -> str # print(result)
StickResult.plot(self, which=None, axes=None, **kwargs)Before building a forecasting model it helps to understand what is in
a time series: how much of its variation comes from seasonality, how
much from a trend, and how much is left over as irregular noise.
The stick() function — the Seasonality, Trend, and
Irregular Contribution Kit — answers exactly this question.
stick() implements the STI classification of Hans Levenbach. It
fits an Analysis of Variance (ANOVA) of the series on seasonal and trend
factors and reports the share of the total variation (sum of squares)
explained by each component. The three shares sum to one, so they can be
read directly as proportions.
# R — function is in the greybox namespace
library(greybox)# Python
from greybox import stick- A data frame is built internally with the series
yin the first column, a categorical (factor) variable for each seasonal lag, and a trend ("year") factor in the last column.- For monthly data (
lags=12) the seasonal factor cycles through1, 2, …, 12(the month of the year), and the trend factor takes values1, 2, …, ceil(T/12), each repeated 12 times (the year). - The trend factor is built from the longest lag provided.
- For monthly data (
- An ANOVA is fitted:
y ~ seasonal + trend. - The strength of each component is its sum of squares divided by the total sum of squares. The residual sum of squares is the irregular component.
| Parameter | R | Python | Type | Default | Description |
|---|---|---|---|---|---|
| y | y |
y |
ts / array-like |
— | The time series to analyse |
| lags | lags |
lags |
numeric / int or list |
frequency(y) (R) / required (Python) |
Seasonal periodicity/-ies, e.g. 12 for monthly, c(24, 168) for hourly |
| Field | R | Python | Type | Description |
|---|---|---|---|---|
| y | $y |
.y |
ts / np.ndarray
|
The original series |
| lags | $lags |
.lags |
vector / np.ndarray
|
Seasonal lags used (unique, ascending) |
| anova | $anova |
.anova |
data.frame / pd.DataFrame
|
ANOVA table (Df, Sum Sq, Mean Sq, F value, Pr(>F)) |
| strength | $strength |
.strength |
named vector / pd.Series
|
Share of variation per component; sums to 1 |
The classic monthly airline-passengers series (Jan 1949 – Dec 1960, 144 observations) has both a strong upward trend and a pronounced yearly seasonal pattern — an ideal showcase.
AirPassengers ships with base R, so no extra data package is needed:
library(greybox)
result <- stick(AirPassengers, lags=12)
print(result)
#> Seasonality, Trend, and Irregular Contribution Kit
#> Seasonal lags: 12
#>
#> Strength of the components:
#> seasonal12 trend irregular
#> 0.1061 0.8613 0.0326
# Visualise the components (seasonal plot + trend plot)
plot(result)In Python the AirPassengers series is available from the
fcompdata package
(pip install fcompdata); .y returns the full 144-point series:
import fcompdata
from greybox import stick
y = fcompdata.AirPassengers.y # full 144-point monthly series
result = stick(y, lags=12)
print(result)
# Seasonality, Trend, and Irregular Contribution Kit
# Seasonal lags: 12
#
# Strength of the components:
# seasonal12 0.1061
# trend 0.8613
# irregular 0.0326
# Visualise the components (seasonal plot + trend plot)
result.plot()| Component | Strength | Interpretation |
|---|---|---|
trend |
0.8613 | ~86% of the series' total variation is explained by the year-to-year level change — the airline traffic grows steadily over the 12 years. This is by far the dominant signal. |
seasonal12 |
0.1061 | ~11% comes from the repeating within-year (monthly) pattern — the summer peaks and winter troughs. |
irregular |
0.0326 | Only ~3% is left unexplained (the residual / noise). A small irregular share means the series is highly structured and very predictable. |
The three shares add up to 1 (0.8613 + 0.1061 + 0.0326 = 1.000), so they
can be read directly as percentages of the total variation. The ANOVA
table behind these numbers is available via result$anova (R) /
result.anova (Python), including the F-statistics and p-values for each
factor.
A practical reading: because the trend dominates, a forecasting
model for AirPassengers must capture the growth first; seasonality is a
meaningful secondary effect, and there is very little irregular variation
to worry about.
For high-frequency data with more than one cycle, pass several lags. The trend is then formed from the longest one:
# R — hourly data with daily (24) and weekly (168) seasonality
result <- stick(y, lags=c(24, 168))# Python
result = stick(y, lags=[24, 168])Each seasonal lag receives its own strength entry
(seasonal24, seasonal168), alongside trend and irregular.
| R Function | Python Function |
|---|---|
stick() |
stick() |
print(x) |
print(result) / str(result)
|
plot(x, which=...) |
result.plot(which=...) |
- Levenbach, H. (2021). Four P's in a Pod: e-Commerce Forecasting and Planning for Supply Chain Practitioners. Independently published. ISBN 979-8461733575.
- Svetunkov, I. (2023). Statistics for Business Analytics. https://openforecast.org/sba/