Skip to content

Test the seasonality of a given Time-Series Dataset🔗

Introduction🔗

Summary

As stated by Rob Hyndman and George Athanasopoulos

In describing these time series, we have used words such as "trend" and "seasonal" which need to be defined more carefully.

Trend A trend exists when there is a long-term increase or decrease in the data. It does not have to be linear. Sometimes we will refer to a trend as "changing direction", when it might go from an increasing trend to a decreasing trend.

Seasonal A seasonal pattern occurs when a time series is affected by seasonal factors such as the time of the year or the day of the week. Seasonality is always of a fixed and known frequency.

Cyclic A cycle occurs when the data exhibit rises and falls that are not of a fixed frequency. These fluctuations are usually due to economic conditions, and are often related to the "business cycle". The duration of these fluctuations is usually at least 2 years.


For more info, see: Forecasting: Principles and Practice - Time Series Patterns

Information Details
Module ts_stat_tests.seasonality.tests
Algorithms qs, ocsb, ch, seasonal_strength, trend_strength, spikiness
Complexity \(O(n \log n)\) to \(O(n^2)\) depending on algorithm
Implementation statsmodels, pmdarima, tsfeatures

Source Library

We leverage several industry-standard libraries for the underlying statistical tests in this module.

  1. pmdarima: Provides the implementation for the Canova-Hansen (ch) and Osborn-Chui-Smith-Birchenhall (ocsb) tests through its nsdiffs and estimator classes.
  2. tsfeatures: Used as the basis for calculating seasonal strength, trend strength, and spikiness, following the approach popularized by the R feasts and tsfeatures packages.
  3. seastests: An R package that served as the primary reference and inspiration for our Python implementation of the Quenouille-Sarle (qs) test.

Source Module

The source code for the seasonality tests is organized into two primary layers:

Modules🔗

ts_stat_tests.seasonality.tests 🔗

Summary

This module contains functions to assess the seasonality of time series data.

The implemented algorithms include:

  • QS Test
  • OCSB Test
  • CH Test
  • Seasonal Strength
  • Trend Strength
  • Spikiness

Each function is designed to analyze a univariate time series and return relevant statistics or indicators of seasonality. This module provides both a dispatcher for flexible algorithm selection and a boolean check for easy integration into pipelines.

seasonality 🔗

seasonality(
    x: ArrayLike,
    algorithm: str = "qs",
    **kwargs: Union[float, int, str, bool, ArrayLike, None]
) -> Union[
    float, int, tuple[Union[float, int, ARIMA, None], ...]
]

Summary

Dispatcher for seasonality algorithms. This function provides a unified interface to call various seasonality tests.

Details

The seasonality function acts as a centralized dispatcher for the various seasonality algorithms implemented in the algorithms.seasonality module. It allows users to easily switch between different tests by specifying the algorithm name.

The supported algorithms include:

  • "qs": The QS (Quenouille-Sarle) test for seasonality.
  • "ocsb": The Osborn-Chui-Smith-Birchenhall test for seasonal differencing.
  • "ch": The Canova-Hansen test for seasonal stability.
  • "seasonal_strength" (or "ss"): The STL-based seasonal strength measure.
  • "trend_strength" (or "ts"): The STL-based trend strength measure.
  • "spikiness": The STL-based spikiness measure.

Parameters:

Name Type Description Default
x ArrayLike

The data to be checked.

required
algorithm str

Which seasonality algorithm to use.
Default: "qs"

'qs'
kwargs Union[float, int, str, bool, ArrayLike, None]

Additional arguments to pass to the underlying algorithm.

{}

Returns:

Type Description
Union[float, int, tuple[Union[float, int, object, None], ...]]

The result of the seasonality test. The return type depends on the chosen algorithm: - "qs" returns a tuple (statistic, pvalue). - "ocsb" and "ch" return an integer (0 or 1). - "seasonal_strength", "trend_strength", and "spikiness" return a float.

Examples
Basic usage
1
2
3
4
5
6
7
8
9
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.tests import seasonality
>>> data = load_airline().values
>>> # Using the default QS test
>>> seasonality(x=data, freq=12)
(194.469289..., 5.909223...-43)
>>> # Using seasonal strength
>>> seasonality(x=data, algorithm="ss", m=12)
0.778721...
Source code in src/ts_stat_tests/seasonality/tests.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@typechecked
def seasonality(
    x: ArrayLike,
    algorithm: str = "qs",
    **kwargs: Union[float, int, str, bool, ArrayLike, None],
) -> Union[float, int, tuple[Union[float, int, ARIMA, None], ...]]:
    """
    !!! note "Summary"
        Dispatcher for seasonality algorithms. This function provides a unified interface to call various seasonality tests.

    ???+ abstract "Details"

        The `seasonality` function acts as a centralized dispatcher for the various seasonality algorithms implemented in the `algorithms.seasonality` module. It allows users to easily switch between different tests by specifying the `algorithm` name.

        The supported algorithms include:

        - `"qs"`: The QS (Quenouille-Sarle) test for seasonality.
        - `"ocsb"`: The Osborn-Chui-Smith-Birchenhall test for seasonal differencing.
        - `"ch"`: The Canova-Hansen test for seasonal stability.
        - `"seasonal_strength"` (or `"ss"`): The STL-based seasonal strength measure.
        - `"trend_strength"` (or `"ts"`): The STL-based trend strength measure.
        - `"spikiness"`: The STL-based spikiness measure.

    Params:
        x (ArrayLike):
            The data to be checked.
        algorithm (str, optional):
            Which seasonality algorithm to use.<br>
            Default: `"qs"`
        kwargs (Union[float, int, str, bool, ArrayLike, None]):
            Additional arguments to pass to the underlying algorithm.

    Returns:
        (Union[float, int, tuple[Union[float, int, object, None], ...]]):
            The result of the seasonality test. The return type depends on the chosen algorithm:
            - `"qs"` returns a tuple `(statistic, pvalue)`.
            - `"ocsb"` and `"ch"` return an integer (0 or 1).
            - `"seasonal_strength"`, `"trend_strength"`, and `"spikiness"` return a float.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.tests import seasonality
        >>> data = load_airline().values
        >>> # Using the default QS test
        >>> seasonality(x=data, freq=12)
        (194.469289..., 5.909223...-43)
        >>> # Using seasonal strength
        >>> seasonality(x=data, algorithm="ss", m=12)
        0.778721...

        ```
    """
    options: dict[str, tuple[str, ...]] = {
        "qs": ("qs",),
        "ocsb": ("ocsb",),
        "ch": ("ch",),
        "seasonal_strength": ("seasonal_strength", "ss"),
        "trend_strength": ("trend_strength", "ts"),
        "spikiness": ("spikiness",),
    }

    # Internal helper to handle kwargs casting for ty
    def _call(
        func: Callable[..., Any],
        **args: Any,
    ) -> Any:
        """
        !!! note "Summary"
            Internal helper to call the test function.

        Params:
            func (Callable[..., Any]):
                The function to call.
            args (Any):
                The arguments to pass.

        Returns:
            (Any):
                The result.

        ???+ example "Examples"

            ```python
            # Internal helper.
            ```
        """
        return func(**args)

    if algorithm in options["qs"]:
        return _call(_qs, x=x, **kwargs)
    if algorithm in options["ocsb"]:
        return _call(_ocsb, x=x, **kwargs)
    if algorithm in options["ch"]:
        return _call(_ch, x=x, **kwargs)
    if algorithm in options["seasonal_strength"]:
        return _call(_seasonal_strength, x=x, **kwargs)
    if algorithm in options["trend_strength"]:
        return _call(_trend_strength, x=x, **kwargs)
    if algorithm in options["spikiness"]:
        return _call(_spikiness, x=x, **kwargs)

    raise ValueError(
        generate_error_message(
            parameter_name="algorithm",
            value_parsed=algorithm,
            options={k: str(v) for k, v in options.items()},
        )
    )

