Plot a special integral function - sympy

I am a newbye on Sympy.
I try to plot the function:
plot(Integral((t*ln(abs(2+t)))/((1+t**2)),(t,0,x)))
The important check to exercise is to look around the"singular" point of the integrating function.
I have a strange behaviour of tool on -2 bound.
Does it exist (into Sympy) a different plotting methods to obtain the global function behaviour without split the integral on 2 pieces ?
What does it happen on -2 bound that collapse the Sympy algorithm?
Integration on over (t,-3,-1) works.
Thanks to all.

Sadly, this is a case where the plotting module in unable to evaluate the expression. We can workaround it: we use sympy's lambdify to convert the symbolic expression to a numerical function. Then we use numpy and matplotlib to create a plot:
import matplotlib.pyplot as plt
import numpy as np
expr = Integral((t*ln(abs(2+t)))/((1+t**2)),(t,0,x))
f1 = lambdify([x], expr)
# since the expression contains an integral, we need to vectorize
# the numerical function so that it will automatically evaluate
# a numpy array
f1 = np.vectorize(f1)
f2 = lambdify([x], expr.diff(x))
f2 = np.vectorize(f2)
xx = np.linspace(-4, 0, 1000)
yy1 = f1(xx)
yy2 = f2(xx)
fig, axs = plt.subplots(2, 1)
def grid_labels(ax, ylabel):
ax.grid(which='major', axis='both', linewidth=0.75,
linestyle='-', color='0.85')
ax.grid(which='minor', axis='both', linewidth=0.25,
linestyle='--', color='0.80')
ax.minorticks_on()
ax.set_xlabel("x")
ax.set_ylabel(ylabel)
axs[0].plot(xx, yy1)
grid_labels(axs[0], "f(x)")
axs[1].plot(xx, yy2)
grid_labels(axs[1], "df/dx")
plt.show()

Related

Create indexed functions in sympy

Is it possible to create indexed functions in sympy like fi(t) which might be used in a product or sum, eg Σfi(t)?
import sympy as sp
f = sp.Function('f')
i = sp.symbols('i', integer=True)
t = sp.symbols('t', real=True)
sp.Indexed(f, i)(t)
The above code produces the following error:
TypeError:
The base can only be replaced with a string, Symbol, IndexedBase or an
object with a method for getting items (i.e. an object with a
`__getitem__` method).
Assuming that you just want graphically pleasing output you can use the following
import sympy as sp
class f(sp.Function):
name='f'
def _latex(self, printer=None):
a = [printer.doprint(i) for i in self.args]
name=self.name
return r'{}_{{{}}}\left('.format(name,a[0])+','.join(a[1:])+r'\right)'
i = sp.symbols('i', integer=True)
t = sp.symbols('t', real=True)
f(i,t)
sp.Sum(f(i,t),(i,0,sp.oo))

Parallel processing of a 3d matrix in Python

Hi I need to speed up this code
import numpy as np
matrix3d=np.empty([10,10,1000])
matrix3d[:]=np.random.randint(10)
matrix3d_1=np.empty([10,10,1000])
x=10
y=1
for z in range(0,1000):
matrix3d_1[:,:,z]=func(matrix3d[:,:,z],x,y)
def func(matrix,x,y):
return matrix*x+y
I have tried using multiprocessig using Pool.map() but it did not work.
from functools import partial
import multiprocessing as mp
pool=mp.Pool(processes=2)
args=partial(func,x,y)
matrix3d_2=np.empty([10,10,1000])
matrix3d_2=pool.map(args,matrix3d)
pool.close()
If I compare the two matrix matrix3d_1==matrix3d_2 the results is false.
How can this be fixed?
Parallel processing of a 3d matrix
The python map method as well as the pool.map methode can only take one input object. See for example https://stackoverflow.com/a/10973817/4045774
To reduce the inputs to one input we can use for example functools. The input which remains have to be on the last place.
from functools import partial
import numpy as np
import multiprocessing as mp
def main():
matrix3d=np.empty([10,10,1000])
matrix3d[:]=np.random.randint(10)
matrix3d_1=np.empty([10,10,1000])
x=10
y=1
pool=mp.Pool(processes=4)
func_p=partial(func,x,y)
#parallel map returns a list
res=pool.map(func_p,(matrix3d[:,:,z] for z in xrange(0,matrix3d.shape[2])))
#copy the data to array
for i in xrange(0,matrix3d.shape[2]):
matrix3d_1[:,:,i]=res[i]
def func(x,y,matrix):
return matrix*x+y
Parallel version using numba
This version will scale well over all cores and is at least 200 times faster than simple multiprocessing shown above. I have modified the code you linked to a bit, to get rid of any other dependencies than numpy.
import numpy
from numba import njit, prange
nb_meanInterp = njit("float32[:,:](float32[:,:],int64,int64)")(meanInterp)
resample_3d_nb = njit("float32[:,:,:](float32[:,:,:],int64,int64)",parallel=True)(resample_3d)
def resample_3d(matrix_3d,x,y):
matrix3d_res=numpy.empty((x,y,matrix_3d.shape[2]),dtype=numpy.float32)
for z in prange(0,matrix_3d.shape[2]):
matrix3d_res[:,:,z]=nb_meanInterp(matrix_3d[:,:,z],x,y)
return matrix3d_res
def meanInterp(data, m, n):
newData = numpy.zeros((m,n),dtype=numpy.float32)
mOrig, nOrig = data.shape
hBoundariesOrig, vBoundariesOrig = numpy.linspace(0,1,mOrig+1),
numpy.linspace(0,1,nOrig+1)
hBoundaries, vBoundaries = numpy.linspace(0,1,m+1), numpy.linspace(0,1,n+1)
for iOrig in range(mOrig):
for jOrig in range(nOrig):
for i in range(m):
if hBoundaries[i+1] <= hBoundariesOrig[iOrig]: continue
if hBoundaries[i] >= hBoundariesOrig[iOrig+1]: break
for j in range(n):
if vBoundaries[j+1] <= vBoundariesOrig[jOrig]: continue
if vBoundaries[j] >= vBoundariesOrig[jOrig+1]: break
#boxCoords = ((hBoundaries[i], vBoundaries[j]),(hBoundaries[i+1], vBoundaries[j+1]))
#origBoxCoords = ((hBoundariesOrig[iOrig], vBoundariesOrig[jOrig]),(hBoundariesOrig[iOrig+1], vBoundariesOrig[jOrig+1]))
#area=overlap(boxCoords, origBoxCoords)
#hopefully this is equivivalent (not tested)-----
T_x=(hBoundaries[i],hBoundaries[i+1],hBoundariesOrig[iOrig],hBoundariesOrig[iOrig+1])
T_y=(vBoundaries[j],vBoundaries[j+1],vBoundariesOrig[jOrig],vBoundariesOrig[jOrig+1])
tx=(T_x[1]-T_x[0]+T_x[3]-T_x[2])-(max(T_x)-min(T_x))
ty=(T_y[1]-T_y[0]+T_y[3]-T_y[2])-(max(T_y)-min(T_y))
area=tx*ty
#------------------------
newData[i][j] += area * data[iOrig][jOrig] / (hBoundaries[1] * vBoundaries[1])
return newData

