【发布时间】:2018-08-16 16:00:58
【问题描述】:
在使用来自statsmodels 的UnobservedComponents 拟合本地级别模型后,我们正试图找到用结果模拟新时间序列的方法。比如:
import numpy as np
import statsmodels as sm
from statsmodels.tsa.statespace.structural import UnobservedComponents
np.random.seed(12345)
ar = np.r_[1, 0.9]
ma = np.array([1])
arma_process = sm.tsa.arima_process.ArmaProcess(ar, ma)
X = 100 + arma_process.generate_sample(nsample=100)
y = 1.2 * x + np.random.normal(size=100)
y[70:] += 10
plt.plot(X, label='X')
plt.plot(y, label='y')
plt.axvline(69, linestyle='--', color='k')
plt.legend();
ss = {}
ss["endog"] = y[:70]
ss["level"] = "llevel"
ss["exog"] = X[:70]
model = UnobservedComponents(**ss)
trained_model = model.fit()
在给定外生变量X[70:] 的情况下,是否可以使用trained_model 来模拟新的时间序列?正如我们拥有arma_process.generate_sample(nsample=100) 一样,我们想知道是否可以这样做:
trained_model.generate_random_series(nsample=100, exog=X[70:])
其背后的动机是,我们可以计算出具有与观察到的y[70:] 一样极端的时间序列的概率(用于识别响应的 p 值大于预测值)。
[编辑]
阅读 Josef 和 cfulton 的 cmets 后,我尝试实现以下内容:
mod1 = UnobservedComponents(np.zeros(y_post), 'llevel', exog=X_post)
mod1.simulate(f_model.params, len(X_post))
但这导致模拟似乎无法将 predicted_mean 的预测跟踪 X_post 作为 exog。这是一个例子:
虽然y_post 蜿蜒在 100 左右,但模拟为 -400。这种方法总是导致 p_value 为 50%。
所以当我尝试使用initial_sate=0 和随机冲击时,结果如下:
现在看来,模拟遵循了预测的平均值及其 95% 的可信区间(正如 cfulton 在下面评论的那样,这实际上是一种错误的方法,它正在取代训练模型的水平方差)。
我尝试使用这种方法只是为了查看我观察到的 p 值。以下是我计算 p 值的方法:
samples = 1000
r = 0
y_post_sum = y_post.sum()
for _ in range(samples):
sim = mod1.simulate(f_model.params, len(X_post), initial_state=0, state_shocks=np.random.normal(size=len(X_post)))
r += sim.sum() >= y_post_sum
print(r / samples)
就上下文而言,这是由 Google 开发的 Causal Impact 模型。由于它是在 R 中实现的,我们一直在尝试在 Python 中复制实现,使用 statsmodels 作为处理时间序列的核心。
我们已经有了一个非常酷的 WIP implementation,但我们仍然需要 p 值来知道实际上我们何时产生了不能由单纯随机性解释的影响(模拟系列并计算那些summation超过y_post.sum()也在Google的模型中实现)。
在我的示例中,我使用了y[70:] += 10。如果我只添加一个而不是十个,Google 的 p 值计算会返回 0.001(y 有影响),而在 Python 的方法中它返回 0.247(没有影响)。
只有当我给y_post加上+5时,模型返回的p_value为0.02并且低于0.05,我们认为y_post有影响。
我正在使用 python3,statsmodels 版本 0.9.0
[EDIT2]
阅读 cfulton 的 cmets 后,我决定全面调试代码以查看发生了什么。这是我发现的:
当我们创建UnobservedComponents 类型的对象时,最终会启动卡尔曼滤波器的表示。默认情况下,它 receives the parameter initial_variance 为 1e6 设置对象的same property。
当我们运行 simulate 方法时,initial_state_cov 值 is created 使用相同的值:
initial_state_cov = (
np.eye(self.k_states, dtype=self.ssm.transition.dtype) *
self.ssm.initial_variance
)
最后,用这个相同的值找到initial_state:
initial_state = np.random.multivariate_normal(
self._initial_state, self._initial_state_cov)
这导致标准差为 1e6 的正态分布。
然后我尝试运行以下命令:
mod1 = UnobservedComponents(np.zeros(len(X_post)), level='llevel', exog=X_post, initial_variance=1)
sim = mod1.simulate(f_model.params, len(X_post))
plt.plot(sim, label='simul')
plt.plot(y_post, label='y')
plt.legend();
print(sim.sum() > y_post.sum())
导致:
然后我测试了 p 值,最后测试了 y_post 中 +1 的变化,模型现在可以正确识别添加的信号。
不过,当我使用 R 的 Google 包中的相同数据进行测试时,p 值仍然关闭。也许这是进一步调整输入以提高其准确性的问题。
【问题讨论】:
-
@Josef 非常感谢这些链接!会试一试!我之前看到过
simulate方法,但被解释为它们只适用于model对象而不是受过训练的对象。我还可以从经过训练的模型中获取参数并构建一个新模型(有点间接,但我仍然希望它有效) -
嗨@Josef,我遵循了你的建议,它似乎有效。还有一些问题,不知道你能不能帮帮我。我用更新的信息编辑了我的问题,如您所见,我必须将
initial_state设置为零并将state_shocks设置为正常值。你知道为什么这是必要的或它意味着什么吗?我遵循了单元测试,它似乎正在工作,但不太明白为什么。 -
模拟在 -400 左右的事实令人担忧,我无法复制。您使用的是哪个版本的 Statsmodels?
-
@cfulton 我正在使用 0.9.0,在
jupyter/datascience-notebook:latestdocker 映像中运行笔记本。尝试在这里重新启动所有内容,但结果仍然与预期相差甚远(有时几乎是 -1000)
标签: python scipy statsmodels