Andrew
Andrew

Reputation: 31

How to put constraints on fitting parameter?

I want to fit a curve to my data:

x=[24,25,28,37,58,104,200,235,235]  
y=[340,350,370,400,430,460,490,520,550]  
xerr=[1.1,1,0.8,1.4,1.4,2.6,3.8,2,2]

def fit_fc(x, a, b, c):  
return a*x**b+c

popt, pcov=curve_fit(fit_fc,x,y,maxfev=5000)

plt.plot(x,fit_fc(x,popt[0],popt[1],popt[2]))
plt.errorbar(x,y,xerr=xerr,fmt='-o') 

but i want to put some constraints on the a,b and c. For example I want them to be in some range, lets say between 0 and 20. How can i achieve that? I'm new in Python, so any help would be appreciated.

Upvotes: 3

Views: 2825

Answers (3)

Cleb
Cleb

Reputation: 25997

You could use lmfit to constrain you parameters. For the following plot, I constrained your parameters a and b to the range [0,20] (which you mentioned in your post) and c to the range [0, 400]. The parameters you get are:

a:   19.9999991
b:   0.46769173
c:   274.074071

and the corresponding plot looks as follows:

enter image description here

As you can see, the model reproduces the data reasonable well and the parameters are in the given ranges.

Here is the code that reproduces the results with additional comments:

from lmfit import minimize, Parameters, Parameter, report_fit
import numpy as np

x=[24,25,28,37,58,104,200,235,235]  
y=[340,350,370,400,430,460,490,520,550]  


def fit_fc(params, x, data):  
    a = params['a'].value
    b = params['b'].value
    c = params['c'].value

    model = np.power(x,b)*a + c
    return model - data #that's what you want to minimize

# create a set of Parameters
#'value' is the initial condition
#'min' and 'max' define your boundaries
params = Parameters()
params.add('a', value= 2, min=0, max=20) 
params.add('b', value= 0.5, min=0, max=20)
params.add('c', value= 300.0, min=0, max=400) 

# do fit, here with leastsq model
result = minimize(fit_fc, params, args=(x, y))

# calculate final result
final = y + result.residual

# write error report
report_fit(params)

#plot results
try:
    import pylab
    pylab.plot(x, y, 'k+')
    pylab.plot(x, final, 'r')
    pylab.show()
except:
    pass

If you constrain all of your parameters to the range [0,20], the plot looks rather bad:

enter image description here

Upvotes: 2

xpinguin
xpinguin

Reputation: 21

Scipy uses unconstrained least squares in order to fit curve parameters, so it won't be that straightforward: https://github.com/scipy/scipy/blob/v0.16.0/scipy/optimize/minpack.py#L454

What you'd probably like to do is called constrained (non-linear?, giving what you're trying to fit) least squares problem. For instance, take a look at those discussions:

Upvotes: 0

stevieb
stevieb

Reputation: 9296

It depends on what you want to have happen if the variables are out of range. You can use a simple if statement (in this case the program exit()s):

x = 21

if (x not in range(0, 20)):
    print("var x is out of range")
    exit()

Another way is to assert that the variable must be in the range. In this case, it's wrapped in a try/except block that handles the problem gracefully, and also exit()s like above:

try:
    assert(x in range(0, 20))
except AssertionError:
    print("variable x is out of range")
    exit()

Upvotes: 0

Related Questions