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.
Related
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
I have three arrays, r_vals, Tgas_vals, and n_vals. They are all numpy arrays of the shape (9998.). The arrays have repeated values and I want to iterate over the unique values of r_vals and find the corresponding values of Tgas_vals, and n_vals so I can use the last two arrays to calculate the weighted average. This is what I have right now:
def calc_weighted_average (r_vals,Tgas_vals,n_vals):
for r in r_vals:
mask = r == r_vals
count = 0
count += 1
for t in Tgas_vals[mask]:
print (count, np.average(Tgas_vals[mask]*n_vals[mask]))
weighted_average = calc_weighted_average (r_vals,Tgas_vals,n_vals)
The problem I am running into is that the function is only looping through once. Did I implement mask incorrectly, or is the problem somewhere else in the for loop?
I'm not sure exactly what you plan to do with all the averages, so I'll toss this out there and see if it's helpful. The following code will calculate a bunch of weighted averages, one per unique value of r_vals and store them in a dictionary(which is then printed out).
def calc_weighted_average (r_vals, z_vals, Tgas_vals, n_vals):
weighted_vals = {} #new variable to store rval=>weighted ave.
for r in np.unique(r_vals):
mask = r_vals == r # I think yours was backwards
weighted_vals[r] = np.average(Tgas_vals[mask]*n_vals[mask])
return weighted_vals
weighted_averages = calc_weighted_average (r_vals, z_vals, Tgas_vals, n_vals)
for rval in weighted_averages:
print ('%i : %0.4f' % (rval, weighted_averages[rval])) #assuming rval is integer
alternatively, you may want to factor in "z_vals" in somehow. Your question was not clear in this.
I'm trying to use the scipy function odeint using the code
tmax,dt=5000,1.68
t=linspace(0,tmax,num=round(tmax/dt)+1)
def d_displ(x,t,a,b,c,d,e):
#x'=y, y'=a*x+(b/(x**2.0+c))*x+d*y+e
y=x[0]
dy=x[1]
dx=[dy,a*x+(b/(x**2.0+c))*x+d*y+e]
return dx
def displ(d_displ,J_merger,a,b,c,d,e):
y0=[0.0,a]
sol=odeint(d_displ,y0,t,args=(a,b,c,d,e))
return sol
print displ(d_displ,J_merger,1.0,2.0,3.0,4.0,5.0)
but I got the error
TypeError: float() argument must be a string or a number
why? What I want is an array of values for x.
First I do not get the use of J_merger here, but anyway:
By unwise choice of variable naming you set up a trap for yourself. in d_displ x is an array and not your value x(t). So your first return value is of the form [float, array] and not [float,float]. In the second iteration, hence, odeint fails.
Should look something like this
def d_displ(solList,t,a,b,c,d,e):
# we want x'' = a*x + b*x/(x**2+c)+d*x'+e
# set x'= u
# we get u'= a*x + b*x/(x**2+c)+d*u+e
# solList contains [x,u]
x,u=solList
dx=u
du= a*x + b*x/(x**2+c)+d*u+e
dsolList=[dx,du]
return dsolList
and it works...(well, not up to 5000 as the equation explodes, but that's a different story)
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
I'm in need of a function returning only the significant part of a value with respect to a given error. Meaning something like this:
def (value, error):
""" This function takes a value and determines its significant
accuracy by its error.
It returns only the scientific important part of a value and drops the rest. """
magic magic magic....
return formated value as String.
What i have written so far to show what I mean:
import numpy as np
def signigicant(value, error):
""" Returns a number in a scintific format. Meaning a value has an error
and that error determines how many digits of the
value are signifcant. e.g. value = 12.345MHz,
error = 0.1MHz => 12.3MHz because the error is at the first digit.
(in reality drop the MHz its just to show why.)"""
xx = "%E"%error # I assume this is most ineffective.
xx = xx.split("E")
xx = int(xx[1])
if error <= value: # this should be the normal case
yy = np.around(value, -xx)
if xx >= 0: # Error is 1 or bigger
return "%i"%yy
else: # Error is smaller than 1
string = "%."+str(-xx) +"f"
return string%yy
if error > value: # This should not be usual but it can happen.
return "%g"%value
What I don't want is a function like numpys around or round. Those functions take a value and want to know what part of this value is important. The point is that in general I don't know how many digits are significant. It depends in the size of the error of that value.
Another example:
value = 123, error = 12, => 120
One can drop the 3, because the error is at the size of 10. However this behaviour is not so important, because some people still write 123 for the value. Here it is okay but not perfectly right.
For big numbers the "g" string operator is a usable choice but not always what I need. For e.g.
If the error is bigger than the value.( happens e.g. when someone wants to measure something that does not exist.)
value = 10, error = 100
I still wish to keep the 10 as the value because I done know it any better. The function should return 10 then and not 0.
The stuff I have written does work more or less, but its clearly not effective or elegant in any way. Also I assume this question does concern hundreds of people because every scientist has to format numbers in that way. So I'm sure there is a ready to use solution somewhere but I haven't found it yet.
Probably my google skills aren't good enough but I wasn't able to find a solution to this in two days and now I ask here.
For testing my code I used this the following but more is needed.
errors = [0.2,1.123,1.0, 123123.1233215,0.123123123768]
values = [12.3453,123123321.4321432, 0.000321 ,321321.986123612361236,0.00001233214 ]
for value, error in zip(values, errors):
print "Teste Value: ",value, "Error:", error
print "Result: ", signigicant(value, error)
import math
def round_on_error(value, error):
significant_digits = 10**math.floor(math.log(error, 10))
return value // significant_digits * significant_digits
Example:
>>> errors = [0.2,1.123,1.0, 123123.1233215,0.123123123768]
>>> values = [12.3453,123123321.4321432, 0.000321 ,321321.986123612361236,0.00001233214 ]
>>> map(round_on_error, values, errors)
[12.3, 123123321.0, 0.0, 300000.0, 0.0]
And if you want to keep a value that is inferior to its error
if (value < error)
return value
else
def round_on_error(value, error):
significant_digits = 10**math.floor(math.log(error, 10))
return value // significant_digits * significant_digits