如何在快速傅立叶变换中正确缩放频率轴?

时间:2019-06-27 20:05:26

标签: python numpy fft frequency

我正在尝试使用简单正弦函数FFT的一些示例代码。下面是代码

import numpy as np
from matplotlib import pyplot as plt

N = 1024
limit = 10
x = np.linspace(-limit, limit, N)
dx = x[1] - x[0]
y = np.sin(2 * np.pi * 5 * x) + np.sin(2 * np.pi * x)
Y = np.abs(np.fft.fft(y) ** 2)
z = fft.fftshift(np.fft.fftfreq(N, dx))
plt.plot(z[int(N/2):], Y[int(N/2):])
plt.show()

从给定的函数formula中可以看出,在频率1和5处应该有两个尖峰。但是,当我运行这段代码时,得到以下图。

enter image description here

显然,峰值不在应有的位置。另外,我注意到频率缩放对点N的数量以及我设定的limit的间隔限制很敏感。例如,设置N = 2048可以得到以下图表。

enter image description here

如您所见,尖峰的位置已更改。现在保持N = 1024并设置limit = 100也会改变结果。

enter image description here

如何做到这一点,以便可以始终正确缩放频率轴?

2 个答案:

答案 0 :(得分:5)

fftfreq按以下顺序返回频率范围:从最低到最高的正频率,然后以绝对值的相反顺序返回负频率。 (您通常只想绘制一半,就像在代码中一样。)请注意,该函数实际上实际上需要很少了解数据:只需知道采样数及其在时域中的间距即可。

fft执行实际的(快速)傅立叶变换。它对输入采样进行相同的假设,即等距,并以与fftfreq相同的顺序输出傅立叶分量。它不在乎实际的频率值:采样间隔不会作为参数传递。

但是它确实接受复数作为输入。实际上,这种情况很少见。输入通常是实数样本,如上例所示。在那种情况下,傅立叶变换具有特殊的性质:它在频域中是对称的,即f−f的值相同。因此,绘制频谱的两个半部通常是没有意义的,因为它们包含相同的信息。

有一种突出的频率:f = 0。它是信号平均值(从零开始的偏移量)的度量。在fft返回的频谱和fftfreq的频率范围中,它位于第一个数组索引处。 如果同时绘制两个半部,则可能需要左右移动频谱,以使负半部位于零分量的左侧,正半部位于零分量的右侧,这意味着所有值都在升序并准备绘制。

fftshift正是这样做的。但是,如果您只绘制了频谱的一半,那么您也可以完全不用这样做。尽管您执行了 if ,但是您必须同时移动两个数组:频率和傅立叶分量。在您的代码中,您仅移动了频率。这就是峰值最终出现在频谱的错误一侧的原因:您绘制的傅立叶分量是指频率的正半部分相对于负半部分,因此,右侧的峰实际上意味着接近零,而不是在远端。

您实际上并不需要依赖那些在频率上运行的功能。仅根据fftfreq的文档即可生成范围:

from numpy.fft import fft
from numpy import arange, linspace, sin, pi as π
from matplotlib import pyplot

t = linspace(-10, +10, num=1024)
y = sin(2*π * 5*t) + sin(2*π * t)

def FFT(t, y):
    n = len(t)
    Δ = (max(t) - min(t)) / (n-1)
    f = arange(int(n/2)) / (n*Δ)
    Y = 2/n * abs(fft(y))[:int(n/2)]
    return (f, Y)

(f, Y) = FFT(t, y)
pyplot.plot(f, Y)
pyplot.show()

答案 1 :(得分:1)

import numpy as np
from matplotlib import pyplot as plt

N = 1024
limit = 10

x = np.linspace(-limit, limit, N)
dx = x[1] - x[0]

y = np.sin(2 * np.pi * 5 * x) + np.sin(2 * np.pi * x)
yhat = np.fft.fft(y)

Y = np.abs(yhat)
freq = np.linspace(0.0, 1.0/(2*dx), N//2)

plt.plot(freq, Y[0:N//2]*(2/N))
plt.show()

enter image description here