简体   繁体   English

使用NumPy FFT计算非整数频率

[英]Calculate non-integer frequency with NumPy FFT

I would like to calculate the frequency of a periodic time series using NumPy FFT. 我想使用NumPy FFT计算周期时间序列的频率。 As an example, let's say my time series y is defined as follows: 例如,假设我的时间序列y定义如下:

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

If the frequency is an integer, I can calculate it using np.argmax(np.abs(np.fft.fft(y))) . 如果频率是整数,则可以使用np.argmax(np.abs(np.fft.fft(y))) However, in case the frequency is not an integer, how do I calculate the frequency with more precision? 但是,如果频率不是整数,如何更精确地计算频率?

EDIT: To clarify, we are not supposed to know how the time series y is generated. 编辑:为澄清起见,我们不应该知道如何生成时间序列y The above code snippet is just an artificial example of how a non-integer frequency could come up. 上面的代码片段只是一个非整数频率如何出现的人工示例。 Obviously if we already know the function that generates the time series, we don't need FFT to determine the frequency. 显然,如果我们已经知道生成时间序列的函数,则不需要FFT即可确定频率。

You need to give your signal more resolution 您需要给信号更多的分辨率

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

The number of data points in x need to be 10 times more than the number you divide y with. x的数据点数量必须是y除以的数量的10倍。 You could get the same effect like this: 您可以获得类似的效果:

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

If you want to find the frequency with two decimals the resolution needs to be 100 times more. 如果要用两位小数查找频率,则分辨率需要提高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

You can pad the data with zeros before computing the FFT. 您可以在计算FFT之前用零填充数据。

For example, here's your original calculation. 例如,这是您的原始计算。 It finds the Fourier coefficient with the maximum magnitude at frequency 12.0: 它找到频率为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

Now recompute the Fourier transform, but pad the input to have a length of six times the original length (you can adjust that factor as needed). 现在重新计算傅立叶变换,但是将输入的长度填充为原始长度的六倍(您可以根据需要调整该因子)。 With the padded signal the Fourier coefficient with maximum magnitude is associated with frequency 12.333: 对于填充信号,最大幅度的傅立叶系数与频率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

Here's a plot that illustrates the effect of padding the signal. 这是说明填充信号效果的图。 The signal is not the same as above; 信号与上述信号不同; I used different values with a much shorter signal to make it easier to see the effect. 我使用了更短的信号来使用不同的值,以使其更容易看到效果。 The shapes of the lobes are not changed, but the number of points at which the frequency is sampled is increased. 瓣的形状没有改变,但是采样频率的点数增加了。

情节

The plot is generated by the following script: 该图由以下脚本生成:

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()

You can try using either interpolation or zero-padding (which is equivalent to entire vector interpolation) to potentially improve your frequency estimation, if the S/N allows. 如果S / N允许,您可以尝试使用插值或零填充(等效于整个矢量插值)来潜在地改善频率估计。 Sinc kernel interpolation is more accurate than parabolic interpolation. Sinc核插值比抛物线插值更准确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM