Sympy extract Fourier Series coefficients - python

I am using sympy to carry on some symbolic math manipulations.
Start by creating a Fourier series representation of a rectangular pulse train (duty cycle < 50%), then try to access the multiplying factors, i.e. a_n and b_n of the standard Fourier series.
In a nutshell:
import sympy as sy
from sympy import fourier_series, pi, cos, sin
from sympy.abc import t
from sympy.functions.special.delta_functions import Heaviside
T = sy.symbols('T')
s = fourier_series(Heaviside(t) - Heaviside(t-1/4), (t, 0, 1))
s.truncate(3)
1/π*sin(2πt)+1/π*sin(4πt)+1/π*cos(2πt)+0.25
I would then like to access the coefficients of the base functions. To this extent, I thought I should use as_coefficient(expr).
This produces the expected results in a simpler case:
g = 1/(pi*T)*sin(2*pi*t)
g.as_coefficient(sin(2*pi*t))
1/πT
However, with the object returned by fourier_series, this does not seem to work:
a = s.truncate(3)
a.as_coefficient(sin(2*pi*t))
returns nothing (not even a warning or message).
Other methods like s.as_Add() or s.as_Mul() return both a full expression where the a_n is tied to its sin(2*pi*n*t) term (or b_n to its dual).

The class sympy.series.fourier.FourierSeries has methods to provide sympy.series.sequences.sequence objects with the cosine and sine terms of the series: a0, an and bn.
After computing the series by
import sympy as sym
from sympy import fourier_series
from sympy.abc import t
from sympy.functions.special.delta_functions import Heaviside
s = fourier_series(Heaviside(t) - Heaviside(t-1/4), (t, 0, 1))
The cosine coefficients can be obtained by
s.a0
0.25
and
s.an
.
For the sine coefficients
s.bn
.
So in order to produce lists of the s series coefficients up to a given order, let say 4 that can be done with
def cosine_fourier_coeffs(fourierSeries, order):
### returns a list of fourier series cosine coefficients up to order
out = []
out.append(fourierSeries.a0)
for i in range(1,order):
out.append(fourierSeries.an.coeff(i).subs(t, 0 ) )
return out
def sine_fourier_coeffs(fourierSeries, order):
### returns a list of fourier series sine coefficients up to order
out = []
for i in range(1,order):
out.append(fourierSeries.bn.coeff(i).subs(t, 1/(4* i) ) )
return out
cosine_fourier_coeffs(s, 4), sine_fourier_coeffs(s, 4)
that will return
([0.250000000000000, 1/pi, 0, -1/(3*pi)], [1/pi, 1/pi, 1/(3*pi)])

The method as_coefficient can't handle a sum of terms like 2*sin(x)+3*cos(x): it picks the coefficient only if the given expression (like sin(x)) can be factored out. So, in order to use it you need to separate the series into chunks with one trig function each. This can be done, but it's easier to change the approach:
Use s.truncate(None) to get a generator for the series.
For each term produced by the generator, plug 0 to get cosine coefficient, and plug 1/(4*k) of the interval length to get sine coefficient (here k is the index)
The reason this works: at 0, sine is 0 and cosine is 1; at 1/4 of length, cosine is 0 and sine is 1.
from sympy import fourier_series, pi, cos, sin
from sympy.abc import t
from sympy.functions.special.delta_functions import Heaviside
s = fourier_series(Heaviside(t) - Heaviside(t-1/4), (t, 0, 1))
iter = s.truncate(None)
cosine_coeffs = []
sine_coeffs = [0] # there is no sine term for k = 0
for k in range(0, 4):
term = next(iter)
cosine_coeffs.append(term.subs(t, 0))
if k > 0:
sine_coeffs.append(term.subs(t, 1/(4*k)))
Result:
cosine_coeffs = [0.250000000000000, 1/pi, 0, -1/(3*pi)]
sine_coeffs = [0, 1/pi, 1/pi, 1/(3*pi)]

Related

Calculating cross-correlation with fft returning backwards output

I'm trying to cross correlate two sets of data, by taking the fourier transform of both and multiplying the conjugate of the first fft with the second fft, before transforming back to time space. In order to test my code, I am comparing the output with the output of numpy.correlate. However, when I plot my code, (restricted to a certain window), it seems the two signals go in opposite directions/are mirrored about zero.
This is what my output looks like
My code:
import numpy as np
import pyplot as plt
phl_data = np.sin(np.arange(0, 10, 0.1))
mlac_data = np.cos(np.arange(0, 10, 0.1))
N = phl_data.size
zeroes = np.zeros(N-1)
phl_data = np.append(phl_data, zeroes)
mlac_data = np.append(mlac_data, zeroes)
# cross-correlate x = phl_data, y = mlac_data:
# take FFTs:
phl_fft = np.fft.fft(phl_data)
mlac_fft = np.fft.fft(mlac_data)
# fft of cross-correlation
Cw = np.conj(phl_fft)*mlac_fft
#Cw = np.fft.fftshift(Cw)
# transform back to time space:
Cxy = np.fft.fftshift(np.fft.ifft(Cw))
times = np.append(np.arange(-N+1, 0, dt),np.arange(0, N, dt))
plt.plot(times, Cxy)
plt.xlim(-250, 250)
# test against convolving:
c = np.correlate(phl_data, mlac_data, mode='same')
plt.plot(times, c)
plt.show()
(both data sets have been padded with N-1 zeroes)
The documentation to numpy.correlate explains this:
This function computes the correlation as generally defined in signal processing texts:
c_{av}[k] = sum_n a[n+k] * conj(v[n])
and:
Notes
The definition of correlation above is not unique and sometimes correlation may be defined differently. Another common definition is:
c'_{av}[k] = sum_n a[n] conj(v[n+k])
which is related to c_{av}[k] by c'_{av}[k] = c_{av}[-k].
Thus, there is not a unique definition, and the two common definitions lead to a reversed output.