Lmfit separate peak fitting

I'm very new to curve/peak fitting, but I am trying to fit a data set with multiple separate independent peaks. I've tried something similar to the example provided by lmfit, and here's my code:
import matplotlib.pyplot as plt
from lmfit.models import GaussianModel
from numpy import loadtxt
data = loadtxt('079-55.freq')
x = data[:, 0]
y = data[:, 1]
gauss1 = GaussianModel(prefix='g1_')
pars = gauss1.make_params()
pars['g1_center'].set(4100, min=2000, max=4500)
pars['g1_amplitude'].set(170, min=10)
gauss2 = GaussianModel(prefix='g2_')
pars.update(gauss2.make_params())
pars['g2_center'].set(4900, min=4500, max=5500)
pars['g2_amplitude'].set(30, min=10)
gauss3 = GaussianModel(prefix='g3_')
pars.update(gauss3.make_params())
pars['g3_center'].set(600, min=5500, max=10000)
pars['g3_amplitude'].set(13, min=10)
mod = gauss1 + gauss2 + gauss3
init = mod.eval(pars, x=x)
plt.plot(x, init, 'k--')
out = mod.fit(y, pars, x=x)
print(out.fit_report())
plt.plot(x, out.best_fit, 'r-')
plt.plot(x, y)
plt.show()
However, the result becomes something like this:
I am very confused as to how to proceed to fit three separate peaks as shown below. I think the parameter update is for pitting multiple model into the same data set, not for separate independent peaks. I could be wrong though. Is there any suggestions?
pars['g3_center'].set(600, min=5500, max=10000)
Probably confuses the parameter or model class as 600 is not within the bounds of min and max.

Iterating operations over unique values of an array

I have a pandas dataframe that resembles one generated as follows.
import numpy as np
import pandas as pd
x0 = pd.DataFrame(np.random.normal(size=(10, 4)))
x1 = pd.DataFrame({'x': [1,1,2,3,2,3,4,1,2,3]})
df = pd.concat((x0, x1), axis=1)
and a function:
def fun(df, n=100):
z = np.random.normal(size=n)
return np.dot(df[[0,1,2,3]], [0.5*z,-1*z,0.3*z,1.2*z])
I would like to:
use identical draws z for each unique value in x,
take the product of the output in the above step over items of unique x
Any suggestion?
Explanation:
Generate n=100 draws to get z such that len(z)=100
For each elem in z, evaluate the function fun,
For i in df.x.unique(), compute the product of the output in step (2) element-wise. I am expecting to get a DataFrame or array of dimension (len(df.x.unique(), n=100)
4.
It sounds like you want to group by 'x', taking one of its instances (let's assume we take the first one observed).
just call your function as follows:
f = fun(df.groupby('x').first())
>>> f.shape
Out[25]: (4, 100)
>>> len(df.x.unique()
Out[26]: 4

Returning np.array or np.matrix objects in a theano function

I have to do something like this.
import theano as th
import theano.tensor as T
x, y = T.dscalars('x', 'y')
z = np.matrix([[x*y, x-y], [x/y, x**2/(2*y)]])
f = th.function([x, y], z) # causes error
# next comes calculations like f(2, 1)*f(3, 2)*some_matrix
I know the last line is not a valid code as th.function doesn't support returning these objects. Is there an efficient way to do this without returning all elements of matrix and casting it as an np.matrix?
The problem with your approach is that z needs to be a list of theano variables not a numpy matrix.
You can achieve the same result using:
z1,z2,z3,z4 = x*y,x-y,x/y,x**2/(2*y)
f = th.function([x, y], [z1,z2,z3,z4])
def createz(z1,z2,z3,z4) :
return np.matrix([[z1,z2],[z3,z4]])
print(createz(*f(1,2)))