is_seasonal 🔗

is_seasonal(
    x: ArrayLike,
    algorithm: str = "qs",
    alpha: float = 0.05,
    **kwargs: Union[float, int, str, bool, ArrayLike, None]
) -> dict[str, Union[str, float, bool, None]]

Summary

Boolean check for seasonality. This function wraps the seasonality dispatcher and returns a standardized dictionary indicating whether the series is seasonal based on a significance level or threshold.

Details

The is_seasonal function interprets the results of the underlying seasonality tests to provide a boolean "result".

  • For "qs", the test is considered seasonal if the p-value is less than alpha.
  • For "ocsb" and "ch", the test is considered seasonal if the returned integer is 1.
  • For "seasonal_strength", the test is considered seasonal if the strength is greater than 0.64 (a common threshold in literature).
  • For others, it checks if the statistic is greater than 0.

Parameters:

Name Type Description Default
x ArrayLike

The data to be checked.

required
algorithm str

Which seasonality algorithm to use.
Default: "qs"

'qs'
alpha float

The significance level for the test (used by "qs").
Default: 0.05

0.05
kwargs Union[float, int, str, bool, ArrayLike, None]

Additional arguments to pass to the underlying algorithm.

{}

Returns:

Type Description
dict[str, Union[str, float, bool, None]]

A dictionary containing:

  • "result" (bool): Indicator if the series is seasonal.
  • "statistic" (float): The test statistic (or strength).
  • "pvalue" (float, optional): The p-value of the test (if available).
  • "alpha" (float): The significance level used.
  • "algorithm" (str): The algorithm used.
Examples
Standard check
1
2
3
4
5
6
7
8
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.tests import is_seasonal
>>> data = load_airline().values
>>> res = is_seasonal(x=data, algorithm="qs", freq=12)
>>> res["result"]
True
>>> res["algorithm"]
'qs'
Source code in src/ts_stat_tests/seasonality/tests.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
@typechecked
def is_seasonal(
    x: ArrayLike,
    algorithm: str = "qs",
    alpha: float = 0.05,
    **kwargs: Union[float, int, str, bool, ArrayLike, None],
) -> dict[str, Union[str, float, bool, None]]:
    """
    !!! note "Summary"
        Boolean check for seasonality. This function wraps the `seasonality` dispatcher and returns a standardized dictionary indicating whether the series is seasonal based on a significance level or threshold.

    ???+ abstract "Details"

        The `is_seasonal` function interprets the results of the underlying seasonality tests to provide a boolean `"result"`.

        - For `"qs"`, the test is considered seasonal if the p-value is less than `alpha`.
        - For `"ocsb"` and `"ch"`, the test is considered seasonal if the returned integer is 1.
        - For `"seasonal_strength"`, the test is considered seasonal if the strength is greater than 0.64 (a common threshold in literature).
        - For others, it checks if the statistic is greater than 0.

    Params:
        x (ArrayLike):
            The data to be checked.
        algorithm (str, optional):
            Which seasonality algorithm to use.<br>
            Default: `"qs"`
        alpha (float, optional):
            The significance level for the test (used by `"qs"`).<br>
            Default: `0.05`
        kwargs (Union[float, int, str, bool, ArrayLike, None]):
            Additional arguments to pass to the underlying algorithm.

    Returns:
        (dict[str, Union[str, float, bool, None]]):
            A dictionary containing:

            - `"result"` (bool): Indicator if the series is seasonal.
            - `"statistic"` (float): The test statistic (or strength).
            - `"pvalue"` (float, optional): The p-value of the test (if available).
            - `"alpha"` (float): The significance level used.
            - `"algorithm"` (str): The algorithm used.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Standard check"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.tests import is_seasonal
        >>> data = load_airline().values
        >>> res = is_seasonal(x=data, algorithm="qs", freq=12)
        >>> res["result"]
        True
        >>> res["algorithm"]
        'qs'

        ```
    """
    res: Any = seasonality(x=x, algorithm=algorithm, **kwargs)

    is_sea: bool = False
    stat: float = 0.0
    pval: Optional[float] = None

    if algorithm in ("qs",):
        if isinstance(res, (tuple, list)):
            v0: Any = res[0]
            v1: Any = res[1]
            stat = float(v0) if isinstance(v0, (int, float)) else 0.0
            pval = float(v1) if isinstance(v1, (int, float)) else 1.0
            is_sea = bool(pval < alpha)
    elif algorithm in ("ocsb", "ch"):
        if isinstance(res, (tuple, list)):
            v0: Any = res[0]
            stat = float(v0) if isinstance(v0, (int, float)) else 0.0
        else:
            v_any: Any = res
            stat = float(v_any) if isinstance(res, (int, float)) else 0.0
        is_sea = bool(stat == 1)
    elif algorithm in ("seasonal_strength", "ss"):
        if isinstance(res, (tuple, list)):
            v0: Any = res[0]
            stat = float(v0) if isinstance(v0, (int, float)) else 0.0
        else:
            v_any: Any = res
            stat = float(v_any) if isinstance(res, (int, float)) else 0.0
        # Default threshold of 0.64 is often used for seasonal strength
        is_sea = bool(stat > 0.64)
    else:
        if isinstance(res, (tuple, list)):
            v0: Any = res[0]
            stat = float(v0) if isinstance(v0, (int, float)) else 0.0
        else:
            v_any: Any = res
            stat = float(v_any) if isinstance(res, (int, float)) else 0.0
        is_sea = bool(stat > 0)

    return {
        "result": is_sea,
        "statistic": stat,
        "pvalue": pval,
        "alpha": alpha,
        "algorithm": algorithm,
    }

ts_stat_tests.seasonality.algorithms 🔗

Summary

Seasonality tests are statistical tests used to determine whether a time series exhibits seasonal patterns or cycles. Seasonality refers to the regular and predictable fluctuations in a time series that occur at specific intervals, such as daily, weekly, monthly, or yearly.

Seasonality tests help identify whether a time series has a seasonal component that needs to be accounted for in forecasting models. By detecting seasonality, analysts can choose appropriate models that capture these patterns and improve the accuracy of their forecasts.

Common seasonality tests include the QS test, OCSB test, Canova-Hansen test, and others. These tests analyze the autocorrelation structure of the time series data to identify significant seasonal patterns.

Overall, seasonality tests are essential tools in time series analysis and forecasting, as they help identify and account for seasonal patterns that can significantly impact the accuracy of predictions.

qs 🔗

qs(
    x: ArrayLike,
    freq: int = 0,
    diff: bool = True,
    residuals: bool = False,
    autoarima: bool = True,
) -> Union[
    tuple[float, float],
    tuple[float, float, Optional[ARIMA]],
]

Summary

The \(QS\) test, also known as the Ljung-Box test, is a statistical test used to determine whether there is any seasonality present in a time series forecasting model. It is based on the autocorrelation function (ACF) of the residuals, which is a measure of how correlated the residuals are at different lags.

Details

If residuals=False the autoarima settings are ignored.

