Skip to main content
Ctrl+K

statsmodels

  • Installing
  • Getting started
  • User Guide
  • Examples
    • API Reference
    • About statsmodels
    • Developer Page
    • Release Notes
  • GitHub
  • PyPI
  • DOI
  • Installing
  • Getting started
  • User Guide
  • Examples
  • API Reference
  • About statsmodels
  • Developer Page
  • Release Notes
  • GitHub
  • PyPI
  • DOI

Section Navigation

  • Ordinary Least Squares
  • Generalized Least Squares
  • Quantile regression
  • Recursive least squares
  • Rolling Regression
  • Regression diagnostics
  • Weighted Least Squares
  • Linear Mixed Effects Models
  • Variance Component Analysis
  • Regression Plots
  • Linear regression diagnostics
  • Plot Interaction of Categorical Factors
  • Box Plots
  • Discrete Choice Models Overview
  • Discrete Choice Models
  • Ordinal Regression
  • Hurdle and truncated count models
  • Post-estimation Overview - Poisson
  • Kernel Density Estimation
  • LOWESS Smoother
  • Generalized Linear Models
  • Generalized Linear Models (Formula)
  • Weighted Generalized Linear Models
  • Influence Measures for GLM Logit
  • Quasi-binomial regression
  • M-Estimators for Robust Linear Modeling
  • Robust Linear Models
  • GEE nested covariance structure simulation study
  • GEE score tests
  • Interactions and ANOVA
  • Statistics and inference for one and two sample Poisson rates
  • Rank comparison: two independent samples
  • Meta-Analysis in statsmodels
  • Mediation analysis with duration data
  • Treatment effects under conditional independence
  • Copula - Multivariate joint distribution
  • Autoregressions
  • Autoregressive Distributed Lag (ARDL) models
  • Deterministic Terms in Time Series Models
  • Autoregressive Integrated Moving Average (ARIMA) Tutorial
  • Autoregressive Moving Average (ARMA): Sunspots data
  • Autoregressive Moving Average (ARMA): Artificial data
  • Time Series Filters
  • Markov switching dynamic regression models
  • Markov switching autoregression models
  • Exponential smoothing
  • Seasonal-Trend decomposition using LOESS (STL)
  • Multiple Seasonal-Trend decomposition using LOESS (MSTL)
  • Setting the seasonal value in STL
  • Stationarity and detrending (ADF/KPSS)
  • SARIMAX: Introduction
  • SARIMAX: Model selection, missing data
  • SARIMAX and ARIMA: Frequently Asked Questions (FAQ)
  • VARMAX models
  • Dynamic factors and coincident indices
  • Detrending, Stylized Facts and the Business Cycle
  • Trends and cycles in unemployment
  • State space modeling: Local Linear Trends
  • Autoregressive Moving Average (ARMA): Sunspots data
  • Seasonality in time series data
  • ARIMA with Seasonal Differencing Using Alternative Estimators
  • Estimating or specifying parameters in state space models
  • TVP-VAR, MCMC, and sparse simulation smoothing
  • Fast Bayesian estimation of SARIMAX models
  • Forecasting, updating datasets, and the “news”
  • Custom statespace models
  • ETS models
  • State space models - concentrating the scale out of the likelihood function
  • State space models - Chandrasekhar recursions
  • The Theta Model
  • statsmodels Principal Component Analysis
  • Multivariate Linear Model - MultivariateLS
  • Contrasts Overview
  • Formulas: Fitting models using R-style formulas
  • Prediction (out of sample)
  • Forecasting in statsmodels
  • Maximum Likelihood Estimation (Generic models)
  • Dates in timeseries models
  • Least squares fitting of models to data
  • Distributed Estimation
  • Examples
  • Trends and cycles in unemployment

Trends and cycles in unemployment#

Here we consider three methods for separating a trend and cycle in economic data. Supposing we have a time series \(y_t\), the basic idea is to decompose it into these two components:

\[y_t = \mu_t + \eta_t\]

