使用傅里叶分析进行时间序列预测

53

我希望使用傅里叶分析来预测已知具有季节性或日常模式的数据。在对时间序列数据运行fft后,我获得了系数。如何使用这些系数进行预测?

我相信FFT假设它接收到的所有数据都构成一个周期,然后,如果我仅使用ifft重新生成数据,我也正在重建我的函数的延续,因此我可以将这些值用于未来值吗?

简单来说:我在t=0,1,2,..10上运行fft,然后使用coef运行ifft,我可以将重新生成的时间序列用于t=11,12,..20吗?

4个回答

62

我知道这个问题对你来说可能已经不再实际,但是对于其他正在寻找答案的人,我在Python中编写了一个非常简单的傅里叶外推示例https://gist.github.com/tartakynov/83f3cd8f44208a1856ce

在运行脚本之前,请确保您已安装所有依赖项(numpy、matplotlib)。随意进行实验。 输入图像描述 P.S. 与傅里叶外推相比,局部平稳小波可能更好。LSW通常用于预测时间序列。傅里叶外推的主要缺点是它只是以N为周期重复您的时间序列,其中N是您的时间序列长度。


所以,抱歉只是想确认我是否理解。 x(蓝线)是观察到的数据吗?外推(红线)是预测吗? - jeffery_the_wind
@jeffery_the_wind 是的,红线是预测结果,蓝色则为观测数据。显然这个例子存在过拟合问题,为了避免这种情况,你可以调整模型中的谐波数量。 - tartakynov
12
这个良好的可视化示例展示了其他帖子中指出的弱点:频域本质上在时间域产生固定周期。上面的红色线性外推只是蓝色(观察到的)线段的开头部分的一个副本,虽然稍微去噪了一下。因此,在任何有意义的短期预测中,需要超过时间单位* h ,其中 h * ≪历史观测数量,只有最显着的“高”频率系数才应该用于外推。可以根据与* h *相关的任意定义来定义“高”频率阈值。 - Special Sauce
1
为了进一步澄清我的先前评论,时间 ~350 的外推红色隆起只是 ~50 处蓝色隆起的复制。如果历史时期在 50 处隆起之前开始,那么第一个预测单位将具有该隆起的副本,这似乎有点愚蠢和武断。因此,通过消除和减轻较低频率成分,我们可以减少历史数据起始点引起的武断性。 - Special Sauce
我对这个脚本有点困惑,主要是这一行 for i in indexes[:1 + n_harm * 2]:。在执行此操作之前,您会按频率值对索引进行排序,以确保获取 n_harm 个最低频率。难道您不想要与最高峰相关的 n_harm 个频率吗?难道索引不应该按 x_freqdom 的绝对值进行排序吗?也许我误解了,但这似乎是去噪的最佳方法。 - Mike
@Mike 你是对的,我想要提取最低频率,因为我建议它们最能描述数据中的周期和周期性,而最高频率通常是噪声。当然,在你的情况下,这个建议可能是错误的。 - tartakynov

30

看起来你想要外推和降噪的结合。

你说你想要将观察到的数据重复多个周期。那么,只需重复观察到的数据即可,无需进行傅里叶分析。

但你还想找“模��”。我猜这意味着找到观察数据中的主频成分。那么,是的,进行傅里叶变换,保留最大系数,消除其余部分。

X = scipy.fft(x)
Y = scipy.zeros(len(X))
Y[important frequencies] = X[important frequencies]

关于周期重复: 设z = [x, x],即信号x的两个周期。那么对于所有的k在{0, 1, ..., N-1}中,有Z[2k] = X[k],其他位置为0。

Z = scipy.zeros(2*len(X))
Z[::2] = X

1
因此,在Z中重复了最重要的系数(在上面的示例中重复了两次),如果使用ifft重新生成我的时间序列,这个新序列将比原始序列更长,并且根据定义将包含预测。 - BBSysDyn
1
我猜你所说的去噪是指挑选最重要的系数。 - BBSysDyn
1
Z = [X[0], 0, X[1], 0, X[2], 0, ..., X[N-1], 0]。如果这就是您的意思,那么是的。而且,保留最重要的系数将会对信号产生“平滑”、“模糊”或“去噪”的效果。 - Steve Tjoa
我刚意识到我可以不断调用 Y[ctr] * (np.cos(xctr2pi/N) + 1jnp.sin(xctr2*pi/N)),其中 x 是新值,Y 是系数,ctr 是系数索引,并将它们相加;这本质上就是预测。你觉得呢? - BBSysDyn
是的,基本上这是在时域中隔离单个复正弦波。但答案将是复数。您还需要组件Y[N-ctr]才能获得实信号。(如果X具有共轭对称性,即X[k] = X*[-k],则x是实信号。) - Steve Tjoa

10
当你在时间序列数据上运行FFT时,你将其转换为频率域。系数乘以系列中的项(正弦和余弦或复指数),每个项具有不同的频率。
外推总是一件危险的事情,但你可以尝试一下。这样做时,你使用过去的信息来预测未来:“通过观察今天来预测明天的天气。”只是要注意风险。
我建议阅读“黑天鹅”

2
我读过《黑天鹅》。我并不是指股票价格。比如说……收集数据,这是非常季节性的,或者著名的太阳黑子数据。所以我说的是一些可预测的东西。 - BBSysDyn
让我再进一步澄清一下:假设我找到了数据中相当显著的一个频率,如何将其与时间域数据中的点联系起来,以便我可以跳到未来并进行预测。 - BBSysDyn
2
+1. 外推是危险的。通常,人们制作模型,并假设未来的行为将像模型所说的那样。模型取决于参数,这些参数是通过现在或过去的观察估计出来的。因此,您并不会“预测”任何事情,您只是拟合一个模型。 - Alexandre C.
@user:这是一个非常困难且可能过于宽泛的问题。 - Alexandre C.
2
@Alexandra C:我在谈论那些已知会重复出现的数据。 - BBSysDyn

3

您可以使用@tartakynov发布的库,并且为了避免在预测中重复完全相同的时间序列(过度拟合),您可以向函数添加一个名为n_param的新参数,并为频率的振幅固定下限h

def fourierExtrapolation(x, n_predict,n_param):

通常您会发现,在一个信号中,一些频率比其他频率具有明显更高的振幅,因此,如果您选择这些频率,就能够分离出信号的周期性。
您可以添加这两行代码,这是由特定的数字n_param确定的。
h=np.sort(x_freqdom)[-n_param]
x_freqdom=[ x_freqdom[i] if np.absolute(x_freqdom[i])>=h else 0 for i in range(len(x_freqdom)) ]

只需添加这个功能,你就能够轻松地预测结果。

关于傅里叶变换的另一篇有用文章: 在R中进行傅里叶变换预测


你提供的链接已经失效了。如果可能的话,你能再次发布一下吗?或者你有其他的链接吗?谢谢。 - dhinar1991
1
@DhivyaNarayanasamy 已修复 ;) - Pablo
非常有趣,但您是否希望在预测时给最近的值更多的权重?也就是说,尝试使用前半部分为平直线而后半部分为斜线的数据进行测试。斜线部分应该比平直部分更加强调。 - anthony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接