If residuals=True, a non-seasonal ARIMA model is estimated for the time series. And the residuals of the fitted model are used as input to the test statistic. If an automatic order selection is used, the Hyndman-Khandakar algorithm is employed with: \(\max(p)=\max(q)<=3\).

The null hypothesis is that there is no correlation in the residuals beyond the specified lags, indicating no seasonality. The alternative hypothesis is that there is significant correlation, indicating seasonality.

Here are the steps for performing the \(QS\) test:

  1. Fit a time series model to your data, such as an ARIMA or SARIMA model.
  2. Calculate the residuals, which are the differences between the observed values and the predicted values from the model.
  3. Calculate the ACF of the residuals.
  4. Calculate the Q statistic, which is the sum of the squared values of the autocorrelations at different lags, up to a specified lag. Using the formula above.
  5. Compare the Q statistic to the critical value from the chi-squared distribution with degrees of freedom equal to the number of lags. If the Q statistic is greater than the critical value, then the null hypothesis is rejected, indicating that there is evidence of seasonality in the residuals.

In summary, the \(QS\) test is a useful tool for determining whether a time series forecasting model has adequately accounted for seasonality in the data. By detecting any seasonality present in the residuals, it helps to ensure that the model is capturing all the important patterns in the data and making accurate predictions.

This function will implement the Python version of the R function qs() from the seastests library.

Parameters:

Name Type Description Default
x ArrayLike

The univariate time series data to test.

required
freq int

The frequency of the time series data.
Default: 0

0
diff bool

Whether or not to run np.diff() over the data.
Default: True

True
residuals bool

Whether or not to run & return the residuals from the function.
Default: False

False
autoarima bool

Whether or not to run the AutoARIMA() algorithm over the data.
Default: True

True

Raises:

Type Description
AttributeError

If x is empty, or freq is too low for the data to be adequately tested.

ValueError

If, after differencing the data (by using np.diff()), any of the values are None (or Null or np.nan), then it cannot be used for QS Testing.

Returns:

Type Description
Union[tuple[float, float], tuple[float, float, Optional[ARIMA]]]

The results of the QS test. - stat (float): The \(\text{QS}\) score for the given data set. - pval (float): The p-value of the given test. Calculated using the survival function of the chi-squared algorithm (also defined as \(1-\text{cdf(...)}\)). For more info, see: scipy.stats.chi2 - model (Optional[ARIMA]): The ARIMA model used in the calculation of this test. Returned if residuals is True.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import qs
>>> data = load_airline().values
>>> qs(data, freq=12)
(194.469289..., 5.909223...)
Advanced usage
1
2
3
4
5
6
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import qs
>>> data = load_airline().values
>>> qs(data, freq=12, diff=True, residuals=True, autoarima=True)
The differences of the residuals of a non-seasonal ARIMA model are computed and used. It may be better to either only take the differences or use the residuals.
(101.8592..., 7.6126..., ARIMA(order=(1, 1, 1), scoring_args={}, suppress_warnings=True))
Calculation

The \(Q\) statistic is given by:

\[ QS = (n \times (n+2)) \times \sum_{k=1}^{h} \frac{r_k^2}{n-k} \]

where:

  • \(n\) is the sample size,
  • \(r_k\) is the autocorrelation at lag \(k\), and
  • \(h\) is the maximum lag to be considered.
