Fit y=ax in Python - python

I wanna fit this as y=ax. However, the code doesn't work. Can anyone could help me?
def func():
return a * x
F1 = [0.55, 0.45, 0.50, 0.65, 0.75, 0.80]
r1 = [18.2, 18.4, 18.8, 19.5, 20.0, 20.2]
plt.plot(F1, r1)
popt = curve_fit(func, r1, F) # I supose it only returns one value
plt.plot(r1, popt * r1, 'g--')
The error is:
ValueError: Unable to determine number of fit parameters.

Something like this. Run the code to see what the results of curve_fit are like and follow the logic carefully.
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit as curve_fit
def f(x,a):
return a*x
F1 = [ 0.55, 0.45, 0.50, 0.65, 0.75, 0.80 ]
r1 = [ 18.2, 18.4, 18.8, 19.5, 20.0, 20.2 ]
result = curve_fit(f, r1, F1)
print (result)
print ( 'F1 = %s * r1' % result[0][0] )
plt.plot(r1, F1, 'b.')
p = [ result[0][0] * _ for _ in r1 ]
plt.plot(r1, p, 'g--')
plt.show()

The main issue with your code is that you have no inputs to your fitting function. func doens't know what a and x are, so can't return a*x.
Also, curve_fit doesn't just return popt, but also pcov, so you have to catch that in the returned values
Here's a fixed version of your code:
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# func needs x and a as inputs
def func(x, a):
return a*x
F1=[0.55,0.45,0.50,0.65,0.75,0.80]
r1=[18.2,18.4,18.8,19.5,20.0,20.2]
plt.plot(r1,F1, 'bo')
popt, pcov = curve_fit(func, r1, F1)
plt.plot(r1,popt*r1,'g--')
plt.show()
Note: the reason the fit is not great is because your line must go through the origin. You can get a better fit using a*x+b, but that's not what you asked how to do.

Related

function model() missing 1 required positional argument

I'm trying to estimate two parameter values A and B of an ODE using curve_fit, and then fit the solution to this ODE to my data set, plotting the results.
My code:
def model(I,t,A,B):
dIdt = A*(2000 - I) + B*(2000 - I)*(I/2000)
return dIdt
xData = # this is an np.array of my x values
yData = # this is an np.array of my y values
plt.plot(xData, yData, 'r.-', label='experimental-data') #This part of the code seems to work
initialGuess = [1.0,1.0]
popt, pcov = curve_fit(model, xData, yData, initialGuess) #This is where the error is
print(popt)
xFit = np.arange(0.0, 5.0, 0.01)
I0 = 0
t = np.linspace(0,60)
I = odeint(model,I0,t) #This is where i integrate the ODE to obtain I(t).
plt.plot(xFit, I(xFit, *popt), 'r', label='fit params: a=%5.3f, b=%5.3f' % tuple(popt))
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
The error I am getting is
model() missing 1 required positional argument: 'B'.
I roughly understand what's going on: my model() function takes in 4 arguments at the beginning: I,t,A and B. However, somewhere along the line, the code only recognizes these first 3 arguments, and leaves out B. I am not sure how to fix this.
I have tried a few things:
taking out the 'initialGuess' from the error line, so that there are 3 arguments in the curve_fit line , and this gave me a new error
Improper input: N=3 must not exceed M=1
which makes me think, that the initialGuess entry isn't the problem.
Changed model in the error line to model(), which gave me the error
model() missing 4 required positional arguments: 'I', 't', 'A', and 'B'
Working off this, I changed model to model(I,t,A,B), which ends up giving me name 'A' is not defined
And now I am lost.
All of these errors are happening in the same line, so I've tried changing things in there, but perhaps I am missing something else. Most of the online sources that touch on this error mention having to instantiate a class instance, but I'm unsure what this means in this context, I have not defined a class in the code.
I hope I've made my confusion clear, any guidance would be appreciated.
Perform curve_fit from scipy.optimize with model function (see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html):
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
def model(i, a, b):
return a * (2_000 - i)\
+ b * (2_000 - i)\
* (i / 2_000)
xData = np.array(range(10))
yData = model(xData, 1, 1)
initialGuess = [1.0, 1.0]
popt, pcov = curve_fit(f=model,
xdata=xData,
ydata=yData,
p0=initialGuess
)
print(popt)
Returns:
[1. 1.]
Next, Perform integration using odeint from scipy.integrate:
from scipy.integrate import odeint
xFit = np.arange(0.0, 5.0, 0.1)
I0 = 0
t = np.linspace(0, 60)
a, b = 1, 1
def model(i, t, a, b):
return a * (2_000 - i)\
+ b * (2_000 - i)\
* (i / 2_000)
I = odeint(model, I0, t, args=(a, b))
plt.plot(xFit, I[:, 0], 'b', label= 'fit params: a=%5.3f, b=%5.3f' % tuple(popt))
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
Reveals the plot (see https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html):

