Python, curve fitting - python

I want to fit a series of Gaussian function from which I do not know before hand how many are required. This is my current solution.
def gauss_recursive(x,max_value,pos,std):
if len(pos)==1:
return gauss(x,max_value,pos,std)
else:
return gauss_recursive(x,max_value[:-1],pos[:-1],std[:-1])+gauss(x,max_value[-1],pos[-1],std[-1])
def gauss(x,max_value,pos,std):
from numpy import exp
return max_value*exp(-(x-pos)*(x-pos)/std/std)
I know round about the position, the std and the maximal value. Therefore, the fit will converge even in the case of many Gaussian funtions. However, I need the curve fit to pass a list or a tuple to my function. It automatically puts it to many single parameters when I start it:
fit_para,err = curve_fit(gauss_recursive,x,y,p0=[[1,5,7],[11,15,14],[2.5,2.5,2.5]])
This results in the following error:
TypeError: gauss_recursive() takes 4 positional arguments but 10 were given
The same effect happens when I pass p0 as a tuple instead of a list.
Is there a way to bypass this problem? If yes how?
Here is the solution:
def gauss_recursive(x,*args):
laenge=int(len(args)/3)
if laenge==1:
return gauss(x,args[0],args[1],args[2])
elif laenge>1:
from numpy import ones
dummy=ones(len(args))
dummy[laenge-1]=0
dummy[2*laenge-1]=0
dummy[-1]=0
dummy2=[args[i] for i in range(laenge*3) if dummy[i]==1]
return gauss_recursive(x,*dummy2)+gauss(x,args[laenge-1],args[2*laenge-1],args[-1])
else:
return -1

Related

Integrating a function of two variables over an array of values

I'm currently trying to solve this integral using SciPy:
I was first advised to use interpolation, which I tried but cannot figure out for some reason, but would probably be a good approach. I found this post about using np.vectorize and I think it might still work, but I am getting an error. Here is the code that I have written thus far (also note that n and n,eq are not indices, they're just variable names):
import numpy as np
from scipy import integrate
def K(x): #This is a function in the integral.
b = 0.252
return b*(((4/(x**3))+(3/(x**2))+1/x) + (4/(x**3) + 1/(x**2))*np.exp(-x))
def Xntot_integrand(x,z): #Defining the integrand
Xneq_x = (1+np.exp(x))**(-1) #This is the term outside the integral and squared within it.
return Xneq_x(x)**2 * np.exp(K(z) - K(x)) * np.exp(x)
Xntot_integrand = np.vectorize(Xntot_integrand)
def Xntot_integrated(x,z):
return quad(Xntot_integrand, 0, z)
Xntot_integrated=np.vectorize(Xntot_integrated)
T_narrow = np.linspace(1,0.01,100) #Narrow T range from 1 to 0.01 MeV
z_narrow = Q/T_narrow
final_integrated_Xneq = Xntot_integrated(z_narrow)
I am getting the error that I am missing a positional argument when I call Xntot_integrated (which makes sense, I think it is still in the two variables x and z).
So I suppose the issue is stemming from where I use quad() because after it is integrated, x should go away. Any advice? Should I use tabulation/interpolation instead?
You need to be using the args keyword argument of integrate.quad to pass additional inputs to the function, so it would look like this:
def Xntot_integrated(z):
return integrate.quad(Xntot_integrand, 0, z, args=(z,))
Note here x is not an input to the integrated function, only z, the first input to the integrand is the integration variable and any extra information is passed via args=(z,) tuple.
alternatively you can define a wrapper that knows z from context and only takes the integration variable as input:
def Xntot_integrated(z):
def integrand(x):return Xntot_integrand(x,z)
return integrate.quad(integrand, 0, z)
but most API's that take a function typically have a keyword argument to specify those inputs. (threading.Thread comes to mind.)
also your Xneq_x should probably be a function itself since you accidentally use it as such inside your integrand (it is just a value there right now) and you will need to use it outside the integration anyway :)

Finding Interpolated Data Value

This is a question I've had before: I have two arrays representing the inputs and corresponding outputs of a function. I need to find the input for a specific output that falls between data points. How do I do that?
For example:
import numpy as np
B = np.arange(0,10,1)
def fun(b):
return b*3/5
A = fun(B)
How to get the value of "B" for fun to return 3.75?
This technique uses linear interpolation to approximate.
I start with this function:
def interpABS(A,B,Aval):
if Aval>max(A) or Aval<min(A):
print('Error: Extrapolating beyond given data')
else:
if len(A)==len(B):
for i in np.arange(1,len(A),1):
ihi = i
ilo = i-1
if A[i]>Aval:
break
Alo = A[ilo]
Blo = B[ilo]
Ahi = A[ihi]
Bhi = B[ihi]
out = Blo + (Bhi-Blo)*(Aval-Alo)/(Ahi-Alo)
return out
else:
print('Error: inputs of different sizes')
Note: I'm kind of an amateur and don't know how to set up exceptions, so instead the error outputs are just print commands on a different path from the rest of the function. Those more experienced than I am may recommend improvements.
Use the output array from your function as A, and the corresponding input array as B, then input your target value as Aval. interpABS will return the an approximate input for your original function to get the target value
So, for our example above, interpABS(A,B,3.75) will return a value of 6.25
This can be useful even if Aval is a value of A to find the corresponding B value, since the math simplifies to Blo + 0. For example, changing Aval in the above example will give 5.0, which is part of the original input set B.

