SEASDECOMP

This function performs classical seasonal decomposition using moving averages. It splits a univariate time series into observed, trend, seasonal, and residual components under either an additive or multiplicative model.

For an additive model, the decomposition is:

y_t = T_t + S_t + R_t

For a multiplicative model, the decomposition is:

y_t = T_t \times S_t \times R_t

The input must contain at least two full seasonal cycles so that seasonal effects can be estimated reliably.

Excel Usage

=SEASDECOMP(data, period, model, two_sided, extrapolate_points)
  • data (list[list], required): 2D range of time-series values (numeric).
  • period (int, required): Seasonal period in samples (positive integer).
  • model (str, optional, default: “additive”): Seasonal decomposition model type.
  • two_sided (bool, optional, default: true): Use centered moving average when true.
  • extrapolate_points (int, optional, default: 0): Number of nearest points used to extrapolate trend at boundaries.

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

Example 1: Additive decomposition with quarterly-style period

Inputs:

data period model two_sided extrapolate_points
10 12 14 12 11 13 15 13 12 14 16 14 4 additive true 0

Excel formula:

=SEASDECOMP({10,12,14,12,11,13,15,13,12,14,16,14}, 4, "additive", TRUE, 0)

Expected output:

Result
10 -1.625
12 0.125
14 12.125 1.875 0
12 12.375 -0.375 0
11 12.625 -1.625 0
13 12.875 0.125 0
15 13.125 1.875 0
13 13.375 -0.375 0
12 13.625 -1.625 0
14 13.875 0.125 0
16 1.875
14 -0.375
Example 2: Multiplicative decomposition with positive series

Inputs:

data period model two_sided extrapolate_points
10 20 15 25 11 22 16 27 12 24 18 30 4 multiplicative true 0

Excel formula:

=SEASDECOMP({10,20,15,25,11,22,16,27,12,24,18,30}, 4, "multiplicative", TRUE, 0)

Expected output:

Result
10 0.599561
20 1.16896
15 17.625 0.844172 1.00816
25 18 1.38731 1.00114
11 18.375 0.599561 0.998463
22 18.75 1.16896 1.00375
16 19.125 0.844172 0.991031
27 19.5 1.38731 0.998057
12 20 0.599561 1.00073
24 20.625 1.16896 0.99545
18 0.844172
30 1.38731
Example 3: Single-column data is flattened and decomposed

Inputs:

data period model two_sided extrapolate_points
10 4 additive true 0
12
14
12
11
13
15
13
12
14
16
14

Excel formula:

=SEASDECOMP({10;12;14;12;11;13;15;13;12;14;16;14}, 4, "additive", TRUE, 0)

Expected output:

Result
10 -1.625
12 0.125
14 12.125 1.875 0
12 12.375 -0.375 0
11 12.625 -1.625 0
13 12.875 0.125 0
15 13.125 1.875 0
13 13.375 -0.375 0
12 13.625 -1.625 0
14 13.875 0.125 0
16 1.875
14 -0.375
Example 4: One-sided moving average decomposition

Inputs:

data period model two_sided extrapolate_points
10 12 14 12 11 13 15 13 12 14 16 14 4 additive false 1

Excel formula:

=SEASDECOMP({10,12,14,12,11,13,15,13,12,14,16,14}, 4, "additive", FALSE, 1)

Expected output:

Result
10 11.125 -1.625 0.5
12 11.375 0.125 0.5
14 11.625 1.875 0.5
12 11.875 -0.375 0.5
11 12.125 -1.625 0.5
13 12.375 0.125 0.5
15 12.625 1.875 0.5
13 12.875 -0.375 0.5
12 13.125 -1.625 0.5
14 13.375 0.125 0.5
16 13.625 1.875 0.5
14 13.875 -0.375 0.5

Python Code

import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose as sm_seasonal_decompose

def seasdecomp(data, period, model='additive', two_sided=True, extrapolate_points=0):
    """
    Decompose a time series into trend, seasonal, and residual components.

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

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

    Args:
        data (list[list]): 2D range of time-series values (numeric).
        period (int): Seasonal period in samples (positive integer).
        model (str, optional): Seasonal decomposition model type. Valid options: Additive, Multiplicative. Default is 'additive'.
        two_sided (bool, optional): Use centered moving average when true. Default is True.
        extrapolate_points (int, optional): Number of nearest points used to extrapolate trend at boundaries. Default is 0.

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

        data = to2d(data)

        if model not in ("additive", "multiplicative"):
            return "Error: model must be 'additive' or 'multiplicative'"
        if not isinstance(period, int) or period < 2:
            return "Error: period must be an integer greater than or equal to 2"
        if not isinstance(data, list) or not all(isinstance(row, list) for row in data):
            return "Error: data must be a 2D list"

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

        if len(values) < 2 * period:
            return "Error: data must contain at least two complete seasonal cycles"

        result = sm_seasonal_decompose(
            values,
            model=model,
            period=period,
            two_sided=two_sided,
            extrapolate_trend=extrapolate_points,
        )

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

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

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

Online Calculator

2D range of time-series values (numeric).
Seasonal period in samples (positive integer).
Seasonal decomposition model type.
Use centered moving average when true.
Number of nearest points used to extrapolate trend at boundaries.