statsmodels 状态 space 模型中的平滑状态干扰

smoothed state disturbances in statsmodels' state space model

我正在构建自定义状态-space 模型,其中包含如下统计模型

from statsmodels.tsa.statespace import mlemodel

mod = mlemodel.MLEModel(YY, k_states=n_st, k_posdef=n_sh)
mod['design'] = Z
mod['transition'] = T
mod['selection'] = R
mod['state_cov'] = np.eye(n_sh)
mod['obs_intercept'] = d

mod.initialize_stationary()

我对平滑状态和平滑状态扰动很感兴趣,我得到了

results = mod.smooth([])

results.smoothed_state 中的平滑状态是正确的(我有我正在比较的真实值),但 results.smoothed_state_disturbance 中的平滑状态扰动向前移动了一个周期 - 第一列包含第二个周期的(正确的)平滑扰动等,而最后一列包含零,这是样本结束后一个周期的正确平滑扰动。

我的理解是,这与状态方程的时间有关,根据statsmodels docs here

alpha(t+1) = T alpha(t) + R eta(t) (1)

因此意味着第一个观测值 y_{1} 与状态 alpha_{1} 有关,而状态 alpha_{1} 又取决于扰动 eta_{0},并且该扰动的平滑值不会由顺畅。另一方面,在this statmodels docs中,状态方程的时序为

alpha(t) = T alpha(t-1) + R eta(t) (2)

并暗示状态 alpha_{1} 取决于 eta_{1},而不是 eta_{0}。由于(未来 (1) 和同期 (2))时间约定都出现在 statsmodels 文档中,我认为可以选择使用哪一个。不幸的是,一直无法找出方法。我尝试使用 results = mod.smooth([], filter_timing=1) 更改更平滑的时间,根据文档使用 Kim 和 Nelson (1999)(同期)时间,而不是默认的 Durbin 和 Koopman (2012)(未来)时间。但是后来我得到了完全不同的结果(而且是错误的,因为我知道真正的价值是什么)不仅来自更平滑的结果,还来自对数似然值的结果。我还在 the unit tests for smoothing 中查找示例,但只有针对 MATLAB 和 R 库的测试也使用未来时序,并且没有针对 STATA 的测试(用于干扰平滑),后者使用替代同期时序。

我的问题是,有没有办法用同期时间(上面的(2))写出状态方程,或者恢复与第一周期观测数据相关的平滑状态扰动。

这是以下带有测量误差的 AR(1) 模型的一些代码,对状态方程使用 同期 时序,使用平稳分布进行初始化。

alpha(0) ~ N(0, 1/(1-.5**2))

alpha(t) = .5 alpha(t-1) + eta(t), eta(t) ~ N(0, 1)

y(t) = alpha(t) + e(t), e(t) ~ N(0, 1)

from statsmodels.tsa.statespace import mlemodel
import numpy as np
import sys
from scipy.stats import multivariate_normal

from numpy.random import default_rng
gen = default_rng(42)

T = np.array([.5])
Z = np.array([1.])
Q = np.array([1.])
H = np.array([1.])
R = np.array([1.])

P0 = 1/(1-T**2)

# Simulate data for 2 periods

alpha0 = gen.normal(0, np.sqrt(P0))
eta1 = gen.normal(0, 1)
e1 = gen.normal(0, 1)
eta2 = gen.normal(0, 1)
e2 = gen.normal(0, 1)

alpha1 = .5*alpha0 + eta1
y1 = alpha1 + e1

alpha2 = .5*alpha1 + eta2
y2 = alpha2 + e2

首先,使用 statsmodels.statespace 计算平滑状态、平滑状态扰动和仅给定第一个数据点的对数似然

mod1 = mlemodel.MLEModel(y1, k_states=1, k_posdef=1)

mod1['design'] = Z
mod1['transition'] = T
mod1['selection'] =  R
mod1['state_cov'] =  Q
mod1['obs_cov'] =  H

mod1.initialize_stationary()
results1 = mod1.smooth([])

results1.smoothed_state, results1.smoothed_state_disturbance, results1.llf

给予

(array([[-0.06491681]]), array([[0.]]), -1.3453530272821392)

注意,观察 y(1) 我们可以计算 eta(1) 的条件期望,然而,这里返回的是什么只是 eta(2) 的条件期望。由于模型是平稳的和高斯的,alpha(1)eta(1) 的条件期望给定 y(1 )可以从它们的联合分布计算出来(相关公式见here),如下代码

# Define a matrix L1 which maps [alpha(0), eta(1), e(1)] into [alpha0, eta1, e1, alpha1, y1]

L1 = np.vstack((np.eye(3),          # alpha(0), eta(1), e(1)
                np.r_[T, 1, 0],     # alpha(1)
                np.r_[T, 1, 1],     # y(1)           
              ))
# check
np.testing.assert_array_equal(np.r_[alpha0, eta1, e1, alpha1, y1],
                              L1 @ np.r_[alpha0, eta1, e1])

# Compute Sigma1 as the covariance matrix of [alpha0, eta1, e1, alpha1, y1]

D1 = np.eye(3)
D1[0, 0] = P0
Sigma1 = L1 @ D1 @ L1.T

# [alpha0, eta1, e1, alpha1, y1] has a multivariate Normal distribution, and we can apply well-known formulae to  compute conditional expectations and the log-likelihood

ind_e1 = 1
ind_eta1 = 2
ind_alpha1 = 3
ind_y1 = 4

smooth_eta1 =  (Sigma1[ind_eta1, ind_y1]/Sigma1[ind_y1, ind_y1])*y1
smooth_alpha1 = (Sigma1[ind_alpha1, ind_y1]/Sigma1[ind_y1, ind_y1])*y1
loglik1 = multivariate_normal.logpdf(y1, cov=Sigma1[ind_y1, ind_y1])

