我正在使用numpy和matplotlib来分析我的模拟中的数据输出。有一个(明显的)不一致,我找不到根源。它是以下内容:
我的信号具有给定的能量a ^ 2~1。当我使用rfft进行FFT并计算傅里叶空间中的能量时,它会显着增大。为了无法提供我的数据的详细信息等,这里有一个简单的sin波的例子:
from pylab import *
xx=np.linspace(0.,2*pi,128)
a=np.zeros(128)
for i in range(0,128):
a[i]=sin(xx[i])
aft=rfft(a)
print mean(abs(aft)**2),mean(a**2)
原则上两个数字应该相同(至少在数字意义上),但这是我从这段代码中得到的:
62.523081632 0.49609375
我试图通过numpy.fft文档但找不到任何内容。这里的搜索给出了以下内容,但我无法理解那里的解释:
Big FFT amplitude difference between the existing (synthesized) signal and the filtered signal
我错过了什么/误会?在这方面的任何帮助/指针将不胜感激。
谢谢!
答案 0 :(得分:8)
亨利在非规范化部分是正确的,但还有一点,因为你使用的是rfft
,而不是fft
。以下是他的回答:
>>> x = np.linspace(0, 2 * np.pi, 128)
>>> y = 1 - np.sin(x)
>>> fft = np.fft.fft(y)
>>> np.mean((fft * fft.conj()).real)
191.49999999999991
>>> np.mean(y**2)
1.4960937500000004
>>> fft = fft / np.sqrt(len(fft))
>>> np.mean((fft * fft.conj()).real)
1.4960937499999991
但是如果你现在尝试使用rfft
,那么事情就不会有用了:
>>> rfft = np.fft.rfft(y)
>>> np.mean((rfft * rfft.conj()).real)
314.58462009358772
>>> rfft /= np.sqrt(len(rfft))
>>> np.mean((rfft * rfft.conj()).real)
4.8397633860551954
65
>>> np.mean((rfft * rfft.conj()).real) / len(rfft)
4.8397633860551954
但以下方法可以正常使用:
>>> (rfft[0] * rfft[0].conj() +
... 2 * np.sum(rfft[1:] * rfft[1:].conj())).real / len(y)
1.4960937873636722
当您使用rfft
时,您获得的数据不恰当是数据的DFT,而只是正数的一半,因为负数将与它对称。要计算均值,您需要考虑除DC分量之外的每个值两次,这是最后一行代码所做的。
答案 1 :(得分:3)
在大多数FFT库中,各种DFT风格都不是orthogonal。 numpy.fft
库仅在逆变换期间应用必要的标准化。
考虑Wikipedia description of the DFT;逆DFT具有DFT不具有的1 / N项(其中N是变换的长度)。要生成DFT的正交版本,需要将非标准化DFT的结果缩放1 / sqrt(N)。在这种情况下,变换是正交的(也就是说,如果我们将正交DFT定义为F,则逆DFT是F的共轭或英调,转置)。
在您的情况下,您可以通过aft
简单地缩放1.0/sqrt(len(a))
来获得正确的答案(请注意,N是从变换的长度中找到的;真正的FFT只会抛出大约一半的值,所以a
的长度很重要。)
我怀疑离开规范化直到结束的原因是在大多数情况下,它没关系,因此您节省了两次进行规范化的计算成本。实际上,非常快速的FFTW library不会在任何一个方向上进行任何规范化,而是完全由用户来处理。
编辑:为了清楚起见,上面的解释并不完全正确。使用这种简单的缩放不会得到正确的答案,因为在你的情况下,DC分量将被添加两次,尽管1.0/sqrt(len(a))
仍然是产生单一变换的正确缩放。