How to plot Butterworth filter manually?

I want to ask something about the Butterworth filter in Python.I know that this is how you plot the graph for a Lowpass Butterworth filter:
from scipy import signal
import matplotlib.pyplot as plt
b,a=signal.butter(N,fCut,'low',analog=True)
w,h=signal.freqs(b,a)
plt.plot(w,(abs(h)))
Where N is the order and fCut the cut-off frequency.
But I am having problems trying to do it manually,without using 'signal.freqs'.
This means I have to compute the analog transfer function:
H(s)=1/Σ(s-sk),k=1..N,sk=e^((j*pi)((2*k+n-1)/2*n))
Is there a way to do this in python?
Here is the Python code corresponding to the formula:
import numpy as np
def butterTF(s, N, fCut):
sk = np.exp(0.5j*np.pi*(2*np.arange(N) + N + 1)/N)
return 1/np.multiply.reduce(np.subtract.outer(1.j*s/fCut, sk), axis=1)
And to test it:
from scipy import signal
import matplotlib.pyplot as plt
b,a=signal.butter(N,fCut,'low',analog=True)
w,h=signal.freqs(b,a)
plt.figure()
plt.plot(w, np.abs(h))
plt.plot(w, np.abs(butterTF(w, N, fCut)), '.')
plt.grid()
plt.show()
Some explanation:
Calculating sk is straightforward except we need to modify the formula a bit because np.arange(N) gives 0, 1, ... , N-1 instead of 1, 2, ... , N.
Now the tricky part: np.subtract.outer forms a matrix len(s) x N, whose element i, k is the result of 1.j*s[i]/fCut - sk[k]. May be not a very memory-effective way of calculation, especially if N is large, but a relatively fast one.
np.multiply.reduce(..., axis=1) calculates the product of each row of the matrix.

Current iteration in scipy odeint

I am using Scipy's odeint (scipy.integrate.odeint) to solve some ODEs for me, and all is working nice and well. However, I'd now like to include another time-dependent set of data into my calculations, i.e. for t = [0, 1, 2, 3] I've got data z = [0.1, 0.2, 0.25, 0.22] to be included in the calculations. I can pass the vector as an argument, but that gives me the entire vector for every time step. Is there an efficient way of getting the current step (iterator) of the calculation? That way I can obtain z[i] for the i-th time step. Note that z has the length of t, and that both can contain several thousands of elements.
Thanks
A very simple example:
import numpy as np
from scipy.integrate import odeint
def func(y, t, z):
# I'd like to get the i-th element
# of z, corresponding to t[i]
return y+z[i]
result = odeint(func, [0], t, (z,))
The work-around solution for this problem is using the more generic scipy.integrate.ode function. This function has several integration schemes build-in, and you have more control of what happens during each iteration. See the example below:
import numpy as np
from scipy.integrate import ode
def func(t, y, z):
return y+z
t = np.linspace(0, 1.0, 100)
dt = t[1]-t[0]
z = np.random.rand(100)
output = np.empty_like(t)
r = ode(func).set_integrator("dop853")
r.set_initial_value(0, 0).set_f_params(z[0])
for i in xrange(len(t)):
r.set_f_params(z[i])
r.integrate(r.t+dt)
output[i] = r.y
During each iteration, the solver's value of z is updated accordingly.
For ode solvers, you can use an interpolation for time-varying inputs. In this case:
import numpy as np
from scipy.integrate import odeint
from scipy.interpolate import interp1d
t_arr = np.array([0,1,2,3])
z_arr = np.array([0.1, 0.2, 0.25, 0.22])
finterp = interp1d(t_arr,z_arr,fill_value='extrapolate') #create interpolation function
def func(y,t,z):
print(t)
zt = finterp(t) # call interpolation at time t
return y+zt
result = odeint(func, [0], t_arr, (z_arr,))
However, the solver in odeint may request time instants beyond t_arr (in your case at 3.028), so you have to specify values for z beyond t=3 or allow for extrapolation with fill_value (as shown above). Just be careful when selecting a kind of interpolation, to reflect the behavior that you expect.

Is there a Python equivalent to the mahalanobis() function in R? If not, how can I implement it?