smooth_alpha1, smooth_eta1, loglik1

这给出了

(array([-0.06491681]), array([-0.04868761]), -1.3453530272821392)

扩展到前 2 个周期,statsmodels

y = np.array([y1, y2])
mod2 = mlemodel.MLEModel(y, k_states=1, k_posdef=1)

mod2.ssm.timing_init_filtered = True

mod2['design'] = Z
mod2['transition'] = T
mod2['selection'] =  R
mod2['state_cov'] =  Q
mod2['obs_cov'] =  H

mod2.initialize_stationary()
results2 = mod2.smooth([])

results2.smoothed_state, results2.smoothed_state_disturbance, results2.llf

给予

(array([[-0.25292213, -0.78447967]]),
 array([[-0.65801861,  0.        ]]),
 -3.1092778246103645)

并计算联合分布的条件期望

# L2 maps [alpha(0), eta(1), e(1), eta(2), e(2)] into [alpha0, eta1, e1, eta2, e2, alpha1, alpha2, y1, y2]

L2 = np.vstack((np.eye(5), # alpha(0), eta(1), e(1), eta(2), e(2)
                np.r_[T,    1, 0, 0, 0],     # alpha(1)
                np.r_[T**2, T, 0, 1, 0],     # alpha(2)
                np.r_[T,    1, 1, 0, 0],     # y(1)           
                np.r_[T**2, T, 0, 1, 1],     # y(2)
              ))

np.testing.assert_array_equal(np.r_[alpha0, eta1, e1, eta2, e2, alpha1, alpha2, y1, y2],
                              L2 @ np.r_[alpha0, eta1, e1, eta2, e2,]) 

# Sigma2 is the covariance of [alpha0, eta1, e1, eta2, e2, alpha1, alpha2, y1, y2]

D2 = np.eye(5)
D2[0, 0] = P0
Sigma2 = L2 @ D2 @ L2.T

ind_e = [2, 4]
ind_eta = [1, 3]
ind_alpha = [5, 6]
ind_y = [7, 8]

# compute smoothed disturbances and states, and loglikelihood

smooth_eta = Sigma2[ind_eta, :][:, ind_y] @ np.linalg.solve(Sigma2[ind_y, :][:, ind_y], y)
smooth_alpha = Sigma2[ind_alpha, :][:, ind_y] @ np.linalg.solve(Sigma2[ind_y, :][:, ind_y], y)
loglik2 = multivariate_normal.logpdf(y.flatten(), cov=Sigma2[ind_y, :][:, ind_y])

smooth_alpha.flatten(), smooth_eta.flatten(), loglik2

给予

(array([-0.25292213, -0.78447967]),
 array([-0.1896916 , -0.65801861]),
 -3.1092778246103636)

平滑状态 alpha(t) 和对数似然值相同。 statsmodels.statespace.mlemodel 返回的平滑干扰用于 eta(2)eta(3).

最短答案

对您的问题的简短回答是,您可以执行以下操作来恢复您正在寻找的平滑估计:

r_0 = results1.smoother_results.scaled_smoothed_estimator_presample
R = mod1['selection']
Q = mod1['state_cov']
eta_hat_0 = Q @ R.T @ r_0
print(eta_hat_0)

这会如您所愿地给出 [-0.04868761]。这来自周期 0 的通常扰动平滑方程(例如 Durbin 和 Koopman 2012 年的书,方程 4.69)。在 Statsmodels 中,我们存储样本周期 1 - nobs 的平滑扰动估计,但我们确实存储 r_0,如上所示,所以你可以直接计算出你想要的值。

替代方法,也许更简单

这是获得此输出的另一种方法,我们可以通过以不同的方式编写您的问题来得出该输出。正如您所指出的,在 Statsmodels 的时间假设下,第一状态 alpha(1) ~ N(a1, P1) 的分布被指定为先验。我们可以将您想要的模型视为指定第 0 个状态 alpha(0) ~ N(a0, P0) 作为先验,然后将第一个状态视为由转移方程产生:alpha(1) = T alpha(0 ) + eta(0).

这是现在使用 Statsmodels 的错误约定编写的,我们可以使用 Statsmodels 来计算平滑的结果。唯一的技巧是没有与第一个状态 alpha(0) 关联的观察 y(0),因为我们只包括 alpha(0) -> alpha(1) 转换步骤,以便我们可以像您一样指定先验通缉。但这没问题,我们可以简单地包含一个缺失值。

因此,如果我们只是通过将 nan 预采样值放入输入数据来修改您的原始模型,我们将得到我们想要的结果:

mod1 = mlemodel.MLEModel(np.r_[np.nan, y1], k_states=1, k_posdef=1)

mod1['design'] = Z
mod1['transition'] = T
mod1['selection'] =  R
mod1['state_cov'] =  Q
mod1['obs_cov'] =  H

mod1.initialize_stationary()
results1 = mod1.smooth([])

print(results1.smoothed_state, results1.smoothed_state_disturbance, results1.llf)

产量:

[[-0.03245841 -0.06491681]] [[-0.04868761  0.        ]] -1.3453530272821392

关于 filter_timing 标志的注意事项:

filter_timing 标志还允许您更改先验的计时约定,但不能更改状态。如果在备选时间(filter_timing=1)中设置先验 alpha(0|0) ~ N(a_00, P_00),则设置 alpha(1) ~ N(a_1, P_1) with a_1 = T a_00 and P_1 = T P_00 T' + R Q R' in the default timing (filter_timing=0 ) 会给你完全相同的结果。