Python Estimate the standard deviation after data fitting

I am trying to fit a data set into the hyperpolic equation using ipython --pylab:
y = ax / (b + x)
Here is my python code:
from scipy import optimize as opti
import numpy as np
from pandas import DataFrame
x = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8])
y = np.array([0.375, 0.466, 0.509, 0.520, 0.525, 0.536, 0.541])
y_stdev = np.array([0.025, 0.016, 0.009, 0.009, 0.025, 0.019])
def func(x, a, b):
return a*x / (b + x)
popt, pcov = opti.curve_fit(func, x, y)
print(popt)
print("a = ", popt.ix[0])
print("b = ", popt.ix[1])
The values of a and b should be inside the popt parameter. What I would like to ask is, the values of a and b are inferred when fitting the data set into the func(x, a, b), then, how can we estimate the standard deviations of a and b?
Thank you.
The answer is in the docs:
pcov : 2d array
The estimated covariance of popt. The diagonals provide the variance of the parameter estimate. To compute one standard deviation errors on the parameters use perr = np.sqrt(np.diag(pcov))...

How to fit an int list to a desired function

I have an int list x, like [43, 43, 46, ....., 487, 496, 502](just for example)
x is a list of word count, I want change a list of word count to a list penalty score when training a text classification model.
I'd like use a curve function(maybe like math.log?) use to map value from x to y, and I need the min value in x(43) mapping to y(0.8), and the max value in x(502) to y(0.08), the other values in x map to a y follow the function.
For example:
x = [43, 43, 46, ....., 487, 496, 502]
y_bounds = [0.8, 0.08]
def creat_curve_func(x, y_bounds, curve_shape='log'):
...
func = creat_curve_func(x, y)
assert func(43) == 0.8
assert func(502) == 0.08
func(46)
>>> 0.78652 (just a fake result for example)
func(479)
>>> 0.097 (just a fake result for example)
I quickly found that I have to try some parameter by my self to get a curve function fit my purpose, try again and again.
Then I try to find a lib to do such work, scipy.optimize.curve_fit turns out. But it need three parameter at least: f(the function I want to generate), xdata, ydata(I only have y bounds:0.8, 0.08), only xdata I have.
Is there any good sulotion?
update
I think this is easy unserstood so didn't write the fail code of curve_fit.Is this the reason of down vote?
The reason that why I can't just use curve_fit
x = sorted([43, 43, 46, ....., 487, 496, 502])
y = np.linspace(0.8, 0.08, len(x)) # can not set y as this way which lead to the wrong result
def func(x, a, b):
return a * x +b # I want a curve function in fact, linear is simple to understand here
popt, pcov = curve_fit(func, x, y)
func(42, *popt)
0.47056348146450089 # I want 0.8 here
How about this way?
EDIT: added weights. If you don't need to put your end points exactly on the curve you could use weights:
import scipy.optimize as opti
import numpy as np
xdata = np.array([43, 56, 234, 502], float)
ydata = np.linspace(0.8, 0.08, len(xdata))
weights = np.ones_like(xdata, float)
weights[0] = 0.001
weights[-1] = 0.001
def fun(x, a, b, z):
return np.log(z/x + a) + b
popt, pcov = opti.curve_fit(fun, xdata, ydata, sigma=weights)
print fun(xdata, *popt)
>>> [ 0.79999994 ... 0.08000009]
EDIT:
You can also play with these parameters, of course:
import scipy.optimize as opti
import numpy as np
xdata = np.array([43, 56, 234, 502], float)
xdata = np.round(np.sort(np.random.rand(100) * (502-43) + 43))
ydata = np.linspace(0.8, 0.08, len(xdata))
weights = np.ones_like(xdata, float)
weights[0] = 0.00001
weights[-1] = 0.00001
def fun(x, a, b, z):
return np.log(z/x + a) + b
popt, pcov = opti.curve_fit(fun, xdata, ydata, sigma=weights)
print fun(xdata, *popt)
>>>[ 0.8 ... 0.08 ]

Getting standard error associated with parameter estimates from scipy.optimize.curve_fit

