Decompose Time Series Data
An expert skill for decomposing time series into trend, seasonality, and residual components using classical, ML, and signal-processing methods.
Maintainer of this project? Claim this page to edit the listing.
1.0.0Add to Favorites
Why it matters
Decompose complex time series data into its fundamental components: trend, seasonality, and residuals. This asset provides robust methods for understanding temporal patterns and improving forecasting accuracy.
Outcomes
What it gets done
Apply classical decomposition methods like Moving Average and STL.
Utilize advanced techniques including X-13ARIMA-SEATS, EMD, and Wavelet Decomposition.
Assess decomposition quality using diagnostic metrics and visualizations.
Generate Python code for various decomposition algorithms.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-time-series-decomposition | bash Overview
Time Series Decomposition Expert
Decomposes time series into trend, seasonality, and residual components using classical STL, X-13ARIMA-SEATS, empirical mode decomposition, or wavelet methods. Use it when forecasting or analyzing seasonal data; choose the method based on the series' linearity, stationarity, and noise level.
What it does
This skill acts as an expert in time series decomposition, breaking temporal data into interpretable components - trend, seasonality, cyclical variation, and residual/noise - using classical statistical methods, modern signal processing, and machine learning approaches. It works with both additive (Y(t) = Trend + Seasonal + Residual) and multiplicative (Y(t) = Trend x Seasonal x Residual) models, choosing the right one for the data's structure.
For classical decomposition it applies statsmodels' seasonal_decompose and STL (Seasonal and Trend decomposition using Loess), the latter with tunable seasonal/trend smoother lengths and robustness to outliers:
from statsmodels.tsa.seasonal import STL
def stl_decompose(ts, seasonal=7, trend=None, robust=True):
"""
STL decomposition with flexible parameters
"""
stl = STL(
ts,
seasonal=seasonal, # Length of seasonal smoother
trend=trend, # Length of trend smoother
robust=robust # Robust to outliers
)
result = stl.fit()
return {
'original': result.observed,
'trend': result.trend,
'seasonal': result.seasonal,
'residual': result.resid
}
For advanced cases it applies X-13ARIMA-SEATS for economic/business data needing trading-day adjustment and automatic outlier detection, Empirical Mode Decomposition (EMD/EEMD via PyEMD) for non-linear, non-stationary series, and wavelet decomposition (pywt) for multi-resolution and time-frequency analysis. Decomposition quality is assessed with reconstruction MSE/MAE, a Ljung-Box test for residual white noise, a Shapiro normality test, and explicit seasonal-strength and trend-strength scores - plus a standard four-panel plot (original, trend, seasonal, residual) for visual validation.
When to use - and when NOT to
Use Classical/STL decomposition for regular, stable seasonality with minimal outliers; X-13ARIMA-SEATS for economic or business data requiring trading-day adjustments; EMD/EEMD for non-linear, non-stationary data with complex patterns; and wavelet decomposition for multi-scale analysis, especially financial or engineering data. When seasonal strength scores below 0.3, consider a trend-only model instead of forcing a seasonal component; for high-noise series, prefer robust methods like STL with robust=True or EEMD.
X-13ARIMA-SEATS requires the separate X-13ARIMA-SEATS software to be installed and falls back to STL automatically if it isn't available.
Inputs and outputs
Input is a time series (a pandas Series with a datetime index, or a raw numeric array). Output is a dictionary of components (trend, seasonal, residual/original, or IMFs and wavelet coefficients depending on method) plus diagnostic metrics for validating the decomposition's quality. Suggested parameter defaults: STL seasonal parameter as an odd integer, typically 7-15 for strong seasonality; db4 wavelet for general use, haar for sharp changes, coif2 for smooth data; EMD max_imf starting around data_length/10.
Who it's for
Data scientists and analysts working with temporal data - forecasting, anomaly detection, or seasonal adjustment - who need to separate trend, seasonality, and noise using the method best suited to their data's linearity, stationarity, and noise level.
Source README
Time Series Decomposition Expert
You are an expert in time series decomposition, specializing in breaking down temporal data into interpretable components (trend, seasonality, and residuals) using classical statistical methods, modern signal processing techniques, and machine learning approaches.
Core Decomposition Principles
Fundamental Components
- Trend: Long-term directional movement in the data
- Seasonality: Regular, predictable patterns that repeat over fixed periods
- Cyclical: Irregular fluctuations without fixed periods (often combined with trend)
- Residual/Noise: Random variation after removing systematic components
Decomposition Models
- Additive Model: Y(t) = Trend(t) + Seasonal(t) + Residual(t)
- Multiplicative Model: Y(t) = Trend(t) × Seasonal(t) × Residual(t)
- Mixed Models: Combinations for complex patterns
Classical Decomposition Methods
Moving Average Decomposition
import pandas as pd
import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
### Classical decomposition
def classical_decompose(ts, model='additive', period=None):
"""
Perform classical time series decomposition
"""
decomposition = seasonal_decompose(
ts,
model=model,
period=period,
extrapolate_trend='freq' # Handle NaN values at boundaries
)
return {
'original': decomposition.observed,
'trend': decomposition.trend,
'seasonal': decomposition.seasonal,
'residual': decomposition.resid
}
### Example usage
ts = pd.read_csv('data.csv', index_col='date', parse_dates=True)['value']
result = classical_decompose(ts, model='additive', period=12)
STL Decomposition (Seasonal and Trend decomposition using Loess)
from statsmodels.tsa.seasonal import STL
def stl_decompose(ts, seasonal=7, trend=None, robust=True):
"""
STL decomposition with flexible parameters
"""
stl = STL(
ts,
seasonal=seasonal, # Length of seasonal smoother
trend=trend, # Length of trend smoother
robust=robust # Robust to outliers
)
result = stl.fit()
return {
'original': result.observed,
'trend': result.trend,
'seasonal': result.seasonal,
'residual': result.resid
}
### Adaptive seasonal parameter
def adaptive_stl(ts, freq=None):
if freq is None:
freq = pd.infer_freq(ts.index)
seasonal_length = {
'D': 7, # Daily: weekly seasonality
'H': 24, # Hourly: daily seasonality
'M': 12, # Monthly: yearly seasonality
'Q': 4 # Quarterly: yearly seasonality
}.get(freq, 7)
return stl_decompose(ts, seasonal=seasonal_length)
Advanced Decomposition Techniques
X-13ARIMA-SEATS (Seasonal Adjustment)
from statsmodels.tsa.x13 import x13_arima_analysis
def x13_decompose(ts, trading=True, outlier=True):
"""
X-13ARIMA-SEATS decomposition (requires X-13ARIMA-SEATS software)
"""
try:
result = x13_arima_analysis(
ts,
trading=trading, # Trading day adjustment
outlier=outlier, # Automatic outlier detection
forecast_years=2 # Extend series for better seasonal factors
)
return {
'original': result.observed,
'trend': result.trend,
'seasonal': result.seasadj, # Seasonally adjusted
'irregular': result.irregular
}
except:
print("X-13ARIMA-SEATS not available, falling back to STL")
return stl_decompose(ts)
Empirical Mode Decomposition (EMD)
from PyEMD import EMD, EEMD
def emd_decompose(ts, max_imf=10):
"""
Empirical Mode Decomposition for non-linear, non-stationary series
"""
# Convert to numpy array
values = ts.values if hasattr(ts, 'values') else np.array(ts)
time = np.arange(len(values))
# Standard EMD
emd = EMD()
emd.emd(values, max_imf=max_imf)
imfs, residue = emd.get_imfs_and_residue()
# Ensemble EMD for noise robustness
eemd = EEMD()
eemd.eemd(values, max_imf=max_imf)
eimfs, eresidue = eemd.get_imfs_and_residue()
return {
'imfs': imfs, # Intrinsic Mode Functions
'residue': residue, # Final residue (trend)
'ensemble_imfs': eimfs, # Ensemble IMFs
'ensemble_residue': eresidue
}
Wavelet Decomposition
import pywt
from scipy import signal
def wavelet_decompose(ts, wavelet='db4', levels=6):
"""
Wavelet decomposition for multi-resolution analysis
"""
values = ts.values if hasattr(ts, 'values') else np.array(ts)
# Discrete Wavelet Transform
coeffs = pywt.wavedec(values, wavelet, level=levels)
# Reconstruct components
components = []
for i in range(len(coeffs)):
# Zero out all coefficients except current level
temp_coeffs = [np.zeros_like(c) for c in coeffs]
temp_coeffs[i] = coeffs[i]
# Reconstruct
component = pywt.waverec(temp_coeffs, wavelet)
components.append(component[:len(values)])
return {
'approximation': components[0], # Low frequency (trend)
'details': components[1:], # High frequency components
'coefficients': coeffs
}
def continuous_wavelet_transform(ts, wavelet='cmor', scales=None):
"""
Continuous Wavelet Transform for time-frequency analysis
"""
values = ts.values if hasattr(ts, 'values') else np.array(ts)
if scales is None:
scales = np.arange(1, 128)
coefficients, frequencies = pywt.cwt(values, scales, wavelet)
return {
'coefficients': coefficients,
'frequencies': frequencies,
'scales': scales
}
Decomposition Quality Assessment
Diagnostic Metrics
from scipy import stats
from statsmodels.stats.diagnostic import acorr_ljungbox
def assess_decomposition(original, trend, seasonal, residual):
"""
Comprehensive decomposition quality assessment
"""
# Reconstruction accuracy
reconstructed = trend + seasonal
mse = np.mean((original - reconstructed) ** 2)
mae = np.mean(np.abs(original - reconstructed))
# Residual analysis
residual_clean = residual.dropna()
# Test for white noise in residuals
ljung_box = acorr_ljungbox(residual_clean, lags=min(10, len(residual_clean)//4))
# Normality test
shapiro_stat, shapiro_p = stats.shapiro(residual_clean[:5000]) # Limit for large series
# Seasonality strength
seasonal_strength = 1 - np.var(residual_clean) / np.var(original - trend)
# Trend strength
trend_strength = 1 - np.var(residual_clean) / np.var(original - seasonal)
return {
'reconstruction_mse': mse,
'reconstruction_mae': mae,
'residual_ljung_box_pvalue': ljung_box['lb_pvalue'].iloc[-1],
'residual_normality_pvalue': shapiro_p,
'seasonal_strength': max(0, seasonal_strength),
'trend_strength': max(0, trend_strength)
}
Visualization Best Practices
def plot_decomposition(components, title="Time Series Decomposition"):
"""
Professional decomposition visualization
"""
fig, axes = plt.subplots(4, 1, figsize=(15, 12))
# Original series
axes[0].plot(components['original'], color='black', linewidth=1.2)
axes[0].set_title('Original Series')
axes[0].grid(True, alpha=0.3)
# Trend
axes[1].plot(components['trend'], color='red', linewidth=1.5)
axes[1].set_title('Trend Component')
axes[1].grid(True, alpha=0.3)
# Seasonal
axes[2].plot(components['seasonal'], color='blue', linewidth=1)
axes[2].set_title('Seasonal Component')
axes[2].grid(True, alpha=0.3)
# Residual
axes[3].plot(components['residual'], color='green', linewidth=0.8)
axes[3].axhline(y=0, color='black', linestyle='--', alpha=0.5)
axes[3].set_title('Residual Component')
axes[3].grid(True, alpha=0.3)
plt.tight_layout()
plt.suptitle(title, y=1.02, fontsize=14)
plt.show()
Method Selection Guidelines
Decision Framework
- Classical/STL: Regular, stable seasonality with minimal outliers
- X-13ARIMA-SEATS: Economic/business data requiring trading day adjustments
- EMD/EEMD: Non-linear, non-stationary data with complex patterns
- Wavelet: Multi-scale analysis, especially for financial or engineering data
- Seasonal strength < 0.3: Consider trend-only models
- High noise levels: Use robust methods (STL with robust=True, EEMD)
Parameter Tuning
- STL seasonal parameter: Odd integer, typically 7-15 for strong seasonality
- Wavelet selection: 'db4' for general use, 'haar' for sharp changes, 'coif2' for smooth data
- EMD max_imf: Start with data_length/10, adjust based on meaningful components
Always validate decomposition quality using residual diagnostics and domain knowledge to ensure components are interpretable and useful for your specific application.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.