QS = n(n+2) * sum(r_k^2 / (n-k)) for k = 1 to h
Credit
References
  1. Hyndman, R. J. and Y. Khandakar (2008). Automatic Time Series Forecasting: The forecast Package for R. Journal of Statistical Software 27 (3), 1-22.
  2. Maravall, A. (2011). Seasonality Tests and Automatic Model Identification in TRAMO-SEATS. Bank of Spain.
  3. Ollech, D. and Webel, K. (2020). A random forest-based approach to identifying the most informative seasonality tests. Deutsche Bundesbank's Discussion Paper series 55/2020.
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
@typechecked
def qs(
    x: ArrayLike,
    freq: int = 0,
    diff: bool = True,
    residuals: bool = False,
    autoarima: bool = True,
) -> Union[tuple[float, float], tuple[float, float, Optional[ARIMA]]]:
    r"""
    !!! note "Summary"
        The $QS$ test, also known as the Ljung-Box test, is a statistical test used to determine whether there is any seasonality present in a time series forecasting model. It is based on the autocorrelation function (ACF) of the residuals, which is a measure of how correlated the residuals are at different lags.

    ???+ abstract "Details"

        If `residuals=False` the `autoarima` settings are ignored.

        If `residuals=True`, a non-seasonal ARIMA model is estimated for the time series. And the residuals of the fitted model are used as input to the test statistic. If an automatic order selection is used, the Hyndman-Khandakar algorithm is employed with: $\max(p)=\max(q)<=3$.

        The null hypothesis is that there is no correlation in the residuals beyond the specified lags, indicating no seasonality. The alternative hypothesis is that there is significant correlation, indicating seasonality.

        Here are the steps for performing the $QS$ test:

        1. Fit a time series model to your data, such as an ARIMA or SARIMA model.
        1. Calculate the residuals, which are the differences between the observed values and the predicted values from the model.
        1. Calculate the ACF of the residuals.
        1. Calculate the Q statistic, which is the sum of the squared values of the autocorrelations at different lags, up to a specified lag. Using the formula above.
        1. Compare the Q statistic to the critical value from the chi-squared distribution with degrees of freedom equal to the number of lags. If the Q statistic is greater than the critical value, then the null hypothesis is rejected, indicating that there is evidence of seasonality in the residuals.

        In summary, the $QS$ test is a useful tool for determining whether a time series forecasting model has adequately accounted for seasonality in the data. By detecting any seasonality present in the residuals, it helps to ensure that the model is capturing all the important patterns in the data and making accurate predictions.

        This function will implement the Python version of the R function [`qs()`](https://rdrr.io/cran/seastests/man/qs.html) from the [`seastests`](https://cran.r-project.org/web/packages/seastests/index.html) library.

    Params:
        x (ArrayLike):
            The univariate time series data to test.
        freq (int, optional):
            The frequency of the time series data.<br>
            Default: `0`
        diff (bool, optional):
            Whether or not to run `np.diff()` over the data.<br>
            Default: `True`
        residuals (bool, optional):
            Whether or not to run & return the residuals from the function.<br>
            Default: `False`
        autoarima (bool, optional):
            Whether or not to run the `AutoARIMA()` algorithm over the data.<br>
            Default: `True`

    Raises:
        (AttributeError):
            If `x` is empty, or `freq` is too low for the data to be adequately tested.
        (ValueError):
            If, after differencing the data (by using `np.diff()`), any of the values are `None` (or `Null` or `np.nan`), then it cannot be used for QS Testing.

    Returns:
        (Union[tuple[float, float], tuple[float, float, Optional[ARIMA]]]):
            The results of the QS test.
            - stat (float): The $\text{QS}$ score for the given data set.
            - pval (float): The p-value of the given test. Calculated using the survival function of the chi-squared algorithm (also defined as $1-\text{cdf(...)}$). For more info, see: [scipy.stats.chi2](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.chi2.html)
            - model (Optional[ARIMA]): The ARIMA model used in the calculation of this test. Returned if `residuals` is `True`.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import qs
        >>> data = load_airline().values
        >>> qs(data, freq=12)
        (194.469289..., 5.909223...)

        ```

        ```pycon {.py .python linenums="1" title="Advanced usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import qs
        >>> data = load_airline().values
        >>> qs(data, freq=12, diff=True, residuals=True, autoarima=True)
        The differences of the residuals of a non-seasonal ARIMA model are computed and used. It may be better to either only take the differences or use the residuals.
        (101.8592..., 7.6126..., ARIMA(order=(1, 1, 1), scoring_args={}, suppress_warnings=True))

        ```

    ??? equation "Calculation"

        The $Q$ statistic is given by:

        $$
        QS = (n \times (n+2)) \times \sum_{k=1}^{h} \frac{r_k^2}{n-k}
        $$

        where:

        - $n$ is the sample size,
        - $r_k$ is the autocorrelation at lag $k$, and
        - $h$ is the maximum lag to be considered.

        ```
        QS = n(n+2) * sum(r_k^2 / (n-k)) for k = 1 to h
        ```

    ??? success "Credit"
        - All credit goes to the [`seastests`](https://cran.r-project.org/web/packages/seastests/index.html) library.

    ??? question "References"
        1. Hyndman, R. J. and Y. Khandakar (2008). Automatic Time Series Forecasting: The forecast Package for R. Journal of Statistical Software 27 (3), 1-22.
        1. Maravall, A. (2011). Seasonality Tests and Automatic Model Identification in TRAMO-SEATS. Bank of Spain.
        1. Ollech, D. and Webel, K. (2020). A random forest-based approach to identifying the most informative seasonality tests. Deutsche Bundesbank's Discussion Paper series 55/2020.

    ??? tip "See Also"
        - [github/seastests/qs.R](https://github.com/cran/seastests/blob/master/R/qs.R)
        - [rdrr/seastests/qs](https://rdrr.io/cran/seastests/man/qs.html)
        - [rdocumentation/seastests/qs](https://www.rdocumentation.org/packages/seastests/versions/0.15.4/topics/qs)
        - [Machine Learning Mastery/How to Identify and Remove Seasonality from Time Series Data with Python](https://machinelearningmastery.com/time-series-seasonality-with-python)
        - [StackOverflow/Simple tests for seasonality in Python](https://stackoverflow.com/questions/62754218/simple-tests-for-seasonality-in-python)
    """

    _x: NDArray[np.float64] = np.asarray(x, dtype=float)
    if np.isnan(_x).all():
        raise AttributeError("All observations are NaN.")
    if diff and residuals:
        print(
            "The differences of the residuals of a non-seasonal ARIMA model are computed and used. "
            "It may be better to either only take the differences or use the residuals."
        )
    if freq < 2:
        raise AttributeError(f"The number of observations per cycle is '{freq}', which is too small.")

    model: Optional[ARIMA] = None

    if residuals:
        if autoarima:
            max_order: int = 1 if freq < 8 else 3
            allow_drift: bool = True if freq < 8 else False
            try:
                model = auto_arima(
                    y=_x,
                    max_P=1,
                    max_Q=1,
                    max_p=3,
                    max_q=3,
                    seasonal=False,
                    stepwise=False,
                    max_order=max_order,
                    allow_drift=allow_drift,
                )
            except (ValueError, RuntimeError, IndexError):
                try:
                    model = ARIMA(order=(0, 1, 1)).fit(y=_x)
                except (ValueError, RuntimeError, IndexError):
                    print("Could not estimate any ARIMA model, original data series is used.")
            if model is not None:
                _x = model.resid()
        else:
            try:
                model = ARIMA(order=(0, 1, 1)).fit(y=_x)
            except (ValueError, RuntimeError, IndexError):
                print("Could not estimate any ARIMA model, original data series is used.")
            if model is not None:
                _x = model.resid()

    # Do diff
    y: NDArray[np.float64] = np.diff(_x) if diff else _x

    # Pre-check
    if np.nanvar(y[~np.isnan(y)]) == 0:
        raise ValueError(
            "The Series is a constant (possibly after transformations). QS-Test cannot be computed on constants."
        )

    # Test Statistic
    acf_output: NDArray[np.float64] = _acf(x=y, nlags=freq * 2, missing="drop")
    rho_output: NDArray[np.float64] = acf_output[[freq, freq * 2]]
    rho: NDArray[np.float64] = np.array([0, 0]) if np.any(np.array(rho_output) <= 0) else rho_output
    N: int = len(y[~np.isnan(y)])
    QS: float = float(N * (N + 2) * (rho[0] ** 2 / (N - freq) + rho[1] ** 2 / (N - freq * 2)))
    Pval: float = float(chi2.sf(QS, 2))

    if residuals:
        return QS, Pval, model
    return QS, Pval

ocsb 🔗

ocsb(
    x: ArrayLike,
    m: int,
    lag_method: str = "aic",
    max_lag: int = 3,
) -> int

Summary

Compute the Osborn, Chui, Smith, and Birchenhall (\(OCSB\)) test for an input time series to determine whether it needs seasonal differencing. The regression equation may include lags of the dependent variable. When lag_method="fixed", the lag order is fixed to max_lag; otherwise, max_lag is the maximum number of lags considered in a lag selection procedure that minimizes the lag_method criterion, which can be "aic", "bic" or corrected AIC "aicc".

Details

The \(OCSB\) test is a statistical test that is used to check the presence of seasonality in time series data. Seasonality refers to a pattern in the data that repeats itself at regular intervals.

The \(OCSB\) test is based on the null hypothesis that there is no seasonality in the time series data. If the p-value of the test is less than the significance level (usually \(0.05\)), then the null hypothesis is rejected, and it is concluded that there is seasonality in the data.

The \(OCSB\) test involves dividing the data into two halves and calculating the mean of each half. Then, the differences between the means of each pair of halves are calculated for each possible pair of halves. Finally, the mean of these differences is calculated, and a test statistic is computed.

The \(OCSB\) test is useful for testing seasonality in time series data because it can detect seasonal patterns that are not obvious in the original data. It is also a useful diagnostic tool for determining the appropriate seasonal differencing parameter in ARIMA models.

Critical values for the test are based on simulations, which have been smoothed over to produce critical values for all seasonal periods

The null hypothesis of the \(OCSB\) test is that there is no seasonality in the time series, and the alternative hypothesis is that there is seasonality. The test statistic is compared to a critical value from a chi-squared distribution with degrees of freedom equal to the number of possible pairs of halves. If the test statistic is larger than the critical value, then the null hypothesis is rejected, and it is concluded that there is evidence of seasonality in the time series.

Parameters:

Name Type Description Default
x ArrayLike

The time series vector.

required
m int

The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the OCSB test to work, m must exceed 1.

required
lag_method str

The lag method to use. One of ("fixed", "aic", "bic", "aicc"). The metric for assessing model performance after fitting a linear model.
Default: "aic"

'aic'
max_lag int

The maximum lag order to be considered by lag_method.
Default: 3

3

Returns:

Type Description
int

The seasonal differencing term. For different values of m, the OCSB statistic is compared to an estimated critical value, and returns 1 if the computed statistic is greater than the critical value, or 0 if not.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import ocsb
>>> data = load_airline().values
>>> ocsb(x=data, m=12)
1
Calculation

The equation for the \(OCSB\) test statistic for a time series of length n is:

\[ OCSB = \frac{1}{(n-1)} \times \sum \left( \left( x[i] - x \left[ \frac{n}{2+i} \right] \right) - \left( x \left[ \frac{n}{2+i} \right] - x \left[ \frac{i+n}{2+1} \right] \right) \right) ^2 \]

where:

  • \(n\) is the sample size, and
  • \(x[i]\) is the \(i\)-th observation in the time series.
OCSB = (1 / (n - 1)) * sum( ((x[i] - x[n/2+i]) - (x[n/2+i] - x[i+n/2+1]))^2 )

In this equation, the time series is split into two halves, and the difference between the means of each half is calculated for each possible pair of halves. The sum of the squared differences is then divided by the length of the time series minus one to obtain the \(OCSB\) test statistic.

Credit
References
  • Osborn DR, Chui APL, Smith J, and Birchenhall CR (1988) "Seasonality and the order of integration for consumption", Oxford Bulletin of Economics and Statistics 50(4):361-377.
  • R's forecast::OCSB test source code: https://bit.ly/2QYQHno
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
@typechecked
def ocsb(x: ArrayLike, m: int, lag_method: str = "aic", max_lag: int = 3) -> int:
    r"""
    !!! note "Summary"
        Compute the Osborn, Chui, Smith, and Birchenhall ($OCSB$) test for an input time series to determine whether it needs seasonal differencing. The regression equation may include lags of the dependent variable. When `lag_method="fixed"`, the lag order is fixed to `max_lag`; otherwise, `max_lag` is the maximum number of lags considered in a lag selection procedure that minimizes the `lag_method` criterion, which can be `"aic"`, `"bic"` or corrected AIC `"aicc"`.

    ???+ abstract "Details"

        The $OCSB$ test is a statistical test that is used to check the presence of seasonality in time series data. Seasonality refers to a pattern in the data that repeats itself at regular intervals.

        The $OCSB$ test is based on the null hypothesis that there is no seasonality in the time series data. If the p-value of the test is less than the significance level (usually $0.05$), then the null hypothesis is rejected, and it is concluded that there is seasonality in the data.

        The $OCSB$ test involves dividing the data into two halves and calculating the mean of each half. Then, the differences between the means of each pair of halves are calculated for each possible pair of halves. Finally, the mean of these differences is calculated, and a test statistic is computed.

        The $OCSB$ test is useful for testing seasonality in time series data because it can detect seasonal patterns that are not obvious in the original data. It is also a useful diagnostic tool for determining the appropriate seasonal differencing parameter in ARIMA models.

        Critical values for the test are based on simulations, which have been smoothed over to produce critical values for all seasonal periods

        The null hypothesis of the $OCSB$ test is that there is no seasonality in the time series, and the alternative hypothesis is that there is seasonality. The test statistic is compared to a critical value from a chi-squared distribution with degrees of freedom equal to the number of possible pairs of halves. If the test statistic is larger than the critical value, then the null hypothesis is rejected, and it is concluded that there is evidence of seasonality in the time series.

    Params:
        x (ArrayLike):
            The time series vector.
        m (int):
            The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the OCSB test to work, `m` must exceed `1`.
        lag_method (str, optional):
            The lag method to use. One of (`"fixed"`, `"aic"`, `"bic"`, `"aicc"`). The metric for assessing model performance after fitting a linear model.<br>
            Default: `"aic"`
        max_lag (int, optional):
            The maximum lag order to be considered by `lag_method`.<br>
            Default: `3`

    Returns:
        (int):
            The seasonal differencing term. For different values of `m`, the OCSB statistic is compared to an estimated critical value, and returns 1 if the computed statistic is greater than the critical value, or 0 if not.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import ocsb
        >>> data = load_airline().values
        >>> ocsb(x=data, m=12)
        1

        ```

    ??? equation "Calculation"

        The equation for the $OCSB$ test statistic for a time series of length n is:

        $$
        OCSB = \frac{1}{(n-1)} \times \sum \left( \left( x[i] - x \left[ \frac{n}{2+i} \right] \right) - \left( x \left[ \frac{n}{2+i} \right] - x \left[ \frac{i+n}{2+1} \right] \right) \right) ^2
        $$

        where:

        - $n$ is the sample size, and
        - $x[i]$ is the $i$-th observation in the time series.

        ```
        OCSB = (1 / (n - 1)) * sum( ((x[i] - x[n/2+i]) - (x[n/2+i] - x[i+n/2+1]))^2 )
        ```

        In this equation, the time series is split into two halves, and the difference between the means of each half is calculated for each possible pair of halves. The sum of the squared differences is then divided by the length of the time series minus one to obtain the $OCSB$ test statistic.

    ??? success "Credit"
        - All credit goes to the [`pmdarima`](http://alkaline-ml.com/pmdarima/index.html) library with the implementation of [`pmdarima.arima.OCSBTest`](http://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.OCSBTest.html).

    ??? question "References"
        - Osborn DR, Chui APL, Smith J, and Birchenhall CR (1988) "Seasonality and the order of integration for consumption", Oxford Bulletin of Economics and Statistics 50(4):361-377.
        - R's forecast::OCSB test source code: https://bit.ly/2QYQHno

    ??? tip "See Also"
        - [pmdarima.arima.OCSBTest](http://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.OCSBTest.html)
    """
    return OCSBTest(m=m, lag_method=lag_method, max_lag=max_lag).estimate_seasonal_differencing_term(x)

ch 🔗

ch(x: ArrayLike, m: int) -> int

Summary

The Canova-Hansen test for seasonal differences. Canova and Hansen (1995) proposed a test statistic for the null hypothesis that the seasonal pattern is stable. The test statistic can be formulated in terms of seasonal dummies or seasonal cycles. The former allows us to identify seasons (e.g. months or quarters) that are not stable, while the latter tests the stability of seasonal cycles (e.g. cycles of period 2 and 4 quarters in quarterly data).

Warning

This test is generally not used directly, but in conjunction with pmdarima.arima.nsdiffs(), which directly estimates the number of seasonal differences.

Details

The \(CH\) test (also known as the Canova-Hansen test) is a statistical test for detecting seasonality in time series data. It is based on the idea of comparing the goodness of fit of two models: a non-seasonal model and a seasonal model. The null hypothesis of the \(CH\) test is that the time series is non-seasonal, while the alternative hypothesis is that the time series is seasonal.

The test statistic is compared to a critical value from the chi-squared distribution with degrees of freedom equal to the difference in parameters between the two models. If the test statistic exceeds the critical value, the null hypothesis of non-seasonality is rejected in favor of the alternative hypothesis of seasonality.

The \(CH\) test is based on the following steps:

  1. Fit a non-seasonal autoregressive integrated moving average (ARIMA) model to the time series data, using a criterion such as Akaike Information Criterion (AIC) or Bayesian Information Criterion (BIC) to determine the optimal model order.
  2. Fit a seasonal ARIMA model to the time series data, using the same criterion to determine the optimal model order and seasonal period.
  3. Compute the sum of squared residuals (SSR) for both models.
  4. Compute the test statistic \(CH\) using the formula above.
  5. Compare the test statistic to a critical value from the chi-squared distribution with degrees of freedom equal to the difference in parameters between the two models. If the test statistic exceeds the critical value, reject the null hypothesis of non-seasonality in favor of the alternative hypothesis of seasonality.

The \(CH\) test is a powerful test for seasonality in time series data, as it accounts for both the presence and the nature of seasonality. However, it assumes that the time series data is stationary, and it may not be effective for detecting seasonality in non-stationary or irregular time series data. Additionally, it may not work well for time series data with short seasonal periods or with low seasonal amplitudes. Therefore, it should be used in conjunction with other tests and techniques for detecting seasonality in time series data.

Parameters:

Name Type Description Default
x ArrayLike

The time series vector.

required
m int

The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the Canova-Hansen test to work, m must exceed 1.

required

Returns:

Type Description
int

The seasonal differencing term.

The \(CH\) test defines a set of critical values:

