[英]Calculate non-integer frequency with 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即可確定頻率。
您需要給信號更多的分辨率
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
您可以在計算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
這是說明填充信號效果的圖。 信號與上述信號不同; 我使用了更短的信號來使用不同的值,以使其更容易看到效果。 瓣的形狀沒有改變,但是采樣頻率的點數增加了。
該圖由以下腳本生成:
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()
如果S / N允許,您可以嘗試使用插值或零填充(等效於整個矢量插值)來潛在地改善頻率估計。 Sinc核插值比拋物線插值更准確。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.