使用NumPy FFT计算非整数频率

时间:2018-08-05 01:48:37

标签: python numpy fft

我想使用NumPy FFT计算周期时间序列的频率。例如,假设我的时间序列y定义如下:

import numpy as np
freq = 12.3
x = np.arange(10000)
y = np.cos(x * 2 * np.pi * freq / 10000)

如果频率是整数,则可以使用np.argmax(np.abs(np.fft.fft(y)))进行计算。但是,如果频率不是整数,如何更精确地计算频率?

编辑:为澄清起见,我们不应该知道如何生成时间序列y。上面的代码片段只是一个非整数频率如何出现的人工示例。显然,如果我们已经知道生成时间序列的函数,则不需要FFT即可确定频率。

3 个答案:

答案 0 :(得分:1)

您需要为信号提供更高的分辨率

import numpy as np
freq = 12.3
x = np.arange(100000)  # 10 times more resolution
y = np.cos(x * 2 * np.pi * freq / 10000)  # don't change this

print(np.argmax(np.abs(np.fft.fft(y))) / 10)  # divide by 10
# 12.3

x中数据点的数量必须是除以y的数量的10倍。您可以获得类似的效果:

x = np.arange(10000)
y = np.cos(x * 2 * np.pi * freq / 1000)

print(np.argmax(np.abs(np.fft.fft(y))) / 10) 
# 12.3

如果要用两位小数查找频率,则分辨率需要提高100倍。

freq = 12.34
x = np.arange(10000)  
y = np.cos(x * 2 * np.pi * freq / 100)  # 100 times more resolution

print(np.argmax(np.abs(np.fft.fft(y))) / 100)  # divide by 100
# 12.34

答案 1 :(得分:1)

您可以在计算FFT之前用零填充数据。

例如,这是您的原始计算。找到频率为12.0时最大幅度的傅立叶系数:

In [84]: freq = 12.3

In [85]: x = np.arange(10000)

In [86]: y = np.cos(x * 2 * np.pi * freq / 10000)

In [87]: f = np.fft.fft(y)

In [88]: k = np.argmax(np.abs(f))

In [89]: np.fft.fftfreq(len(f), d=1/10000)[k]
Out[89]: 12.0

现在重新计算傅立叶变换,但将输入的长度填充为原始长度的六倍(您可以根据需要调整该因子)。对于填充信号,最大幅度的傅立叶系数与频率12.333相关:

In [90]: f = np.fft.fft(y, 6*len(y))

In [91]: k = np.argmax(np.abs(f))

In [92]: np.fft.fftfreq(len(f), d=1/10000)[k]
Out[92]: 12.333333333333332

以下是说明填充信号效果的图。信号与上述信号不同;我使用了更短的信号来使用不同的值,以使其更容易看到效果。瓣的形状没有改变,但是采样频率的点数增加了。

plot

该图由以下脚本生成:

import numpy as np
import matplotlib.pyplot as plt

fs = 10
T = 1.4
t = np.arange(T*fs)/fs

freq = 2.6
y = np.cos(2*np.pi*freq*t)

fy = np.fft.fft(y)
magfy = np.abs(fy)
freqs = np.fft.fftfreq(len(fy), d=1/fs)
plt.plot(freqs, magfy, 'd', label='no padding')

for (factor, markersize) in [(2, 9), (16, 4)]:
    fy_padded = np.fft.fft(y, factor*len(y))
    magfy_padded = np.abs(fy_padded)
    freqs_padded = np.fft.fftfreq(len(fy_padded), d=1/fs)
    plt.plot(freqs_padded, magfy_padded, '.', label='padding factor %d' % factor,
             alpha=0.5, markersize=markersize)

plt.xlabel('Frequency')
plt.ylabel('Magnitude of Fourier Coefficient')
plt.grid()
plt.legend(framealpha=1, shadow=True)

plt.show()

答案 2 :(得分:0)

如果S / N允许,您可以尝试使用插值或零填充(等效于整个矢量插值)来潜在地改善频率估计。 Sinc核插值比抛物线插值更准确。