35. 用矩阵代数表示的单变量时间序列#
35.1. 概述#
本讲使用矩阵来解决线性差分方程。
作为一个实际例子,我们将研究一个保罗·萨缪尔森 1939 年文章 [Samuelson, 1939] 中的二阶线性差分方程,该文章引入了乘数加速器模型。
这个模型对早期美国凯恩斯主义宏观经济学的计量经济学研究产生了重要影响。
如果你想了解更多关于这个模型的细节,可以参考Samuelson Multiplier-Accelerator。
(该讲座也包含了二阶线性差分方程的一些技术细节。)
在本讲座中,我们将探讨如何用两种不同的方式来表示非平稳时间序列
我们还将研究一个涉及解“前瞻性”线性差分方程的“完全预见的”股票价格模型。
让我们先导入所需的Python包:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
# 设置默认图形大小
plt.rcParams["figure.figsize"] = (11, 5)
# 设置打印的浮点数精度
np.set_printoptions(precision=3, suppress=True)
35.2. 萨缪尔森的模型#
设
对于
我们假设
在萨缪尔森的模型中,
方程 (35.1) 称为 二阶线性差分方程,因为它包含了两个滞后值。
实际上,它是
Note
为了能够解决一个二阶线性差分方程,我们需要两个边界条件,它们可以采取两个初始条件或两个终端条件或每种各一个的形式。
我们将方程写成堆叠系统
或者
其中
显然,
向量
我们用 Python 来实现一个例子来展现萨缪尔森乘数-加速器模型的基本思想。
我们使用与Samuelson Multiplier-Accelerator讲座相同的参数值。
T = 80
# 参数
α_0 = 10.0
α_1 = 1.53
α_2 = -.9
y_neg1 = 28.0 # y_{-1}
y_0 = 24.0
现在我们构造
A = np.identity(T) # T x T 的单位矩阵
for i in range(T):
if i-1 >= 0:
A[i, i-1] = -α_1
if i-2 >= 0:
A[i, i-2] = -α_2
b = np.full(T, α_0)
b[0] = α_0 + α_1 * y_0 + α_2 * y_neg1
b[1] = α_0 + α_2 * y_0
让我们打印出例子中的矩阵
A, b
(array([[ 1. , 0. , 0. , ..., 0. , 0. , 0. ],
[-1.53, 1. , 0. , ..., 0. , 0. , 0. ],
[ 0.9 , -1.53, 1. , ..., 0. , 0. , 0. ],
...,
[ 0. , 0. , 0. , ..., 1. , 0. , 0. ],
[ 0. , 0. , 0. , ..., -1.53, 1. , 0. ],
[ 0. , 0. , 0. , ..., 0.9 , -1.53, 1. ]]),
array([ 21.52, -11.6 , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ,
10. , 10. , 10. , 10. , 10. , 10. , 10. , 10. ]))
现在我们来求解
如果
要求解
A_inv = np.linalg.inv(A)
y = A_inv @ b
或者我们可以使用 np.linalg.solve
:
y_second_method = np.linalg.solve(A, b)
我们确保这两种方法在一定精度下给出相同的结果:
np.allclose(y, y_second_method)
True
# Check if A is lower triangular
np.allclose(A, np.tril(A))
True
Note
一般来说,np.linalg.solve
比使用np.linalg.solve
在数值上更稳定。
然而,对于这个小例子来说,稳定性不是问题。此外,我们将下面重复使用A_inv
,直接计算出它来会有比较好。
现在我们可以绘制时间序列。
如果我们将初始值设为
y_star = α_0 / (1 - α_1 - α_2)
y_neg1_steady = y_star # y_{-1}
y_0_steady = y_star
b_steady = np.full(T, α_0)
b_steady[0] = α_0 + α_1 * y_0_steady + α_2 * y_neg1_steady
b_steady[1] = α_0 + α_2 * y_0_steady
y_steady = A_inv @ b_steady
35.3. 添加随机项#
为了让这个例子有趣一些,我们将遵循经济学家尤金·斯卢茨基和拉格纳·弗里希的方法,用以下二阶随机线性差分方程替换我们原来的二阶差分方程:
其中
我们将把这些
让我们定义随机向量
其中
所支配
让我们在Python中尝试一下。
σ_u = 2.
u = np.random.normal(0, σ_u, size=T)
y = A_inv @ (b + u)
上面生成的时间序列与许多发达国家近几十年的实际GDP数据(去除趋势后)有着惊人的相似性。
我们可以模拟
N = 100
for i in range(N):
col = cm.viridis(np.random.rand()) # 从 viridis 色系中随机选择一种颜色
u = np.random.normal(0, σ_u, size=T)
y = A_inv @ (b + u)
plt.plot(np.arange(T)+1, y, lw=0.5, color=col)
plt.xlabel('t')
plt.ylabel('y')
plt.show()
同样考虑
35.4. 计算总体矩#
我们可以应用多元正态分布的标准公式来计算我们的时间序列模型
你可以在这篇讲义中阅读关于多元正态分布的内容 多元正态分布。
让我们将我们的模型写为
其中
因为正态随机变量的线性组合依然是正态的,我们知道
其中
以及
让我们编写一个Python类来计算均值向量
class population_moments:
"""
计算人群矩 mu_y, Sigma_y.
---------
参数:
alpha0, alpha1, alpha2, T, y_1, y0
"""
def __init__(self, α_0=10.0,
α_1=1.53,
α_2=-.9,
T=80,
y_neg1=28.0,
y_0=24.0,
σ_u=1):
# 计算 A
A = np.identity(T)
for i in range(T):
if i-1 >= 0:
A[i, i-1] = -α_1
if i-2 >= 0:
A[i, i-2] = -α_2
# 计算 b
b = np.full(T, α_0)
b[0] = α_0 + α_1 * y_0 + α_2 * y_neg1
b[1] = α_0 + α_2 * y_0
# 计算 A 的逆
A_inv = np.linalg.inv(A)
self.A, self.b, self.A_inv, self.σ_u, self.T = A, b, A_inv, σ_u, T
def sample_y(self, n):
"""
提供一个大小为 n 的 y 样本。
"""
A_inv, σ_u, b, T = self.A_inv, self.σ_u, self.b, self.T
us = np.random.normal(0, σ_u, size=[n, T])
ys = np.vstack([A_inv @ (b + u) for u in us])
return ys
def get_moments(self):
"""
计算 y 的总体矩。
"""
A_inv, σ_u, b = self.A_inv, self.σ_u, self.b
# 计算 μ_y
self.μ_y = A_inv @ b
self.Σ_y = σ_u**2 * (A_inv @ A_inv.T)
return self.μ_y, self.Σ_y
series_process = population_moments()
μ_y, Σ_y = series_process.get_moments()
A_inv = series_process.A_inv
接下来,让我们探索不同参数值对
这个分析也将帮助我们理解一个重要的性质:
为了直观地理解这一点,我们先生成
# 绘制均值
N = 100
for i in range(N):
col = cm.viridis(np.random.rand()) # 从 viridis 色系中随机选择一种颜色
ys = series_process.sample_y(N)
plt.plot(ys[i,:], lw=0.5, color=col)
plt.plot(μ_y, color='red')
plt.xlabel('t')
plt.ylabel('y')
plt.show()
从图中可以观察到一个有趣的现象:随着时间
绘制总体方差
从图中我们可以看到,总体方差随时间增加,最终趋于一个稳定值。这种收敛行为反映了系统的内在稳定性。
为了进一步验证这一点,让我们通过模拟多条样本路径来计算样本方差,并将其与理论预测进行对比。
series_process = population_moments(α_0=0,
α_1=.8,
α_2=0,
T=6,
y_neg1=0.,
y_0=0.,
σ_u=1)
μ_y, Σ_y = series_process.get_moments()
print("μ_y = ", μ_y)
print("Σ_y = \n", Σ_y)
μ_y = [0. 0. 0. 0. 0. 0.]
Σ_y =
[[1. 0.8 0.64 0.512 0.41 0.328]
[0.8 1.64 1.312 1.05 0.84 0.672]
[0.64 1.312 2.05 1.64 1.312 1.049]
[0.512 1.05 1.64 2.312 1.849 1.48 ]
[0.41 0.84 1.312 1.849 2.48 1.984]
[0.328 0.672 1.049 1.48 1.984 2.587]]
观察
这个特征告诉我们,由
要使序列变得平稳,我们需要对系统做一个关键的改变:不再将初始条件
如果你想了解如何实现这一点,可以参考我们在线性状态空间模型中的详细讨论。
在继续深入分析之前,让我们先来看看
series_process = population_moments()
μ_y, Σ_y = series_process.get_moments()
print("bottom right corner of Σ_y = \n", Σ_y[72:,72:])
bottom right corner of Σ_y =
[[ 14.965 12.051 4.969 -3.243 -9.434 -11.515 -9.128 -3.602]
[ 12.051 14.965 12.051 4.969 -3.243 -9.434 -11.515 -9.128]
[ 4.969 12.051 14.966 12.051 4.97 -3.243 -9.434 -11.516]
[ -3.243 4.969 12.051 14.966 12.052 4.97 -3.243 -9.434]
[ -9.434 -3.243 4.97 12.052 14.967 12.053 4.97 -3.243]
[-11.515 -9.434 -3.243 4.97 12.053 14.968 12.053 4.97 ]
[ -9.128 -11.515 -9.434 -3.243 4.97 12.053 14.968 12.053]
[ -3.602 -9.128 -11.516 -9.434 -3.243 4.97 12.053 14.968]]
请注意,随着时间
这表明我们的过程是渐近平稳的。
你可以在线性状态空间模型中阅读更多关于更一般线性时间序列模型的平稳性。
通过观察不同时间段的
35.5. 移动平均表示#
让我们来研究
它是三角形矩阵、接近三角形,还是其他形式
?
为了便于观察,我们将打印
print(A_inv[0:7,0:7])
[[ 1. 0. -0. -0. 0. -0. -0. ]
[ 1.53 1. -0. -0. 0. -0. -0. ]
[ 1.441 1.53 1. 0. 0. 0. 0. ]
[ 0.828 1.441 1.53 1. 0. 0. 0. ]
[-0.031 0.828 1.441 1.53 1. -0. -0. ]
[-0.792 -0.031 0.828 1.441 1.53 1. 0. ]
[-1.184 -0.792 -0.031 0.828 1.441 1.53 1. ]]
显然,
注意每一行的结尾都与前一行的前对角线元素相同。
由于
与初始条件
相关的时间依赖函数 ,以及当前和过去 IID 冲击
的加权和。
因此,设
显然,对于
这是一个移动平均表示,其系数随时间变化。
我们可以看到,系统既可以用移动平均形式 (35.4) 表示,也可以用自回归形式 (35.3) 表示。这两种表示方式描述了同一个随机过程,只是从不同的角度来看。
35.6. 从后视到前瞻#
到目前为止,我们分析的萨缪尔森模型是一个后视(backward-looking)模型 - 给定初始条件后,系统就会向前演化。
接下来让我们转向一个*前瞻(forward-looking)*模型,这类模型在宏观经济学和金融学中被广泛使用。
我们应用类似的线性代数工具来研究一个广泛用作宏观经济学和金融学基准的完全预见模型。
例如,假设
假设股息
我们的完全预见股票价格模型是
其中
该模型表明,股票在
β = .96
# 构建 B
B = np.zeros((T, T))
for i in range(T):
B[i, i:] = β ** np.arange(0, T-i)
print(B)
[[1. 0.96 0.922 ... 0.043 0.041 0.04 ]
[0. 1. 0.96 ... 0.045 0.043 0.041]
[0. 0. 1. ... 0.047 0.045 0.043]
...
[0. 0. 0. ... 1. 0.96 0.922]
[0. 0. 0. ... 0. 1. 0.96 ]
[0. 0. 0. ... 0. 0. 1. ]]
σ_u = 0.
u = np.random.normal(0, σ_u, size=T)
y = A_inv @ (b + u)
y_steady = A_inv @ (b_steady + u)
p = B @ y
plt.plot(np.arange(0, T)+1, y, label='y')
plt.plot(np.arange(0, T)+1, p, label='p')
plt.xlabel('t')
plt.ylabel('y/p')
plt.legend()
plt.show()
你能解释一下为什么价格的趋势在随时间下降吗?
接下来还可以考虑当