How to get a transformation of a function as a return?

My objective is to implement the following system (Taken from signal processing schematic-ish):
Now, X[n] could be any signal, any function, for simplicity and for testing the program we could see it as the exponential function. And as the signal goes along the subsystems it undergoes some transformations, for S1 the transformation is:
and after S2 is:
and so far I have done this:
import numpy as np
import timeit
n=np.arange(-20,20)
y=np.zeros(n.shape)
x=np.exp(n)
def S1(inputSign, x_values, finalSys=True):
if finalSys==True:
output=np.zeros(x_values.shape)
for i in range(len(x_values)):
if x_values[i]%2==0:
output[i]=inputSign(x_values[i]/2)
else:
output[i] = 0
return output
else:
return inputSign
def S2(inputSign, x_values, finalSys=True):
if finalSys==True:
output=np.zeros(x_values.shape)
for i in range(len(x_values)):
output[i]=inputSign(x_values)+0.5*inputSign(x_values)+0.25*inputSign(x_values)
return output
else:
return inputSign+0.5*?????
Now the functions are defined with a condition finalSys that is supposed to be that if it is the last system it gives me an array of values. The objective is to "cascade"(S2(S1,n,finalSys=True), maybe not the correct term) the functions to as to get a final answer dependent only of the initial input signal but transformed along the way by the subsystems. I could do it manually and get an expression to the resulting system but I am doing this as a learning exercise so please bear with me. My trouble has been how to alter the arguments of the inputSign. As an example, S1(np.exp,n,finalSys=False)(2) should return the same as np.exp(1).

Python -- nondimensionalize

I'm writing a python module to allow me to make unit-based calculations, and I'm trying to implement unit-sensitive integration of functions. My idea is basically to write a wrapper for scipy.integrate -- take the function and arguments given, including the limits of integration, nondimensionalize them all, pass to scipy.integrate.quad or some such thing, get the answer, and then multiply by the correct units at the end.
To accomplish this, I'm trying to figure out how to nondimensionalize an arbitrary function. I've implemented units so that if you divide two quantities with the same units, it returns an ordinary number, so my first thought was to just do this:
def nonDimensionalize(func, *args):
val = func(*args)
dimensions = val / val.value
return lambda args : (func(args) / dimensions)
This works like a charm to nondimensionalize the function's output, but I'm having a harder time with the input. What I really need is to return a function that takes in ordinary numbers, multiplies them by the correct SI dimensions (which I can figure out how to do), gets the output, divides it by the correct SI dimensions, and returns that value as an ordinary number. Then I can pass said function to scipy.integrate (or scipy.fslove, etc.). I tried the following:
def nonDimensionalize(func, *args):
argDims = []
for arg in args:
aDim = arg / arg.value
argDims.append(aDim)
nDargs = []
index = 0
for arg in args:
nDargs.append(arg / argDims[index])
index += 1
val = func(*args)
dimensions = val / val.value
return lambda args : (func(args) / dimensions)
but it doesn't work; it has exactly the same effect as my four-line function above. I'm not sure how to proceed at this point. Help?
What I really need is to return a function that takes in ordinary numbers, multiplies them by the correct SI dimensions (which I can figure out how to do), gets the output, divides it by the correct SI dimensions, and returns that value as an ordinary number.
I'm not sure I understand exactly how you dimensionalize/non-dimensionalize values, so just modify the corresponding functions as necessary, but you could do it like this:
def dimensionalizeValue(nonDimValue, dimensions):
return nonDimValue * dimensions
def nonDimensionalizeValue(dimValue):
dimensions = dimValue / dimValue.value
return dimValue / dimensions
def nonDimensionalizeFunction(function):
def wrapper(*nonDimArgs):
# Figure out the correct dimensions.
dimensions = None
# Transform/dimensionalize the arguments.
dimArgs = [dimensionalizeValue(arg, dimensions) for arg in nonDimArgs]
# Get output using dimensionalized arguments.
dimVal = function(*dimArgs)
# Non-dimensionalize the output.
nonDimVal = nonDimensionalizeValue(dimVal)
return nonDimVal
return wrapper

Changing only the calculation in a function

Is it possible to have a function where you specify a function within it as a variable.
For example, I have two functions which follow exactly the same process, except one calculaate the Average using np.mean and the other calculates the standard deviation where only np.std is different.
i.e.
it would be defined:
def calculate(function)
you would call one in the script like:
calculate(mean)
and the other
calculate(std)
I'm just wondering if it is possible to do something like this s it would greatly reduce my script length.
EDIT
Sorry I should have said that I wanted the mean and std to be the ones predefined in numpy. getattr() in Xu's answer worked
Use getattr to get the method object according to the method name:
def calculate(function):
func = getattr(np, function)
func(...) # do what you want
calculate("mean") # calculate the average number
calculate("std") # calculate the standard deviation
Yes this is possible.
Example:
def addIt(x):
return x+x
def test(fn):
for x in xrange(5):
print fn(x)
test(addIt)
Output:
0
2
4
6
8
def f1(t): return t * 2
def f2(t): return t * t
def comb(t,f): return f(t)
print comb(10, f1)
print comb(10, f2)

Categories

Resources