【问题标题】:How to properly scale frequency axis in Fast Fourier Transform?如何在快速傅里叶变换中正确缩放频率轴?
【发布时间】:2019-11-09 21:06:41
【问题描述】:

我正在尝试一些对简单正弦函数进行 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()

根据给定的函数,很明显应该在频率 1 和 5 处有两个尖峰。但是,当我运行此代码时,我得到以下图。

显然,尖峰不在应有的位置。此外,我注意到频率缩放对点数N 以及我设置的间隔限制limit 很敏感。例如,设置N = 2048 会给出以下图表。

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

如何才能使频率轴始终正确缩放?

【问题讨论】:

    标签: python numpy fft frequency


    【解决方案1】:

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

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

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

    有一个突出的频率:f = 0。它是信号平均值的度量,它与零的偏移量。在fft 返回的频谱和fftfreq 的频率范围内,它位于第一个数组索引处。 如果绘制两半,则将频谱四处移动可能是有意义的,因此负半部分位于零分量的左侧,正半部分位于其右侧,这意味着所有值都在升序并准备好绘制。

    fftshift 正是这样做的。但是,如果您无论如何只绘制了一半的频谱,那么您可能根本不费心去做。虽然 if 你这样做了,你必须移动 both 数组:频率和傅立叶分量。在您的代码中,您只改变了频率。这就是峰值出现在频谱错误一侧的原因:您绘制了傅立叶分量,指的是频率的正半部分相对于负半部分,因此右侧的峰值实际上意味着接近于零,而不是在远端。

    您实际上并不需要依赖任何在频率上运行的函数。仅根据fftfreq 的文档就可以直接生成它们的范围:

    from numpy.fft import fft
    from numpy import arange, linspace, sin, pi as π
    from matplotlib import pyplot
    
    def FFT(t, y):
        n = len(t)
        Δ = (max(t) - min(t)) / (n-1)
        k = int(n/2)
        f = arange(k) / (n*Δ)
        Y = abs(fft(y))[:k]
        return (f, Y)
    
    t = linspace(-10, +10, num=1024)
    y = sin(2*π * 5*t) + sin(2*π * t)
    (f, Y) = FFT(t, y)
    pyplot.plot(f, Y)
    pyplot.show()
    

    请注意,NumPy 还为实值数据的常见用例提供了专用函数 rfftrfftfreq

    【讨论】:

    • 现在这是我第一次看到有人在代码中使用 ascii delta。编辑:wtf你实际上可以在Python中输入π,它知道那是什么!?接受我的投票并离开(答案也很好:P)
    • 这篇写的很好。你很好地解释了一切,谢谢!您是否也碰巧知道对N 的依赖和间隔?我认为这只是混叠,但你能确认一下吗?
    • @JohnHennig 太棒了。谢谢你的一切!
    【解决方案2】:
    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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      • 2011-07-12
      • 2017-09-14
      • 2012-12-10
      • 2013-03-31
      相关资源
      最近更新 更多