问题描述
我有一组数据
d=np.array([1,2,3,4,5])
经过傅里叶变换
f=np.fft.fft(d)
查看反傅里叶变换的结果:
np.fft.ifft(f).real
array([1., 2., 3., 4., 5.])
可以看到和原始数据符合的很好
我的问题是,傅里叶变换是变成了一组三角函数的组合,那么反傅里叶变换应该可以得到一个无穷数列,而非有限的数组。请问用numpy的fft库如何得到后续的数据?
解决方案
这是一个非常经典且深刻的问题。要解答这个问题,我们需要先理解离散傅里叶变换(DFT/FFT)的隐含假设。
1. 为什么 ifft 只能得到有限数组?
当你对有限长度的离散数据 [1, 2, 3, 4, 5] 进行 np.fft.fft 时,数学上它隐含了一个重要假设:你的原始信号是周期性的。也就是说,FFT 认为你的真实数据是 ..., 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ... 这样无限循环的。
因此,如果你想问“后续的数据是什么”,在 FFT 的逻辑里,第6个数据就是1,第7个数据就是2。np.fft.ifft 默认只返回一个周期(即你输入的长度 N=5)的数据,因为它知道后面的数据只是前面的重复。
2. 如何得到连续的“无穷数列”?
正如你所说,傅里叶变换的结果是一组三角函数的组合。如果你想打破离散数组的限制,得到任意时间点 $t$(比如 $t=5.5$ 或 $t=100$)的值,你不能直接依赖 ifft 函数,而是需要提取 FFT 结果中的振幅和相位,手动写出三角函数的求和公式。
离散傅里叶逆变换的数学公式为:
$$ x(t) = \frac{1}{N} \sum_{k=0}^{N-1} X[k] \cdot e^{i 2\pi k t / N} $$
利用欧拉公式展开为实数形式(三角函数组合),我们可以用 Python 代码手动还原这个无穷数列:
import numpy as np
import matplotlib.pyplot as plt
d = np.array([1, 2, 3, 4, 5])
f = np.fft.fft(d)
N = len(d)
# 定义你想要获取数据的任意时间点 t(可以是连续的,也可以是超出原始范围的)
# 原始数据的索引相当于 t = 0, 1, 2, 3, 4
t_continuous = np.linspace(0, 10, 500) # 预测到 t=10
# 手动构建三角函数组合
y_continuous = np.zeros_like(t_continuous, dtype=float)
for k in range(N):
# 提取复数的实部和虚部
real_part = f[k].real
imag_part = f[k].imag
# 累加每个频率成分的三角函数
# 注意:e^{ix} = cos(x) + i*sin(x)
angle = 2 * np.pi * k * t_continuous / N
y_continuous += (real_part * np.cos(angle) - imag_part * np.sin(angle)) / N
# 打印部分后续数据 (例如 t=5, 6, 7)
print("t=5 的值:", y_continuous[np.abs(t_continuous - 5.0).argmin()]) # 结果接近 1.0
print("t=6 的值:", y_continuous[np.abs(t_continuous - 6.0).argmin()]) # 结果接近 2.0
3. 在量化交易(如 JoinQuant)中的应用启示
在量化分析中,很多研究员试图用 FFT 对股票的收盘价进行变换,然后向未来外推以预测股价。但根据上述原理,你会发现:
- 直接外推是无效的:因为 FFT 拟合出的三角函数组合是严格周期的。如果你用过去 100 天的数据做 FFT 并外推,它只会把过去 100 天的走势在未来完美重复一遍,这显然不符合金融市场的规律。
- 正确的用法(滤波与特征提取):在 JoinQuant 等平台上,FFT 通常用于频域滤波。比如,将高频部分(代表短期噪音)的复数系数置为 0,然后再用
ifft变回时域,这样就能得到一条平滑的趋势线(类似于均线,但没有均线的滞后性)。或者提取能量最大的几个低频周期,作为机器学习模型的输入特征。