I have the following code in R that calculates the mahalanobis distance on the Iris dataset and returns a numeric vector with 150 values, one for every observation in the dataset.
x=read.csv("Iris Data.csv")
mean<-colMeans(x)
Sx<-cov(x)
D2<-mahalanobis(x,mean,Sx)
I tried to implement the same in Python using 'scipy.spatial.distance.mahalanobis(u, v, VI)' function, but it seems this function takes only one-dimensional arrays as parameters.
I used the Iris dataset from R, I suppose it is the same you are using.
First, these is my R benchmark, for comparison:
x <- read.csv("IrisData.csv")
x <- x[,c(2,3,4,5)]
mean<-colMeans(x)
Sx<-cov(x)
D2<-mahalanobis(x,mean,Sx)
Then, in python you can use:
from scipy.spatial.distance import mahalanobis
import scipy as sp
import pandas as pd
x = pd.read_csv('IrisData.csv')
x = x.ix[:,1:]
Sx = x.cov().values
Sx = sp.linalg.inv(Sx)
mean = x.mean().values
def mahalanobisR(X,meanCol,IC):
m = []
for i in range(X.shape[0]):
m.append(mahalanobis(X.iloc[i,:],meanCol,IC) ** 2)
return(m)
mR = mahalanobisR(x,mean,Sx)
I defined a function so you can use it in other sets, (observe I use pandas DataFrames as inputs)
Comparing results:
In R
> D2[c(1,2,3,4,5)]
[1] 2.134468 2.849119 2.081339 2.452382 2.462155
In Python:
In [43]: mR[0:5]
Out[45]:
[2.1344679233248431,
2.8491186861585733,
2.0813386639577991,
2.4523816316796712,
2.4621545347140477]
Just be careful that what you get in R is the squared Mahalanobis distance.
A simpler solution would be:
from scipy.spatial.distance import cdist
x = ...
mean = x.mean(axis=0).reshape(1, -1) # make sure 2D
vi = np.linalg.inv(np.cov(x.T))
cdist(mean, x, 'mahalanobis', VI=vi)

Scipy: Integration of Hermite function with quadrature weights

I want to integrate the product of two time- and frequency-shifted Hermite functions using scipy.integrate.quad.
However, since large order-polynomials are included, there are numerical errors occuring. Here's my Code:
import numpy as np
import scipy.integrate
import scipy.special as sp
from math import pi
def makeFuncs():
# Create the 0th, 4th, 8th, 12th and 16th order hermite function
return [lambda t, n=n: np.exp(-0.5*t**2)*sp.hermite(n)(t) for n in np.arange(5)*4]
def ambgfun(funcs, i, k, tau, f):
# Integrate f1(t)*f2(t+tau)*exp(-j2pift) over t from -inf to inf
f1 = funcs[i]
f2 = funcs[k]
func = lambda t: np.real(f1(t) * f2(t+tau) * np.exp(-1j*(2*pi)*f*t))
return scipy.integrate.quad(func, -np.inf, np.inf)
def main():
f = makeFuncs()
print "A00(0,0):", ambgfun(f, 0, 0, 0, 0)
print "A01(0,0):", ambgfun(f, 0, 1, 0, 0)
print "A34(0,0):", ambgfun(f, 3, 4, 0, 0)
if __name__ == '__main__':
main()
The hermite functions are orthogonal, thus all integrals should be equal to zero. However, they are not, as the output shows:
A00(0,0): (1.7724538509055159, 1.4202636805184462e-08)
A01(0,0): (8.465450562766819e-16, 8.862237123626351e-09)
A34(0,0): (-10.1875, 26.317246925873935)
How can I make this calculation more accurate? The hermite-function from scipy contain a weights variable which should be used for Gaussian Quadrature, as given in the documentation (http://docs.scipy.org/doc/scipy/reference/special.html#orthogonal-polynomials). However, I have not found a hint in the docs how to use these weights.
I hope you can help :)
Thanks, Max
The answer is that the result you get is numerically as close to zero as it gets. I don' think it's really possible to get much better results if you work with floating point numbers --- you are facing a general problem in numerical integration.
Consider this:
import numpy as np
from scipy import integrate, special
f = lambda t: np.exp(-t**2) * special.eval_hermite(12, t) * special.eval_hermite(16, t)
abs_ig, abs_err = integrate.quad(lambda t: abs(f(t)), -np.inf, np.inf)
ig, err = integrate.quad(f, -np.inf, np.inf)
print ig
# -10.203125
print abs_ig
# 2.22488114805e+15
print ig / abs_ig, err / abs_ig
# -4.58591912155e-15 1.18053770382e-14
The value of the integrand has therefore been computed to an accuracy comparable to the floating point epsilon. Because of the rounding error in subtracting values of a large-magnitude oscillating integrand, it's not really possible to get better results.
So how to proceed? In my experience, what you'd need to do now is to approach the problem not numerically, but analytically. Importantly, the Fourier transform of Hermite polynomials times the weight function is known, so you can work in the Fourier space all the time here.

Categories

Resources