使用条件方程

时间:2017-10-10 10:41:16

标签: python conditional-statements curve-fitting

问题
我已经创建了一个曲线拟合练习(参见下面的功能代码),但我想添加功能 我需要能够定义以下条件: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

的约束

3 个答案:

答案 0 :(得分:1)

您在xmin处的多项式的导数= 0的条件可以表示为简单约束,并且意味着变量p2p3p4是实际上并不独立。推导条件是

p2 + 2*p3*xmin + 3*p4*xmin**2 = 0

其中xminxdata的最小值。此外,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 

并制作一个情节 enter image description here

现在,要添加约束条件,我们会将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 

之类的情节

enter image description here

如果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]

With zero slope fit

答案 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]

目前图片上传无效......无论出于何种原因,结果与其他帖子相同