(0.4617146, 0.7479655, 1.0007818,
 1.2375350, 1.4625240, 1.6920200,
 1.9043096, 2.1169602, 2.3268562,
 2.5406922, 2.7391007)

For different values of m, the \(CH\) statistic is compared to the corresponding critical value, and returns 1 if the computed statistic is greater than the critical value, or 0 if not.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import ch
>>> data = load_airline().values
>>> ch(x=data, m=12)
0
Calculation

The test statistic for the \(CH\) test is given by:

\[ CH = \frac { \left( \frac { SSRns - SSRs } { n - p - 1 } \right) } { \left( \frac { SSRs } { n - p - s - 1 } \right) } \]

where:

  • \(SSRns\) is the \(SSR\) for the non-seasonal model,
  • \(SSRs\) is the \(SSR\) for the seasonal model,
  • \(n\) is the sample size,
  • \(p\) is the number of parameters in the non-seasonal model, and
  • \(s\) is the number of parameters in the seasonal model.
CH = [(SSRns - SSRs) / (n - p - 1)] / (SSRs / (n - p - s - 1))
Notes

This test is generally not used directly, but in conjunction with pmdarima.arima.nsdiffs(), which directly estimates the number of seasonal differences.

Credit
References
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
@typechecked
def ch(x: ArrayLike, m: int) -> int:
    r"""
    !!! note "Summary"
        The Canova-Hansen test for seasonal differences. Canova and Hansen (1995) proposed a test statistic for the null hypothesis that the seasonal pattern is stable. The test statistic can be formulated in terms of seasonal dummies or seasonal cycles. The former allows us to identify seasons (e.g. months or quarters) that are not stable, while the latter tests the stability of seasonal cycles (e.g. cycles of period 2 and 4 quarters in quarterly data).

        !!! warning "Warning"
            This test is generally not used directly, but in conjunction with `pmdarima.arima.nsdiffs()`, which directly estimates the number of seasonal differences.

    ???+ abstract "Details"

        The $CH$ test (also known as the Canova-Hansen test) is a statistical test for detecting seasonality in time series data. It is based on the idea of comparing the goodness of fit of two models: a non-seasonal model and a seasonal model. The null hypothesis of the $CH$ test is that the time series is non-seasonal, while the alternative hypothesis is that the time series is seasonal.

        The test statistic is compared to a critical value from the chi-squared distribution with degrees of freedom equal to the difference in parameters between the two models. If the test statistic exceeds the critical value, the null hypothesis of non-seasonality is rejected in favor of the alternative hypothesis of seasonality.

        The $CH$ test is based on the following steps:

        1. Fit a non-seasonal autoregressive integrated moving average (ARIMA) model to the time series data, using a criterion such as Akaike Information Criterion (AIC) or Bayesian Information Criterion (BIC) to determine the optimal model order.
        1. Fit a seasonal ARIMA model to the time series data, using the same criterion to determine the optimal model order and seasonal period.
        1. Compute the sum of squared residuals (SSR) for both models.
        1. Compute the test statistic $CH$ using the formula above.
        1. Compare the test statistic to a critical value from the chi-squared distribution with degrees of freedom equal to the difference in parameters between the two models. If the test statistic exceeds the critical value, reject the null hypothesis of non-seasonality in favor of the alternative hypothesis of seasonality.

        The $CH$ test is a powerful test for seasonality in time series data, as it accounts for both the presence and the nature of seasonality. However, it assumes that the time series data is stationary, and it may not be effective for detecting seasonality in non-stationary or irregular time series data. Additionally, it may not work well for time series data with short seasonal periods or with low seasonal amplitudes. Therefore, it should be used in conjunction with other tests and techniques for detecting seasonality in time series data.

    Params:
        x (ArrayLike):
            The time series vector.
        m (int):
            The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the Canova-Hansen test to work, `m` must exceed 1.

    Returns:
        (int):
            The seasonal differencing term.

            The $CH$ test defines a set of critical values:

            ```
            (0.4617146, 0.7479655, 1.0007818,
             1.2375350, 1.4625240, 1.6920200,
             1.9043096, 2.1169602, 2.3268562,
             2.5406922, 2.7391007)
            ```

            For different values of `m`, the $CH$ statistic is compared to the corresponding critical value, and returns 1 if the computed statistic is greater than the critical value, or 0 if not.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import ch
        >>> data = load_airline().values
        >>> ch(x=data, m=12)
        0

        ```

    ??? equation "Calculation"

        The test statistic for the $CH$ test is given by:

        $$
        CH = \frac { \left( \frac { SSRns - SSRs } { n - p - 1 } \right) } { \left( \frac { SSRs } { n - p - s - 1 } \right) }
        $$

        where:

        - $SSRns$ is the $SSR$ for the non-seasonal model,
        - $SSRs$ is the $SSR$ for the seasonal model,
        - $n$ is the sample size,
        - $p$ is the number of parameters in the non-seasonal model, and
        - $s$ is the number of parameters in the seasonal model.

        ```
        CH = [(SSRns - SSRs) / (n - p - 1)] / (SSRs / (n - p - s - 1))
        ```

    ??? note "Notes"
        This test is generally not used directly, but in conjunction with `pmdarima.arima.nsdiffs()`, which directly estimates the number of seasonal differences.

    ??? success "Credit"
        - All credit goes to the [`pmdarima`](http://alkaline-ml.com/pmdarima/index.html) library with the implementation of [`pmdarima.arima.CHTest`](http://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.CHTest.html).

    ??? question "References"
        - Testing for seasonal stability using the Canova and Hansen test statistic: http://bit.ly/2wKkrZo
        - R source code for CH test: https://github.com/robjhyndman/forecast/blob/master/R/arima.R#L148

    ??? tip "See Also"
        - [`pmdarima.arima.CHTest`](http://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.CHTest.html)
    """
    return CHTest(m=m).estimate_seasonal_differencing_term(x)

seasonal_strength 🔗

seasonal_strength(x: ArrayLike, m: int) -> float

Summary

The seasonal strength test is a statistical test for detecting the strength of seasonality in time series data. It measures the extent to which the seasonal component of a time series explains the variation in the data.

Details

The seasonal strength test involves computing the seasonal strength index (\(SSI\)).

The \(SSI\) ranges between \(0\) and \(1\), with higher values indicating stronger seasonality in the data. The critical value for the \(SSI\) can be obtained from statistical tables based on the sample size and level of significance. If the \(SSI\) value exceeds the critical value, the null hypothesis of no seasonality is rejected in favor of the alternative hypothesis of seasonality.

The seasonal strength test involves the following steps:

  1. Decompose the time series data into its seasonal, trend, and residual components using a method such as seasonal decomposition of time series (STL) or moving average decomposition.
  2. Compute the variance of the seasonal component \(Var(S)\) and the variance of the residual component \(Var(R)\).
  3. Compute the \(SSI\) using the formula above.
  4. Compare the \(SSI\) to a critical value from a statistical table for a given significance level and sample size. If the \(SSI\) exceeds the critical value, reject the null hypothesis of no seasonality in favor of the alternative hypothesis of seasonality.

The seasonal strength test is a simple and intuitive test for seasonality in time series data. However, it assumes that the seasonal component is additive and that the residuals are independent and identically distributed. Moreover, it may not be effective for detecting complex seasonal patterns or seasonality in non-stationary or irregular time series data. Therefore, it should be used in conjunction with other tests and techniques for detecting seasonality in time series data.

Parameters:

Name Type Description Default
x ArrayLike

The time series vector.

required
m int

