我正在尝试反转python numpy / scipy的fft,rfft和dct转换回正弦/余弦波的总和,以重建原始数据集。我之所以这样做,是因为我希望能够使用更多/更少的采样点(我相信可能已经被scipy.signal.resample覆盖)来重建原始数据集,并且主要是因为我想将正弦/余弦序列扩展为未来与在某些系列中如何使用线性回归来给出对未来价值的一般理解没有什么不同。我知道这在技术上是不正确的,因为fft假设离散样本在所有时间点都重复,而dct假设数据是“镜像的”,但我认为它可能具有某种短期的预测价值。
我试图遵循此处编写的内容作为Numpy分解算法的指南: http://snowball.millersville.edu/~adecaria/ESCI386P/esci386-lesson17-Fourier-Transforms.pdf
这是我的代码:
import numpy as np
from scipy.fftpack import fft,ifft,dct,idct,rfft,irfft
import matplotlib.pyplot as plt
def reconstructSeries(transformedVals,newxvals):
transformedVals=transformedVals.astype('complex128')
transformedVals=transformedVals/len(transformedVals) #for some reason, numpy does not normalize the values it has, so I have to do it here.
reconstructedVals=np.zeros(len(newxvals))
series=[]
# perhaps [:len(transformedVals)//2] ?
for frequency,val in enumerate(transformedVals):
#the position of the coefficient is the frequency (in radians)
#amplitude=np.sqrt(np.real(val)**2+np.imag(val)**2)
#phase=np.arctan(np.imag(val)/np.real(val))
series.append(lambda x: np.real(val)*np.cos(frequency*newxvals)-np.imag(val)*np.sin(frequency*newxvals))
#series.append(lambda x: amplitude*np.cos(2*np.pi*frequency*newxvals+phase)) #this is in radians to accomidate phase and the default cosine function
reconstructedVals=reconstructedVals+np.array(series[frequency](newxvals))
return reconstructedVals,series
#y=np.arange(250)
y=np.cos(np.arange(250)+5)
yf = fft(y) #this can be rfft or dct as well
myyvalues,sinosoidseries=reconstructSeries(yf,np.arange(250))
plt.plot(ifft(yf));plt.plot(y);plt.plot(myyvalues);plt.show()
这段代码应该做的是:
在这个特定的代码中,我试图查看我的重构是否等于原始序列/ scipy的逆分解,以确保我做对了。我认为代码可以正常工作,但是用于正弦/余弦重构的基本公式是错误的。这是此特定代码的输出:
绿色是我的重构值,橙色/蓝色是原始值。显然,我的算法无法正确地重新制作系列。如在其他站点上所建议的那样,使用幅度和相位将正弦和余弦项组合为单个余弦项,会得出不同但仍不正确的结果,这很可能是由于上述建议中减去了正弦项资源。有人知道我的公式或代码有错吗?我想它要么在cos()-sin()部分,要么是频率未乘以常数之类的东西。
*注意:我确实知道这个问题有点像: Fourier Series from Discrete Fourier Transform 但我认为这里的答案不适合我。
答案 0 :(得分:1)
我在代码中看到的错误在于复数乘法:您将频域样本的实数分量与cos相乘,并将虚数分量与sin相乘。这不是复杂乘法的工作原理。您需要将复数样本值乘以复数cos + i sin。乘以a + ib和c + id的复数可得到ac-bd + iad + ibc,而不是ac + bd。
编辑:如何用零填充频域进行插值
The SciPy ifft
function有一个参数n
,可用于在转换之前用零填充数组。不要使用此参数。它将零添加到信号的末尾,破坏了信号的对称性,因此通常会产生非真实的结果。
DFT(fft
计算得出)的频率为k
= 0
... N-1
。但是k
是周期性的,这意味着k=N-1
与k=-1
相同。对于实值时域信号,我们需要在傅立叶域中保留k=0
周围的(复共轭)对称性,这意味着k=1
和{ {1}}必须保持这种对称性(以及k=-1
和k=2
等)。
当填充零时,我们增加该值k=-2
,因此我们还更改了N
在数组中的位置(因为它在k=-1
处,增加k=N-1
意味着这个位置移动)。
因此,填充必须在数组中间添加零,以便保留数组开头和结尾的原始值。数组的中间位于N
和(N+1)//2-1
之间:
(N+1)//2
请注意,时域信号如何保持不变,但样本数量却翻倍。
还要注意这是如何推断的:如果扩展构成N = 250
y = np.cos(np.arange(N)+5)
yf = fft(y)
yf = np.concatenate((yf[:(N+1)//2], np.zeros(N), yf[(N+1)//2:]))
y2 = ifft(yf)
plt.subplot(2,1,1)
plt.plot(y,'.-')
plt.subplot(2,1,2)
plt.plot(y2,'.-')
plt.show()
的正弦波和余弦波,则会从信号的开头重建值,就像y
以此方式重建一样是周期性的。即y
,y[N]==y[0]
等