where \(\mu_t\) represents the trend or level and \(\eta_t\) represents the cyclical component. In this case, we consider a stochastic trend, so that \(\mu_t\) is a random variable and not a deterministic function of time. Two of methods fall under the heading of “unobserved components” models, and the third is the popular Hodrick-Prescott (HP) filter. Consistent with e.g. Harvey and Jaeger (1993), we find that these models all produce similar decompositions.

This notebook demonstrates applying these models to separate trend from cycle in the U.S. unemployment rate.

[1]:
%matplotlib inline
[2]:
import matplotlib.pyplot as plt

import statsmodels.api as sm
[3]:
from pandas_datareader.data import DataReader

endog = DataReader("UNRATE", "fred", start="1954-01-01")
endog.index.freq = endog.index.inferred_freq

Hodrick-Prescott (HP) filter#

The first method is the Hodrick-Prescott filter, which can be applied to a data series in a very straightforward method. Here we specify the parameter \(\lambda=129600\) because the unemployment rate is observed monthly.

[4]:
hp_cycle, hp_trend = sm.tsa.filters.hpfilter(endog, lamb=129600)

Unobserved components and ARIMA model (UC-ARIMA)#

The next method is an unobserved components model, where the trend is modeled as a random walk and the cycle is modeled with an ARIMA model - in particular, here we use an AR(4) model. The process for the time series can be written as:

\[\begin{split}\begin{align} y_t & = \mu_t + \eta_t \\ \mu_{t+1} & = \mu_t + \epsilon_{t+1} \\ \phi(L) \eta_t & = \nu_t \end{align}\end{split}\]

where \(\phi(L)\) is the AR(4) lag polynomial and \(\epsilon_t\) and \(\nu_t\) are white noise.

[5]:
mod_ucarima = sm.tsa.UnobservedComponents(endog, "rwalk", autoregressive=4)
# Here the powell method is used, since it achieves a
# higher loglikelihood than the default L-BFGS method
res_ucarima = mod_ucarima.fit(method="powell", disp=False)
print(res_ucarima.summary())
                        Unobserved Components Results
==============================================================================
Dep. Variable:                 UNRATE   No. Observations:                  870
Model:                    random walk   Log Likelihood                -464.940
                              + AR(4)   AIC                            941.879
Date:                Wed, 29 Jul 2026   BIC                            970.484
Time:                        17:38:40   HQIC                           952.825
Sample:                    01-01-1954
                         - 06-01-2026
Covariance Type:                  opg
================================================================================
                   coef    std err          z      P>|z|      [0.025      0.975]
--------------------------------------------------------------------------------
sigma2.level  3.463e-05      0.012      0.003      0.998      -0.024       0.024
sigma2.ar        0.1718      0.016     10.688      0.000       0.140       0.203
ar.L1            1.0250      0.019     54.140      0.000       0.988       1.062
ar.L2           -0.1043      0.016     -6.467      0.000      -0.136      -0.073
ar.L3            0.0730      0.023      3.138      0.002       0.027       0.119
ar.L4           -0.0237      0.019     -1.258      0.208      -0.061       0.013
===================================================================================
Ljung-Box (L1) (Q):                   0.00   Jarque-Bera (JB):           7215913.08
Prob(Q):                              0.96   Prob(JB):                         0.00
Heteroskedasticity (H):               8.98   Skew:                            17.71
Prob(H) (two-sided):                  0.00   Kurtosis:                       448.01
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Unobserved components with stochastic cycle (UC)#

The final method is also an unobserved components model, but where the cycle is modeled explicitly.