The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the seasonal strength test to work, m must exceed 1.

required

Returns:

Type Description
float

The seasonal strength value.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import seasonal_strength
>>> data = load_airline().values
>>> seasonal_strength(x=data, m=12)
0.778721...
Calculation

The \(SSI\) is computed using the following formula:

\[ SSI = \frac {Var(S)} {Var(S) + Var(R)} \]

where:

  • \(Var(S)\) is the variance of the seasonal component, and
  • \(Var(R)\) is the variance of the residual component obtained after decomposing the time series data into its seasonal, trend, and residual components using a method such as STL or moving average decomposition.
SSI = Var(S) / (Var(S) + Var(R))
Credit
  • Inspired by the tsfeatures library in both Python and R.
References
  • Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
@typechecked
def seasonal_strength(x: ArrayLike, m: int) -> float:
    r"""
    !!! note "Summary"
        The seasonal strength test is a statistical test for detecting the strength of seasonality in time series data. It measures the extent to which the seasonal component of a time series explains the variation in the data.

    ???+ abstract "Details"

        The seasonal strength test involves computing the seasonal strength index ($SSI$).

        The $SSI$ ranges between $0$ and $1$, with higher values indicating stronger seasonality in the data. The critical value for the $SSI$ can be obtained from statistical tables based on the sample size and level of significance. If the $SSI$ value exceeds the critical value, the null hypothesis of no seasonality is rejected in favor of the alternative hypothesis of seasonality.

        The seasonal strength test involves the following steps:

        1. Decompose the time series data into its seasonal, trend, and residual components using a method such as seasonal decomposition of time series (STL) or moving average decomposition.
        1. Compute the variance of the seasonal component $Var(S)$ and the variance of the residual component $Var(R)$.
        1. Compute the $SSI$ using the formula above.
        1. Compare the $SSI$ to a critical value from a statistical table for a given significance level and sample size. If the $SSI$ exceeds the critical value, reject the null hypothesis of no seasonality in favor of the alternative hypothesis of seasonality.

        The seasonal strength test is a simple and intuitive test for seasonality in time series data. However, it assumes that the seasonal component is additive and that the residuals are independent and identically distributed. Moreover, it may not be effective for detecting complex seasonal patterns or seasonality in non-stationary or irregular time series data. Therefore, it should be used in conjunction with other tests and techniques for detecting seasonality in time series data.

    Params:
        x (ArrayLike):
            The time series vector.
        m (int):
            The seasonal differencing term. For monthly data, e.g., this would be 12. For quarterly, 4, etc. For the seasonal strength test to work, `m` must exceed 1.

    Returns:
        (float):
            The seasonal strength value.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import seasonal_strength
        >>> data = load_airline().values
        >>> seasonal_strength(x=data, m=12)
        0.778721...

        ```

    ??? equation "Calculation"

        The $SSI$ is computed using the following formula:

        $$
        SSI = \frac {Var(S)} {Var(S) + Var(R)}
        $$

        where:

        - $Var(S)$ is the variance of the seasonal component, and
        - $Var(R)$ is the variance of the residual component obtained after decomposing the time series data into its seasonal, trend, and residual components using a method such as STL or moving average decomposition.

        ```
        SSI = Var(S) / (Var(S) + Var(R))
        ```

    ??? success "Credit"
        - Inspired by the `tsfeatures` library in both [`Python`](https://github.com/Nixtla/tsfeatures) and [`R`](http://pkg.robjhyndman.com/tsfeatures/).

    ??? question "References"
        - Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.

    ??? tip "See Also"
        - [`tsfeatures.stl_features`](https://github.com/Nixtla/tsfeatures/blob/main/tsfeatures/tsfeatures.py)
    """
    decomposition = seasonal_decompose(x=x, period=m, model="additive")
    seasonal = np.nanvar(decomposition.seasonal)
    residual = np.nanvar(decomposition.resid)
    return float(seasonal / (seasonal + residual))

trend_strength 🔗

trend_strength(x: ArrayLike, m: int) -> float

Summary

The trend strength test is a statistical test for detecting the strength of the trend component in time series data. It measures the extent to which the trend component of a time series explains the variation in the data.

Details

The trend strength test involves computing the trend strength index (\(TSI\)).

The \(TSI\) ranges between \(0\) and \(1\), with higher values indicating stronger trend in the data. The critical value for the \(TSI\) can be obtained from statistical tables based on the sample size and level of significance. If the \(TSI\) value exceeds the critical value, the null hypothesis of no trend is rejected in favor of the alternative hypothesis of trend.

The trend strength test involves the following steps:

  1. Decompose the time series data into its trend, seasonal, and residual components using a method such as seasonal decomposition of time series (STL) or moving average decomposition.
  2. Compute the variance of the trend component, denoted by \(Var(T)\).
  3. Compute the variance of the residual component, denoted by \(Var(R)\).
  4. Compute the trend strength index (\(TSI\)) using the formula above.
  5. Compare the \(TSI\) value to a critical value based on the sample size and level of significance. If the \(TSI\) value exceeds the critical value, reject the null hypothesis of no trend in favor of the alternative hypothesis of trend.

The trend strength test is a useful tool for identifying the strength of trend in time series data, and it can be used in conjunction with other tests and techniques for detecting trend. However, it assumes that the time series data is stationary and that the trend component is linear. Additionally, it may not be effective for time series data with short time spans or with nonlinear trends. Therefore, it should be used in conjunction with other tests and techniques for detecting trend in time series data.

Parameters:

Name Type Description Default
x ArrayLike

The time series vector.

required
m int

The frequency of the time series data set. For the trend strength test to work, m must exceed 1.

required

Returns:

Type Description
float

The trend strength score.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import trend_strength
>>> data = load_airline().values
>>> trend_strength(x=data, m=12)
0.965679...
Calculation

The trend strength test involves computing the trend strength index (\(TSI\)) using the following formula:

\[ TSI = \frac{ Var(T) } { Var(T) + Var(R) } \]

where:

  • \(Var(T)\) is the variance of the trend component, and
  • \(Var(R)\) is the variance of the residual component obtained after decomposing the time series data into its trend, seasonal, and residual components using a method such as STL or moving average decomposition.
TSI = Var(T) / (Var(T) + Var(R))
Credit
  • Inspired by the tsfeatures library in both Python and R.
References
  • Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
