[英]How to interpret the results of the Discrete Fourier Transform (FFT) in Python
There are many questions on this topic, and I have cycled through a lot of them getting conceptual pointers on handling frequencies ( here and here ), documentation on numpy functions ( here ), how-to information on extracting magnitude and phase ( here ), and stepping outside the site, for example this or this .关于这个主题有很多问题,我已经循环浏览了很多问题,以获得关于处理频率的概念指针(此处和此处)、有关 numpy 函数的文档(此处)、有关提取幅度和相位的操作方法信息(此处),并走出网站,例如this或this 。
However, only the painful "proving it" to myself with simple examples and checking the output of different functions contrasted to their manual implementation has given me a bit of an idea.然而,只有通过简单的例子痛苦地“证明”给自己,并检查不同功能的 output 与手动实现的对比,这让我有了一点想法。
The answer attempts to document and share details related to the DFT in Python that may constitute barriers of entry if not explained in simple terms.答案试图在 Python 中记录和分享与 DFT 相关的细节,如果不简单解释,这些细节可能构成进入壁垒。
The DFT (FFT being its algorithmic computation) is a dot product between a finite discrete number of samples N of an analogue signal s (t) (a function of time or space) and a set of basis vectors of complex exponentials (sin and cos functions). DFT(FFT 是其算法计算)是模拟信号s (t)(时间或空间的 function)的有限离散样本N与复指数的一组基向量(sin 和 cos)之间的点积功能)。 Although the sample is naturally finite and may show no periodicity, it is implicitly thought of as a periodically repeating discrete function.
尽管样本自然是有限的并且可能没有周期性,但它被隐含地认为是周期性重复的离散 function。 Even when dealing with real-valued signals (the usual situation) it is convenient to work with complex numbers (Euler's equation).
即使在处理实值信号(通常情况)时,使用复数(欧拉方程)也很方便。 It may be intimidating to implement the function on a signal with
np.fft.fft(s)
only to get the output coefficients in complex numbers and get stuck in their interpretation.在带有
np.fft.fft(s)
的信号上实现 function 可能会令人生畏,只是为了获得复数的 output 系数并陷入其解释中。 Some steps are essential:一些步骤是必不可少的:
0 to N - 1
and can be thought of as having units of cycles / set (the set being the N
samples of the signal s
).0 to N - 1
并且可以被认为具有周期/组的单位(该组是信号s
的N
个样本)。 I will omit discussing the Nyquist limit, but for real signals the frequencies form a mirror image after N / 2 , and given as negative decreasing values after that point.N = 5
times the frequencies are: np.fft.fftfreq(5)
, yielding [ 0, 0.2, 0.4, -0.4, -0.2]
, ie [0/5, 1/5, 2/5, -2/5, -1/5]
.N = 5
次采样,则频率为: np.fft.fftfreq(5)
,产生[ 0, 0.2, 0.4, -0.4, -0.2]
,即[0/5, 1/5, 2/5, -2/5, -1/5]
。np.fft.fftfreq(5, d=T)
: If the analogue signal s
is sampled 5
times at equidistant intervals T = 1/2
sec for a total sample of NT = 5 x 1/2 sec
, the normalized frequencies will be np.fft.fftfreq(5, d = 1/2)
, yielding [0 0.4 0.8 -0.8 -0.4]
or [0/NT, 1/NT, 2/NT, -2/NT, -1/NT]
.np.fft.fftfreq(5, d=T)
:如果模拟信号s
以等距间隔T = 1/2
秒采样5
次,总采样为NT = 5 x 1/2 sec
,归一化频率将为np.fft.fftfreq(5, d = 1/2)
,产生[0 0.4 0.8 -0.8 -0.4]
或[0/NT, 1/NT, 2/NT, -2/NT, -1/NT]
。NT
is the total duration for which the signal was sampled.NT
是对信号进行采样的总持续时间。 The index k
does result in multiples of a fundamental frequency ( ω-naught ) corresponding to k = 1
- the frequency of (co-)sine wave that completes exactly one oscillation over NT
( here ).k
确实导致基频 ( ω-naught ) 的倍数,对应于k = 1
- (余) 正弦波的频率,该频率恰好在NT
上完成一次振荡( 此处)。S = fft.fft(s)
, the magnitude of the output coefficients ( here ) is just the Euclidean norm of the complex numbers in the output coefficients adjusted for the symmetry in real signals ( x 2
) and for the number of samples 1/N
: magnitudes = 1/N * np.abs(S)
S = fft.fft(s)
, the magnitude of the output coefficients ( here ) is just the Euclidean norm of the complex numbers in the output coefficients adjusted for the symmetry in real signals ( x 2
) and for样本数1/N
: magnitudes = 1/N * np.abs(S)
np.fft.fftfreq(N)
, or more expediently to incorporate the actual analogue frequency units, frequencies = np.fft.fftfreq(N, d=T)
.np.fft.fftfreq(N)
,或者更方便地结合实际的模拟频率单位, frequencies = np.fft.fftfreq(N, d=T)
。phase = np.arctan(np.imag(S)/np.real(S))
phase = np.arctan(np.imag(S)/np.real(S))
k
corresponding the frequency with the highest magnitude can be accomplished as index = np.argmax(np.abs(S))
.k
可以通过index = np.argmax(np.abs(S))
来完成。 To find the 4
indices with the highest magnitude, for example, the call is indices = np.argpartition(S,-4)[-4:]
.4
索引,调用是indices = np.argpartition(S,-4)[-4:]
。S[index]
with frequency freq_max = np.fft.fftfreq(N, d=T)[index]
.S[index]
频率freq_max = np.fft.fftfreq(N, d=T)[index]
。 Reproducing s
through sines and cosines (p.150 in here ):通过正弦和余弦再现
s
( 此处为第 150 页):
Re = np.real(S[index])
Im = np.imag(S[index])
s_recon = Re * 2/N * np.cos(-2 * np.pi * freq_max * t) + abs(Im) * 2/N * np.sin(-2 * np.pi * freq_max * t)
Here is a complete example:这是一个完整的例子:
import numpy as np
import matplotlib.pyplot as plt
N = 10000 # Sample points
T = 1/5000 # Spacing
# Total duration N * T= 2
t = np.linspace(0.0, N*T, N, endpoint=False) # Time: Vector of 10,000 elements from 0 to N*T=2.
frequency = np.fft.fftfreq(t.size, d=T) # Normalized Fourier frequencies in spectrum.
f0 = 25 # Frequency of the sampled wave
phi = np.pi/8 # Phase
A = 50 # Amplitude
s = A * np.cos(2 * np.pi * f0 * t + phi) # Signal
S = np.fft.fft(s) # Unnormalized FFT
index = np.argmax(np.abs(S))
print(S[index])
magnitude = np.abs(S[index]) * 2/N
freq_max = frequency[index]
phase = np.arctan(np.imag(S[index])/np.real(S[index]))
print(f"magnitude: {magnitude}, freq_max: {freq_max}, phase: {phase}")
print(phi)
fig, [ax1,ax2] = plt.subplots(nrows=2, ncols=1, figsize=(10, 5))
ax1.plot(t,s, linewidth=0.5, linestyle='-', color='r', marker='o', markersize=1,markerfacecolor=(1, 0, 0, 0.1))
ax1.set_xlim([0, .31])
ax1.set_ylim([-51,51])
ax2.plot(frequency[0:N//2], 2/N * np.abs(S[0:N//2]), '.', color='xkcd:lightish blue', label='amplitude spectrum')
plt.xlim([0, 100])
plt.show()
Re = np.real(S[index])
Im = np.imag(S[index])
s_recon = Re*2/N * np.cos(-2 * np.pi * freq_max * t) + abs(Im)*2/N * np.sin(-2 * np.pi * freq_max * t)
fig = plt.figure(figsize=(10, 2.5))
plt.xlim(0,0.3)
plt.ylim(-51,51)
plt.plot(t,s_recon, linewidth=0.5, linestyle='-', color='r', marker='o', markersize=1,markerfacecolor=(1, 0, 0, 0.1))
plt.show()
s.all() == s_recon.all()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.