问题
我已经创建了一个曲线拟合练习(参见下面的功能代码),但我想添加功能
我需要能够定义以下条件:slope at min(xdata) = 0
。
(在文字中:我希望拟合曲线以水平渐变开始)
我尝试了什么
我花了相当多的时间研究scipy.optimize.curve_fit并评估了其他选项(lmfit package和scipy函数 scipy.optimize.fmin_slsqp , scipy.optimize.minimize < / em>等)。 lmfit只允许我在参数上设置静态条件,例如p1 = 2 * p2 + 3.但是它不允许我动态地解析min(xdata),并且我不能在约束中使用推导。登记/>
Scipy只允许我最小化函数(找到最佳x,但参数p已知)。或者它可以用于定义参数的特定范围。我无法定义可用于在曲线拟合期间约束参数的第二个函数。
我需要能够将条件直接传递给曲线拟合算法(而不是通过将条件引入cubic_fit()
方程来解决问题 - 似乎可以消除例如p3并将其定义为组合其他参数和min(xdata))。我的实际拟合函数要复杂得多,我需要在一批数据上迭代运行这个脚本(变化min(xdata))。我不能每次都手动改变拟合函数......
我很感激任何建议,也许还有其他包可以更复杂地定义曲线拟合问题?
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats
import scipy.optimize
# generate dummy data - on which I will run a curve fit below
def cubic_fit_with_noise(x, p1, p2, p3, p4):
return p1 + p2*x + p3*x**2 + p4*x**3 + np.random.rand()
xdata = [x * 0.1 for x in range(0, 100)]
ydata = np.array( [cubic_fit_with_noise (x, 2, 0.4, -.2,0.02) for x in xdata] )
# now, run the curve-fit
# set up the fitting function:
def cubic_fit(x, p1, p2, p3, p4):
return p1 + p2*x + p3*x**2 + p4*x**3
# define starting point:
s1 = 2.5
s2 = 0.2
s3 = -.2
s4 = 0.02
# scipy curve fitting:
popt, pcov = scipy.optimize.curve_fit(cubic_fit, xdata, ydata, p0=(s1,s2,s3,s4))
y_modelled = np.array([cubic_fit(x, popt[0], popt[1], popt[2], popt[3]) for x in xdata])
print(popt) # prints out the 4 parameters p1,p2,p3,p4 defined in curve-fitting
plt.plot(xdata, ydata, 'bo')
plt.plot(xdata, y_modelled, 'r-')
plt.show()
上面的代码使用Python3运行(如果你有Python2,修复print语句)。 另外,我想引入衍生产品:
def cubic_fit_derivative(x, p1, p2, p3, p4):
return p2 + 2.0 * p3 * x + 3 * p4 * x**2
以及cubic_fit_derivative(min(xdata), p1,p2,p3,p4) = 0
。
答案 0 :(得分:1)
您在xmin
处的多项式的导数= 0的条件可以表示为简单约束,并且意味着变量p2
,p3
和p4
是实际上并不独立。推导条件是
p2 + 2*p3*xmin + 3*p4*xmin**2 = 0
其中xmin
是xdata
的最小值。此外,xmin
将在拟合之前被知道(如果不一定在编写脚本时),您可以使用它来约束三个参数中的一个。由于xmin
可能为零(事实上,它适用于您的情况),因此约束应为
p2 = - 2*p3*xmin - 3*p4*xmin**2
使用lmfit,原始的,无约束的合身看起来像这样(我清理了一下):
import numpy as np
from lmfit import Model
import matplotlib.pylab as plt
# the model function:
def cubic_poly(x, p1, p2, p3, p4):
return p1 + p2*x + p3*x**2 + p4*x**3
xdata = np.arange(100) * 0.1
ydata = cubic_poly(xdata, 2, 0.4, -.2, 0.02)
ydata = ydata + np.random.normal(size=len(xdata), scale=0.05)
# make Model, create parameters, run fit, print results
model = Model(cubic_poly)
params = model.make_params(p1=2.5, p2=0.2, p3=-0.0, p4=0.0)
result = model.fit(ydata, params, x=xdata)
print(result.fit_report())
plt.plot(xdata, ydata, 'bo')
plt.plot(xdata, result.best_fit, 'r-')
plt.show()
打印:
[[Model]]
Model(cubic_poly)
[[Fit Statistics]]
# function evals = 13
# data points = 100
# variables = 4
chi-square = 0.218
reduced chi-square = 0.002
Akaike info crit = -604.767
Bayesian info crit = -594.347
[[Variables]]
p1: 2.00924432 +/- 0.018375 (0.91%) (init= 2.5)
p2: 0.39427207 +/- 0.016155 (4.10%) (init= 0.2)
p3: -0.19902928 +/- 0.003802 (1.91%) (init=-0)
p4: 0.01993319 +/- 0.000252 (1.27%) (init= 0)
[[Correlations]] (unreported correlations are < 0.100)
C(p3, p4) = -0.986
C(p2, p3) = -0.967
C(p2, p4) = 0.914
C(p1, p2) = -0.857
C(p1, p3) = 0.732
C(p1, p4) = -0.646
现在,要添加约束条件,我们会将xmin
添加为固定参数,并按上述方式约束p2
,将以上内容替换为:
params = model.make_params(p1=2.5, p2=0.2, p3=-0.0, p4=0.0)
# add an extra parameter for `xmin`
params.add('xmin', min(xdata), vary=False)
# constrain p2 so that the derivative is 0 at xmin
params['p2'].expr = '-2*p3*xmin - 3*p4*xmin**2'
result = model.fit(ydata, params, x=xdata)
print(result.fit_report())
plt.plot(xdata, ydata, 'bo')
plt.plot(xdata, result.best_fit, 'r-')
plt.show()
现在打印
[[Model]]
Model(cubic_poly)
[[Fit Statistics]]
# function evals = 10
# data points = 100
# variables = 3
chi-square = 1.329
reduced chi-square = 0.014
Akaike info crit = -426.056
Bayesian info crit = -418.241
[[Variables]]
p1: 2.39001759 +/- 0.023239 (0.97%) (init= 2.5)
p2: 0 +/- 0 (nan%) == '-2*p3*xmin - 3*p4*xmin**2'
p3: -0.10858258 +/- 0.002372 (2.19%) (init=-0)
p4: 0.01424411 +/- 0.000251 (1.76%) (init= 0)
xmin: 0 (fixed)
[[Correlations]] (unreported correlations are < 0.100)
C(p3, p4) = -0.986
C(p1, p3) = -0.742
C(p1, p4) = 0.658
和
之类的情节如果xmin
未归零(例如xdata = np.linspace(-10, 10, 101)
,则p2
的值和不确定性不会为零。
答案 1 :(得分:0)
正如我的评论中提到的,你只需要适合正确的功能。不过我忘记了常数。所以函数是a*(x-xmin)**2*(x-xn)+c
由于curvefit
不会采取其他参数,例如leatssq
,唯一的诀窍就是通过xmin
。我通过一个全局变量来做到这一点(也许不是最好的方式,但它确实有效。欢迎评论如何做得更好)。
最后,您只需要在代码中添加以下行:
def cubic_zero(x,a,xn,const):
global xmin
return (a*(x-xmin)**2*(x-xn)+const)
和
xmin=xdata[0]
popt2, pcov2 = scipy.optimize.curve_fit(cubic_zero, xdata, ydata)
y_modelled2 = np.array([cubic_zero(x, *popt2) for x in xdata])
print(popt2)
plt.plot(xdata, y_modelled2, color='#ee9900',linestyle="--")
提供
>>>[ 0.01429367 7.63190327 2.92604132]
和
答案 2 :(得分:0)
此解决方案使用scipy.optimize.leastsq
。使用自制residuals
函数,实际上不需要将xmin
作为附加参数传递给拟合。拟合函数与另一篇文章一样,因此不需要约束。这看起来像:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
def cubic_fit_with_noise(x, p1, p2, p3, p4):
return p1 + p2*x + p3*x**2 + p4*x**3 + .2*(1-2*np.random.rand())
def cubic_zero(x,a,xn,const, xmin):
return (a*(x-xmin)**2*(x-xn)+const)
def residuals(params, dataX,dataY):
a,xn,const=params
xmin=dataX[0]
dist=np.fromiter( (y-cubic_zero(x,a,xn,const, xmin) for x,y in zip(dataX,dataY)), np.float)
return dist
xdata = np.linspace(.5,10.5,100)
ydata = np.fromiter( (cubic_fit_with_noise (x, 2, 0.4, -.2,0.02) for x in xdata), np.float )
# scipy curve fitting with leastsq:
initialGuess=[.3,.3,.3]
popt2, pcov2, info2, msg2, ier2 = leastsq(residuals,initialGuess, args=(xdata, ydata), full_output=True)
fullparams=np.append(popt2,xdata[0])
y_modelled2 = np.array([cubic_zero(x, *fullparams) for x in xdata])
print(popt2)
print(pcov2)
print np.array([ -popt2[0]*xdata[0]**2*popt2[1]+popt2[2],popt2[0]*(xdata[0]**2+2*xdata[0]*popt2[1]),-popt2[0]*(2*xdata[0]+popt2[1]),popt2[0] ])
plt.plot(xdata, ydata, 'bo')
plt.plot(xdata, y_modelled2, 'r-')
plt.show()
并提供:
>>>[ 0.01710749 7.69369653 2.38986378]
>>>[[ 4.33308441e-06 5.61402017e-04 2.71819763e-04]
[ 5.61402017e-04 1.10367937e-01 5.67852980e-02]
[ 2.71819763e-04 5.67852980e-02 3.94127702e-02]]
>>>[ 2.35695882 0.13589672 -0.14872733 0.01710749]
目前图片上传无效......无论出于何种原因,结果与其他帖子相同