@typechecked
def trend_strength(x: ArrayLike, m: int) -> float:
    r"""
    !!! note "Summary"
        The trend strength test is a statistical test for detecting the strength of the trend component in time series data. It measures the extent to which the trend component of a time series explains the variation in the data.

    ???+ abstract "Details"

        The trend strength test involves computing the trend strength index ($TSI$).

        The $TSI$ ranges between $0$ and $1$, with higher values indicating stronger trend in the data. The critical value for the $TSI$ can be obtained from statistical tables based on the sample size and level of significance. If the $TSI$ value exceeds the critical value, the null hypothesis of no trend is rejected in favor of the alternative hypothesis of trend.

        The trend strength test involves the following steps:

        1. Decompose the time series data into its trend, seasonal, and residual components using a method such as seasonal decomposition of time series (STL) or moving average decomposition.
        1. Compute the variance of the trend component, denoted by $Var(T)$.
        1. Compute the variance of the residual component, denoted by $Var(R)$.
        1. Compute the trend strength index ($TSI$) using the formula above.
        1. Compare the $TSI$ value to a critical value based on the sample size and level of significance. If the $TSI$ value exceeds the critical value, reject the null hypothesis of no trend in favor of the alternative hypothesis of trend.

        The trend strength test is a useful tool for identifying the strength of trend in time series data, and it can be used in conjunction with other tests and techniques for detecting trend. However, it assumes that the time series data is stationary and that the trend component is linear. Additionally, it may not be effective for time series data with short time spans or with nonlinear trends. Therefore, it should be used in conjunction with other tests and techniques for detecting trend in time series data.

    Params:
        x (ArrayLike):
            The time series vector.
        m (int):
            The frequency of the time series data set. For the trend strength test to work, `m` must exceed 1.

    Returns:
        (float):
            The trend strength score.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import trend_strength
        >>> data = load_airline().values
        >>> trend_strength(x=data, m=12)
        0.965679...

        ```

    ??? equation "Calculation"

        The trend strength test involves computing the trend strength index ($TSI$) using the following formula:

        $$
        TSI = \frac{ Var(T) } { Var(T) + Var(R) }
        $$

        where:

        - $Var(T)$ is the variance of the trend component, and
        - $Var(R)$ is the variance of the residual component obtained after decomposing the time series data into its trend, seasonal, and residual components using a method such as STL or moving average decomposition.

        ```
        TSI = Var(T) / (Var(T) + Var(R))
        ```

    ??? success "Credit"
        - Inspired by the `tsfeatures` library in both [`Python`](https://github.com/Nixtla/tsfeatures) and [`R`](http://pkg.robjhyndman.com/tsfeatures/).

    ??? question "References"
        - Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.

    ??? tip "See Also"
        - [`tsfeatures.stl_features`](https://github.com/Nixtla/tsfeatures/blob/main/tsfeatures/tsfeatures.py)
    """
    decomposition = seasonal_decompose(x=x, period=m, model="additive")
    trend = np.nanvar(decomposition.trend)
    residual = np.nanvar(decomposition.resid)
    return float(trend / (trend + residual))

spikiness 🔗

spikiness(x: ArrayLike, m: int) -> float

Summary

The spikiness test is a statistical test that measures the degree of spikiness or volatility in a time series data. It aims to detect the presence of spikes or sudden changes in the data that may indicate important events or anomalies in the underlying process.

Details

The spikiness test involves computing the spikiness index (\(SI\)). The \(SI\) measures the intensity of spikes or outliers in the data relative to the overall variation. A higher \(SI\) value indicates a more spiky or volatile time series, while a lower \(SI\) value indicates a smoother or less volatile time series.

The spikiness test involves the following steps:

  1. Decompose the time series data into its seasonal, trend, and residual components using a method such as STL or moving average decomposition.
  2. Compute the mean absolute deviation of the residual component (\(MADR\)).
  3. Compute the mean absolute deviation of the seasonal component (\(MADS\)).
  4. Compute the spikiness index (\(SI\)) using the formula above.

The spikiness test can be used in conjunction with other tests and techniques for detecting spikes in time series data, such as change point analysis and outlier detection. However, it assumes that the time series data is stationary and that the spikes are abrupt and sudden. Additionally, it may not be effective for time series data with long-term trends or cyclical patterns. Therefore, it should be used in conjunction with other tests and techniques for detecting spikes in time series data.

Parameters:

Name Type Description Default
x ArrayLike

The time series vector.

required
m int

The frequency of the time series data set. For the spikiness test to work, m must exceed 1.

required

Returns:

Type Description
float

The spikiness score.

Examples
Basic usage
1
2
3
4
5
>>> from ts_stat_tests.utils.data import load_airline
>>> from ts_stat_tests.seasonality.algorithms import spikiness
>>> data = load_airline().values
>>> spikiness(x=data, m=12)
0.484221...
Calculation

The spikiness test involves computing the spikiness index (\(SI\)) using the following formula:

\[ SI = \frac {MADR} {MADS} \]

where:

  • \(MADR\) is the mean absolute deviation of the residuals, and
  • \(MADS\) is the mean absolute deviation of the seasonal component.
SI = MADR / MADS
Credit
  • All credit to the tsfeatures library. This code is a direct copy+paste from the tsfeatures.py module.
    It is not possible to refer directly to a spikiness function in the tsfeatures package because the process to calculate seasonal strength is embedded within their stl_features function. Therefore, it it necessary to copy it here.
References
  • Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.
See Also
Source code in src/ts_stat_tests/seasonality/algorithms.py
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
@typechecked
def spikiness(x: ArrayLike, m: int) -> float:
    r"""
    !!! note "Summary"
        The spikiness test is a statistical test that measures the degree of spikiness or volatility in a time series data. It aims to detect the presence of spikes or sudden changes in the data that may indicate important events or anomalies in the underlying process.

    ???+ abstract "Details"

        The spikiness test involves computing the spikiness index ($SI$). The $SI$ measures the intensity of spikes or outliers in the data relative to the overall variation. A higher $SI$ value indicates a more spiky or volatile time series, while a lower $SI$ value indicates a smoother or less volatile time series.

        The spikiness test involves the following steps:

        1. Decompose the time series data into its seasonal, trend, and residual components using a method such as STL or moving average decomposition.
        1. Compute the mean absolute deviation of the residual component ($MADR$).
        1. Compute the mean absolute deviation of the seasonal component ($MADS$).
        1. Compute the spikiness index ($SI$) using the formula above.

        The spikiness test can be used in conjunction with other tests and techniques for detecting spikes in time series data, such as change point analysis and outlier detection. However, it assumes that the time series data is stationary and that the spikes are abrupt and sudden. Additionally, it may not be effective for time series data with long-term trends or cyclical patterns. Therefore, it should be used in conjunction with other tests and techniques for detecting spikes in time series data.

    Params:
        x (ArrayLike):
            The time series vector.
        m (int):
            The frequency of the time series data set. For the spikiness test to work, `m` must exceed 1.

    Returns:
        (float):
            The spikiness score.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Basic usage"}
        >>> from ts_stat_tests.utils.data import load_airline
        >>> from ts_stat_tests.seasonality.algorithms import spikiness
        >>> data = load_airline().values
        >>> spikiness(x=data, m=12)
        0.484221...

        ```

    ??? equation "Calculation"

        The spikiness test involves computing the spikiness index ($SI$) using the following formula:

        $$
        SI = \frac {MADR} {MADS}
        $$

        where:

        - $MADR$ is the mean absolute deviation of the residuals, and
        - $MADS$ is the mean absolute deviation of the seasonal component.

        ```
        SI = MADR / MADS
        ```

    ??? success "Credit"
        - All credit to the [`tsfeatures`](http://pkg.robjhyndman.com/tsfeatures/) library. This code is a direct copy+paste from the [`tsfeatures.py`](https://github.com/Nixtla/tsfeatures/blob/master/tsfeatures/tsfeatures.py) module.<br>It is not possible to refer directly to a `spikiness` function in the `tsfeatures` package because the process to calculate seasonal strength is embedded within their `stl_features` function. Therefore, it it necessary to copy it here.

    ??? question "References"
        - Wang, X, Hyndman, RJ, Smith-Miles, K (2007) "Rule-based forecasting filters using time series features", Computational Statistics and Data Analysis, 52(4), 2244-2259.

    ??? tip "See Also"
        - [`tsfeatures.stl_features`](https://github.com/Nixtla/tsfeatures/blob/main/tsfeatures/tsfeatures.py)
    """
    decomposition = seasonal_decompose(x=x, model="additive", period=m)
    madr = np.nanmean(np.abs(decomposition.resid))
    mads = np.nanmean(np.abs(decomposition.seasonal))
    return float(madr / mads)