即使在最简单的情况下(干净的正弦波,你的例子),数据看起来很像一条直线(下面的左面板)。只有在去趋势化之后,我们才能看到一点点的曲率(右面板下面,注意非常小的y值),这就是所有假设的算法可以通过的。尤其是在我看来,英国《金融时报》是行不通的。
f = c + sin(om * t
---那么一阶导数和三阶导数
om * cos(om * t)
和
-om^3 * cos(om * t)
如果信号足够简单和干净,这与稳健的数值微分可以用来恢复频率ω。
在下面的演示代码中,我使用了一个SavGol滤波器来获得导数,同时去除了一些添加到信号中的高频噪声(下面的蓝色曲线)(橙色曲线)。可能存在其他(更好的)数值微分方法。
运行示例:
Estimated freq clean signal: 0.009998
Estimated freq noisy signal: 0.009871
我们可以看到,在这个非常简单的情况下,频率恢复正常。
也许可以使用更多的导数和一些线性分解巫毒来恢复多个频率,但我不打算在这里探讨这个问题。
代码:
import numpy as np
import matplotlib.pyplot as plt
'''
I need to caputre a low-frequency oscillation with only 1 time unit of data.
So far, I have not been able to find a way to make the fft resolution < 1.
'''
timeResolution = 10000
mytimes = np.linspace(0, 1, timeResolution)
mypressures = np.sin(2 * np.pi * mytimes / 100)
fft = np.fft.fft(mypressures[:])
T = mytimes[1] - mytimes[0]
N = mypressures.size
# fft of original signal is limitted by the maximum time
f = np.linspace(0, 1 / T, N)
filteredidx = f > 0.001
freq = f[filteredidx][np.argmax(np.abs(fft[filteredidx][:N//2]))]
print('freq bin is is ', f[1] - f[0]) # 1.0
print('frequency is ', freq) # 1.0
print('(real frequency is 0.01)')
import scipy.signal as ss
plt.figure(1)
plt.subplot(121)
plt.plot(mytimes, mypressures)
plt.subplot(122)
plt.plot(mytimes, ss.detrend(mypressures))
plt.figure(2)
mycorrupted = mypressures + 0.00001 * np.random.normal(size=mypressures.shape)
plt.plot(mytimes, ss.detrend(mycorrupted))
plt.plot(mytimes, ss.detrend(mypressures))
width, order = 8999, 3
hw = (width+3) // 2
dsdt = ss.savgol_filter(mypressures, width, order, 1, 1/timeResolution)[hw:-hw]
d3sdt3 = ss.savgol_filter(mypressures, width, order, 3, 1/timeResolution)[hw:-hw]
est_freq_clean = np.nanmean(np.sqrt(-d3sdt3/dsdt) / (2 * np.pi))
dsdt = ss.savgol_filter(mycorrupted, width, order, 1, 1/timeResolution)[hw:-hw]
d3sdt3 = ss.savgol_filter(mycorrupted, width, order, 3, 1/timeResolution)[hw:-hw]
est_freq_noisy = np.nanmean(np.sqrt(-d3sdt3/dsdt) / (2 * np.pi))
print(f"Estimated freq clean signal: {est_freq_clean:10.6f}")
print(f"Estimated freq noisy signal: {est_freq_noisy:10.6f}")