MSTL

This function applies MSTL to decompose a univariate series that contains multiple seasonal cycles. It estimates trend, one or more seasonal components, and residuals.

The additive structure is:

y_t = T_t + \sum_{k=1}^{K} S_{k,t} + R_t

where K is the number of seasonal periods, S_{k,t} are seasonal components for each period, and R_t is the remainder.

Excel Usage

=MSTL(data, periods, iterate)
  • data (list[list], required): 2D range of time-series values (numeric).
  • periods (list[list], required): 2D range containing one or more seasonal periods (integers greater than 1).
  • iterate (int, optional, default: 2): Number of seasonal refinement iterations.

Returns (list[list]): 2D array with columns [observed, trend, seasonal_1, seasonal_2, …, residual].

Example 1: MSTL with two seasonal periods

Inputs:

data periods iterate
20 23 25 22 21 24 26 23 22 25 27 24 23 26 28 25 24 27 29 26 28 25 24 27 29 5 10 2

Excel formula:

=MSTL({20,23,25,22,21,24,26,23,22,25,27,24,23,26,28,25,24,27,29,26,28,25,24,27,29}, {5,10}, 2)

Expected output:

Result
20 21.479 0.0202247 -0.878662 -0.620525
23 21.815 2.23377 -1.36666 0.31791
25 22.1505 1.49776 0.566002 0.785779
22 22.4859 -1.56819 1.20793 -0.125661
21 22.8203 -1.44844 0.223642 -0.595456
24 23.1516 0.2956 0.505432 0.0473239
26 23.4782 1.02264 1.45145 0.0477026
23 23.7966 0.510197 -1.35486 0.0480551
22 24.1 -0.687053 -1.46141 0.0484991
25 24.4101 -0.412508 0.953209 0.0492167
27 24.7222 0.523653 0.360175 1.39397
24 25.0108 -0.156962 -0.355014 -0.498848
23 25.2588 -0.405613 -0.411149 -1.44203
26 25.465 0.186737 -0.0501534 0.398366
28 25.6346 0.580126 0.437742 1.34757
25 25.7859 0.650289 -1.48776 0.0516016
24 25.9359 -1.28232 -0.705539 0.0519384
27 26.053 -1.18458 2.07879 0.0528121
29 26.1462 1.04578 1.75426 0.0537813
26 26.2296 1.48962 -1.77401 0.0547463
28 26.3074 0.725616 1.57736 -0.610343
25 26.3814 -2.37883 0.668545 0.328856
24 26.4536 -1.89141 -1.35967 0.797503
27 26.5242 1.9012 -1.3123 -0.113133
29 26.5924 2.35882 0.630889 -0.582089
Example 2: MSTL with a single seasonal period

Inputs:

data periods iterate
10 12 14 12 11 13 15 13 12 14 16 14 4 2

Excel formula:

=MSTL({10,12,14,12,11,13,15,13,12,14,16,14}, {4}, 2)

Expected output:

Result
10 11.625 -1.625 5.32907e-15
12 11.875 0.125 3.55271e-15
14 12.125 1.875 1.77636e-15
12 12.375 -0.375 5.32907e-15
11 12.625 -1.625 -1.77636e-15
13 12.875 0.125 0
15 13.125 1.875 3.55271e-15
13 13.375 -0.375 -3.55271e-15
12 13.625 -1.625 0
14 13.875 0.125 1.77636e-15
16 14.125 1.875 0
14 14.375 -0.375 3.55271e-15
Example 3: MSTL with column-oriented input

Inputs:

data periods iterate
10 4 2
12
14
12
11
13
15
13
12
14
16
14

Excel formula:

=MSTL({10;12;14;12;11;13;15;13;12;14;16;14}, {4}, 2)

Expected output:

Result
10 11.625 -1.625 5.32907e-15
12 11.875 0.125 3.55271e-15
14 12.125 1.875 1.77636e-15
12 12.375 -0.375 5.32907e-15
11 12.625 -1.625 -1.77636e-15
13 12.875 0.125 0
15 13.125 1.875 3.55271e-15
13 13.375 -0.375 -3.55271e-15
12 13.625 -1.625 0
14 13.875 0.125 1.77636e-15
16 14.125 1.875 0
14 14.375 -0.375 3.55271e-15
Example 4: MSTL with additional refinement iterations

Inputs:

data periods iterate
20 23 25 22 21 24 26 23 22 25 27 24 23 26 28 25 24 27 29 26 28 25 24 27 29 5 10 3

Excel formula:

=MSTL({20,23,25,22,21,24,26,23,22,25,27,24,23,26,28,25,24,27,29,26,28,25,24,27,29}, {5,10}, 3)

Expected output:

Result
20 21.479 0.15935 -1.01817 -0.620191
23 21.815 2.37819 -1.51093 0.317729
25 22.1505 1.55633 0.507639 0.785544
22 22.4859 -1.6703 1.31013 -0.125763
21 22.8203 -1.60248 0.377451 -0.595234
24 23.1516 0.350146 0.450885 0.0473218
26 23.4782 1.11296 1.36114 0.0477006
23 23.7966 0.574082 -1.41874 0.0480534
22 24.1 -0.728905 -1.41956 0.0484975
25 24.4101 -0.493507 1.0342 0.0492154
27 24.7222 0.493575 0.390932 1.39329
24 25.0108 -0.120351 -0.391981 -0.498489
23 25.2588 -0.336597 -0.480626 -1.44157
26 25.465 0.204942 -0.0685571 0.398567
28 25.6346 0.572455 0.445857 1.34712
25 25.7859 0.534735 -1.37222 0.051601
24 25.9359 -1.29901 -0.688858 0.0519379
27 26.053 -1.10988 2.00411 0.0528118
29 26.1462 1.12428 1.67578 0.0537813
26 26.2296 1.55476 -1.83912 0.0547465
28 26.3073 0.524293 1.77839 -0.610006
25 26.3814 -2.44893 0.738879 0.328678
24 26.4535 -1.81052 -1.44027 0.797272
27 26.5242 2.04037 -1.45131 -0.113232
29 26.5923 2.49624 0.493316 -0.581864

Python Code

import numpy as np
from statsmodels.tsa.seasonal import MSTL as sm_MSTL

def mstl(data, periods, iterate=2):
    """
    Perform multi-seasonal STL decomposition on a time series.

    See: https://www.statsmodels.org/stable/generated/statsmodels.tsa.seasonal.MSTL.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        data (list[list]): 2D range of time-series values (numeric).
        periods (list[list]): 2D range containing one or more seasonal periods (integers greater than 1).
        iterate (int, optional): Number of seasonal refinement iterations. Default is 2.

    Returns:
        list[list]: 2D array with columns [observed, trend, seasonal_1, seasonal_2, ..., residual].
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        data = to2d(data)
        periods = to2d(periods)

        if not isinstance(iterate, int) or iterate < 1:
            return "Error: iterate must be an integer greater than or equal to 1"
        if not isinstance(data, list) or not all(isinstance(row, list) for row in data):
            return "Error: data must be a 2D list"
        if not isinstance(periods, list) or not all(isinstance(row, list) for row in periods):
            return "Error: periods must be a 2D list"

        values = []
        for row in data:
            for item in row:
                try:
                    values.append(float(item))
                except (TypeError, ValueError):
                    continue

        period_values = []
        for row in periods:
            for item in row:
                try:
                    p = int(item)
                    if p > 1:
                        period_values.append(p)
                except (TypeError, ValueError):
                    continue

        if not values:
            return "Error: data must contain at least one numeric value"
        if not period_values:
            return "Error: periods must contain at least one integer greater than 1"
        if len(values) < 2 * max(period_values):
            return "Error: data must contain at least two cycles of the largest period"

        period_arg = period_values[0] if len(period_values) == 1 else period_values
        result = sm_MSTL(values, periods=period_arg, iterate=iterate).fit()

        observed = np.asarray(values)
        trend = np.asarray(result.trend)
        seasonal = np.asarray(result.seasonal)
        resid = np.asarray(result.resid)

        if seasonal.ndim == 1:
            seasonal = seasonal.reshape(-1, 1)

        output = []
        for i in range(len(observed)):
            row = [float(observed[i]) if np.isfinite(observed[i]) else ""]
            row.append(float(trend[i]) if np.isfinite(trend[i]) else "")

            for j in range(seasonal.shape[1]):
                val = seasonal[i, j]
                row.append(float(val) if np.isfinite(val) else "")

            row.append(float(resid[i]) if np.isfinite(resid[i]) else "")
            output.append(row)

        return output
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

2D range of time-series values (numeric).
2D range containing one or more seasonal periods (integers greater than 1).
Number of seasonal refinement iterations.