scipy curve fitting negative value

耗尽温柔 提交于 2019-12-08 05:57:02

问题


I would like to fit a curve with curve_fit and prevent it from becoming negative. Unfortunately, the code below does not work. Any hints? Thanks a lot!

# Imports
from scipy.optimize import curve_fit
import numpy as np 
import matplotlib.pyplot as plt

xData = [0.0009824379203203417, 0.0011014182912933933, 0.0012433979929054324, 0.0014147106052612918, 0.0016240300315499524, 0.0018834904507916608, 0.002210485320720769, 0.002630660216394964, 0.0031830988618379067, 0.003929751681281367, 0.0049735919716217296, 0.0064961201261998095, 0.008841941282883075, 0.012732395447351627, 0.019894367886486918, 0.0353677651315323, 0.07957747154594767, 0.3183098861837907]

yData = [99.61973156923796, 91.79478510744039, 92.79302188621314, 84.32927272723863, 77.75060981602016, 75.62801782349504, 70.48026800610839, 72.21240551953743, 68.14019252499526, 55.23015406920851, 57.212682880377464, 50.777016257727176, 44.871140881319626, 40.544138806850846, 32.489105158795525, 25.65367127756607, 19.894206907130403, 13.057996247388862]

def func(x,m,c,d):
    '''
    Fitting Function
    I put d as an absolute number to prevent negative values for d?
    '''
    return x**m * c + abs(d) 

p0 = [-1, 1, 1]
coeff, _ = curve_fit(func, xData, yData, p0) # Fit curve
m, c, d = coeff[0], coeff[1], coeff[2]

print("d: " + str(d)) # Why is it negative!!

回答1:


Your model actually works fine as the following plot shows. I used your code and plotted the original data and the data you obtain with the fitted parameters:

As you can see, the data can nicely be reproduced but you indeed obtain a negative value for d (which must not be a bad thing depending on the context of the model). If you want to avoid it, I recommend to use lmfit where you can constrain your parameters to certain ranges. The next plot shows the outcome.

As you can see, it also reproduces the data well and you obtain a positive value for d as desired.

namely:

m:  -0.35199747 
c:   8.48813181 
d:   0.05775745

Here is the entire code that reproduces the figures:

# Imports
from scipy.optimize import curve_fit
import numpy as np 
import matplotlib.pyplot as plt

#additional import
from lmfit import minimize, Parameters, Parameter, report_fit

xData = [0.0009824379203203417, 0.0011014182912933933, 0.0012433979929054324, 0.0014147106052612918, 0.0016240300315499524, 0.0018834904507916608, 0.002210485320720769, 0.002630660216394964, 0.0031830988618379067, 0.003929751681281367, 0.0049735919716217296, 0.0064961201261998095, 0.008841941282883075, 0.012732395447351627, 0.019894367886486918, 0.0353677651315323, 0.07957747154594767, 0.3183098861837907]
yData = [99.61973156923796, 91.79478510744039, 92.79302188621314, 84.32927272723863, 77.75060981602016, 75.62801782349504, 70.48026800610839, 72.21240551953743, 68.14019252499526, 55.23015406920851, 57.212682880377464, 50.777016257727176, 44.871140881319626, 40.544138806850846, 32.489105158795525, 25.65367127756607, 19.894206907130403, 13.057996247388862]

def func(x,m,c,d):
    '''
    Fitting Function
    I put d as an absolute number to prevent negative values for d?
    '''
    print m,c,d
    return np.power(x,m)*c + d

p0 = [-1, 1, 1]
coeff, _ = curve_fit(func, xData, yData, p0) # Fit curve
m, c, d = coeff[0], coeff[1], coeff[2]

print("d: " + str(d)) # Why is it negative!!
plt.scatter(xData, yData, s=30, marker = "v",label='P')
plt.scatter(xData, func(xData, *coeff), s=30, marker = "v",color="red",label='curvefit')
plt.show()

#####the new approach starts here
def func2(params, x, data):

    m = params['m'].value
    c = params['c'].value
    d = params['d'].value

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

# create a set of Parameters
params = Parameters()
params.add('m', value= -2) #value is the initial condition
params.add('c', value= 8.)
params.add('d', value= 10.0, min=0) #min=0 prevents that d becomes negative

# do fit, here with leastsq model
result = minimize(func2, params, args=(xData, yData))

# calculate final result
final = yData + result.residual

# write error report
report_fit(params)

try:
    import pylab
    pylab.plot(xData, yData, 'k+')
    pylab.plot(xData, final, 'r')
    pylab.show()
except:
    pass



回答2:


You could use the scipy.optimize.curve_fit method's bounds option to specify the maximum bound and the minimum bound.

https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html

Bounds is a two tuple array. In your case, you just need to specify the lower bound for d. You could use, bounds=([-np.inf, -np.inf, 0], np.inf)

Note: If you provide a scalar as a parameter (eg:- as the second variable above), it automatically applies as the upper bound for all three coefficients.




回答3:


You just need to add one little argument to constrain your parameters. That is:

curve_fit(func, xData, yData, p0, bounds=([m1,c1,d1],[m2,c2,d2]))

where m1,c1,d1 are the lower bounds of the parameters (in your case they should be 0) and m2,c2,d2 are the upper bounds.

If u want all m,c,d to be positive, the code should goes like the following:

curve_fit(func, xData, yData, p0, bounds=(0,numpy.inf))

where all the parameters have a lower bound of 0 and an upper bound of infinity(no bound)



来源:https://stackoverflow.com/questions/31408782/scipy-curve-fitting-negative-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!