I am trying to reverse python numpy/scipy's fft, rfft, and dct transforms back into a sum of sine/cosine waves to reconstruct the original dataset. I want to do this because I want to be able to reconstruct the original dataset with more/less sampling points (which I believe may already be covered with scipy.signal.resample) and mainly because I want to extend the sine/cosine series into the future not unlike how a linear regression might be used in some series to give a general idea of future values. I know this is not technically correct, as fft assumes the discrete sample is repeated over all time points, and dct assumes the data is "mirrored", but I think it may have some sort of short term predictive value.
I tried to follow what was written here as a guide to Numpy's decomposition algorithms: http://snowball.millersville.edu/~adecaria/ESCI386P/esci386-lesson17-Fourier-Transforms.pdf
Here is my code:
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()
What this code is supposed to do is:
- Convert all input data-arrays to complex (because dct doesn't output a complex datatype).
- Normalize the Fourier coefficients, as fft() and related transforms don't appear to divide by the number of elements in the dataset.
- Populate an array of lambda functions representing the individual contributions of each Fourier frequency (I assume they are the ordinal positions of the Fourier coefficients)
- Cumulatively sum the individual contributions of each sinusoidal lambda function taken at the new points to be sampled to reconstruct a series
In this particular code, I'm trying to see if my recomposition equals the original series/scipy's inverse decomposition just to make sure I'm doing it right. I think the code works fine, but the underlying formulas it uses for the sine/cosine reconstruction are wrong. Here is the output for this particular code:
Green is my reconstructed values and Orange/Blue are the original values. Clearly, my algorithm is not remaking the series properly. Using amplitude and phase to combine the sine and cosine terms into a single cosine term, as was recommended on other sites, gives a different, but still incorrect, result, most likely due to the subtraction of the sine term that was recommended in the above source. Does anyone know how my formula or code is wrong? I'm thinking it is either in the cos()-sin() part, or something like the frequency not being multiplied by a constant.
*Note: I do know this question is somewhat like: Fourier Series from Discrete Fourier Transform but I don't think the answer to this works for me here.
n
parameter in theifft
function. I've updated my answer to explain also how to correctly pad the frequency-domain signal. – Cris Luengo