I am using scipy.optimize.curve_fit to fit a curve to some data i have. The curves, for the most part, seem to fit very well. For some reason, pcov = inf when i print it off.
What i really need is to calculate the error associated with the parameters i'm fitting, and am not sure how exactly to do this even if it does give me the covariance matrix.
The model being fit to is:
def intensity(x,R_out,R_in,K_in,K_out,a,b,c):
K_in,K_out = abs(0.0),abs(K_out)
if x<=R_in:
return 2*R_out*(K_out*np.sqrt(1-x**2/R_out**2)-
(K_out-0.0)*np.sqrt(R_in**2/R_out**2-x**2/R_out**2)) + c
elif x>=R_in and x<=R_out:
return K_out*2*R_out*np.sqrt(1-x**2/R_out**2) + c
elif x>R_out:
return c
intensity_vec = np.vectorize(intensity)
def intensity_vec_self(x,R_out,R_in,K_in,K_out,a,b,c):
y = np.zeros(x.shape)
for i in range(len(y)):
y[i]=intensity_vec(x[i],R_out,R_in,K_in,K_out,a,b,c)
return y
and there are 400 data points, i can put that on here if you think it will help.
To summarize, i can't get curve_fit to print off my pcov and need help as to figure out why and if i can get it to do so.
Also, if it is a quick explanation i would like to know how to use the pcov array to attain the errors associated with my fit.
Thanks
The variance of parameters are the diagonal elements of the variance-co variance matrix, and the standard error is the square root of it. np.sqrt(np.diag(pcov))
Regarding getting inf, see and compare these two examples:
In [129]:
import numpy as np
def func(x, a, b, c, d):
return a * np.exp(-b * x) + c
xdata = np.linspace(0, 4, 50)
y = func(xdata, 2.5, 1.3, 0.5, 1)
ydata = y + 0.2 * np.random.normal(size=len(xdata))
popt, pcov = so.curve_fit(func, xdata, ydata)
print np.sqrt(np.diag(pcov))
[ inf inf inf inf]
And:
In [130]:
def func(x, a, b, c):
return a * np.exp(-b * x) + c
xdata = np.linspace(0, 4, 50)
y = func(xdata, 2.5, 1.3, 0.5)
ydata = y + 0.2 * np.random.normal(size=len(xdata))
popt, pcov = so.curve_fit(func, xdata, ydata)
print np.sqrt(np.diag(pcov))
[ 0.11097646 0.11849107 0.05230711]
In this extreme example, d has no effect on the function func, hence it will be associated with variance of +inf, or in another word, it can be just about any value. Removing d from func will get what will make sense.
In reality, if parameters are of very different scale, say:
def func(x, a, b, c, d):
#return a * np.exp(-b * x) + c
return a * np.exp(-b * x) + c + d*1e-10
You will also get inf due to float point overflow/underflow.
In your case, I think you never used a and b. So it is just like the first example here.

how to find 50% point after curve fitting using numpy

I have used numpy in python to fit my data to a sigmoidal curve. How can I find the vaue for X at y=50% point in the curve after the data is fit to the curve
enter code here`import numpy as np
enter code here`import pylab
from scipy.optimize import curve_fit
def sigmoid(x, x0, k):
y = 1 / (1 + np.exp(-k*(x-x0)))
return y
xdata = np.array([0.0, 1.0, 3.0, 4.3, 7.0, 8.0, 8.5, 10.0, 12.0])
ydata = np.array([0.01, 0.02, 0.04, 0.11, 0.43, 0.7, 0.89, 0.95, 0.99])
popt, pcov = curve_fit(sigmoid, xdata, ydata)
print popt
x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)
pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 1.05)
pylab.legend(loc='best')
pylab.show()
You just need to solve the function you found for y(x) = 0.50. You can use one of the root finding tools of scipy, though these only solve for zero, so you need to give your function an offset:
def sigmoid(x, x0, k, y0=0):
y = 1 / (1 + np.exp(-k*(x-x0))) + y0
return y
Then it's just a matter of calling the root finding method of choice:
from scipy.optimize import brentq
a = np.min(xdata)
b = np.max(xdata)
x0, k = popt
y0 = -0.50
solution = brentq(sigmoid, a, b, args=(x0, k, y0)) # = 7.142
In addition to your comment:
My code above uses the original popt that was calculated with your code. If you do the curve fitting with the updated sigmoid function (with the offset), popt will also contain a fitted parameter for y0.
Probably you don't want this.. you'll want the curve fitted for y0=0. This can be done by supplying a guess for the curve_fit with only two values. This way the default value for y0 of the sigmoid function will be used:
popt, pcov = curve_fit(sigmoid, xdata, ydata, p0 = (1,1))
Alternatively, just declare two seperate sigmmoid functions, one with the offset and one without it.

Categories

Resources