\[\begin{split}\begin{align} y_t & = \mu_t + \eta_t \\ \mu_{t+1} & = \mu_t + \epsilon_{t+1} \\ \eta_{t+1} & = \eta_t \cos \lambda_\eta + \eta_t^* \sin \lambda_\eta + \tilde \omega_t \qquad & \tilde \omega_t \sim N(0, \sigma_{\tilde \omega}^2) \\ \eta_{t+1}^* & = -\eta_t \sin \lambda_\eta + \eta_t^* \cos \lambda_\eta + \tilde \omega_t^* & \tilde \omega_t^* \sim N(0, \sigma_{\tilde \omega}^2) \end{align}\end{split}\]
[6]:
mod_uc = sm.tsa.UnobservedComponents(
    endog,
    "rwalk",
    cycle=True,
    stochastic_cycle=True,
    damped_cycle=True,
)
# Here the powell method gets close to the optimum
res_uc = mod_uc.fit(method="powell", disp=False)
# but to get to the highest loglikelihood we do a
# second round using the L-BFGS method.
res_uc = mod_uc.fit(res_uc.params, disp=False)
print(res_uc.summary())
                            Unobserved Components Results
=====================================================================================
Dep. Variable:                        UNRATE   No. Observations:                  870
Model:                           random walk   Log Likelihood                -471.775
                   + damped stochastic cycle   AIC                            951.550
Date:                       Wed, 29 Jul 2026   BIC                            970.610
Time:                               17:38:41   HQIC                           958.844
Sample:                           01-01-1954
                                - 06-01-2026
Covariance Type:                         opg
===================================================================================
                      coef    std err          z      P>|z|      [0.025      0.975]
-----------------------------------------------------------------------------------
sigma2.level        0.1366      0.020      6.964      0.000       0.098       0.175
sigma2.cycle        0.0275      0.019      1.474      0.141      -0.009       0.064
frequency.cycle     0.3491      0.204      1.708      0.088      -0.052       0.750
damping.cycle       0.7585      0.072     10.604      0.000       0.618       0.899
===================================================================================
Ljung-Box (L1) (Q):                   1.55   Jarque-Bera (JB):           7342101.07
Prob(Q):                              0.21   Prob(JB):                         0.00
Heteroskedasticity (H):               9.09   Skew:                            17.83
Prob(H) (two-sided):                  0.00   Kurtosis:                       452.41
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
/opt/hostedtoolcache/Python/3.14.6/x64/lib/python3.14/site-packages/statsmodels/tsa/statespace/mlemodel.py:736: ConvergenceWarning: Maximum Likelihood optimization failed to converge. Check mle_retvals
  mlefit = super().fit(

Graphical comparison#

The output of each of these models is an estimate of the trend component \(\mu_t\) and an estimate of the cyclical component \(\eta_t\). Qualitatively the estimates of trend and cycle are very similar, although the trend component from the HP filter is somewhat more variable than those from the unobserved components models. This means that relatively mode of the movement in the unemployment rate is attributed to changes in the underlying trend rather than to temporary cyclical movements.

[7]:
fig, axes = plt.subplots(2, figsize=(13, 5))
axes[0].set(title="Level/trend component")
axes[0].plot(endog.index, res_uc.level.smoothed, label="UC")
axes[0].plot(endog.index, res_ucarima.level.smoothed, label="UC-ARIMA(2,0)")
axes[0].plot(hp_trend, label="HP Filter")
axes[0].legend(loc="upper left")
axes[0].grid()

axes[1].set(title="Cycle component")
axes[1].plot(endog.index, res_uc.cycle.smoothed, label="UC")
axes[1].plot(endog.index, res_ucarima.autoregressive.smoothed, label="UC-ARIMA(2,0)")
axes[1].plot(hp_cycle, label="HP Filter")
axes[1].legend(loc="upper left")
axes[1].grid()

fig.tight_layout();
../../../_images/examples_notebooks_generated_statespace_cycles_11_0.png

previous

Detrending, Stylized Facts and the Business Cycle

next

State space modeling: Local Linear Trends

On this page
  • Hodrick-Prescott (HP) filter
  • Unobserved components and ARIMA model (UC-ARIMA)
  • Unobserved components with stochastic cycle (UC)
  • Graphical comparison
Show Source

© Copyright 2009-2025, Josef Perktold, Skipper Seabold, Jonathan Taylor, statsmodels-developers.

Created using Sphinx 9.1.0.

Built with the PyData Sphinx Theme 0.20.0.