具有固定参数的曲线拟合分段函数

时间:2019-07-02 22:07:18

标签: python

我具有如下所示的功能

   def PPM(x,a,b,c,d):  # d is the fixed parameter
       if x>d: 
          return 100/(1+((x-d)/c)**(-a))**b  
        else: return 0 

参数d是固定的(根据实验室数据计算),我需要进行曲线拟合以获取所有其他参数。

我现在正在使用lmfit,因为我可以轻松地修复参数d。

    from lmfit import minimize, Parameters 
    def residual(params, x, data): 
        d = params['d'] 
        a = params['a'] 
        b = params['b']
        c = params['c'] 
        if x>d: 
            PP = 100/(1+((x-d)/c)**(-a))**b 
        else: PP = 0
        return data-PP 

但显示 ValueError:具有多个元素的数组的真值不明确。使用a.any()或a.all(),我知道它与使用数组矢量化函数有关,但是不知道如何在lmfit中做到这一点

我也通过

修改了上面的代码
    def residual(params, x, data): 
        d = params['d'] #the value of d is 0.078528, the fixed parameter
        a = params['a'] 
        b = params['b'] 
        c = params['c'] 
        PP = 100/(1+((x-d)/c)**(-a))**b 
        PP[np.where(d>x)] = 0 
     return data-PP


    params = Parameters()
    params.add('d', value=0.078528, vary=False)
    params.add('a', value=0.25, min=0, max=1)
    params.add('b', value=5, min=0.5, max=500)
    params.add('c', value=0.001, min=0.0001, max=1)
    out = minimize(residual, params, args=(x, data)) 

修改代码后,运行后没有警告(错误),但拟合的数据看起来很糟糕。似乎无论我分配的初始值如何,都没有更改。 (我想是因为如果x小于d,则该函数没有值) 顺便说一下,我使用从lmfit到Minimumsquare的最小化残差 有办法在python中做到吗? Data and Fitting in excel这是我所期望的 还有数据

    x = [6.2e-5, 1.2e-4, 2.5e-4, 5.0e-4, 1.0e-3, 2.0e-3, 4.2e-3, 8.8e-3, 2.0e-2, 
       4.6e-2, 1.1e-1, 2.6e-1, 6.0e-1]
    Data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.056, 0.478, 3.725, 7.800]

谢谢!

此致

史蒂夫

3 个答案:

答案 0 :(得分:1)

如果不必进行向量化,则可以使用列表理解

from lmfit import minimize, Parameters
import numpy as np 

x=[0.0000621, 0.0001242, 0.00024932, 0.00049772, 0.00100004, 0.00202676, 0.00417128,0.00878876, 0.01984992, 0.04636064, 0.10731156, 0.25733504, 0.60121724] 
data=[ 0,0,0,0,0,0,0,0,0,0.056443102,0.478543944,3.724743415,7.795926674]

def residual(params, x, data): 
    d = params['d'] 
    a = params['a'] 
    b = params['b']
    c = params['c'] 
    PP = [100/(1+((x_-d)/c)**(-a))**b if x_>d else 0 for x_ in x]
    return np.array(data)-np.array(PP)

我不知道a,b,c和d的值应该是什么,所以我把它们补了。

residual({'d':1,'a':2,'b':3,'c':4},x,data)
array([0.        , 0.        , 0.        , 0.        , 0.        ,
       0.        , 0.        , 0.        , 0.        , 0.0564431 ,
       0.47854394, 3.72474341, 7.79592667])

答案 1 :(得分:1)

最好总是包含一个完整的最小脚本,以显示您的实际操作以及您实际获得的实际结果,包括拟合报告。

您的问题表明您确实解决了np.any()np.where()的问题。太好了!

您的模型仍然有

    PP = 100/(1+((x-d)/c)**(-a))**b 

以及该图和问题表明x的某些值将为< d。使用ca都为正实数

    ((x-d)/c)**(-a)

将具有一些复杂的值或给出nan。然后你拿 1 + ((x-d)/c)**(-a)并将其提高到b的幂,b的起始值为20!那是很大的,而且不是很稳定:2.0**20〜= 100万,2.5**20〜= 1亿,3.0**20〜= 30亿。

我建议您进一步探索参数空间。例如,当您仅用初始值评估模型时,您会得到什么? b=20是否适合您的数据?请注意,拟合将从您的初始值开始,对其进行非常非常小的更改,以查看将参数值移动到哪个方向。如果非常非常小的更改不会改变结果,则拟合将不知道要移动到哪里。

作为对您的提示:数据图使用x轴的对数刻度。我还建议为y轴使用对数刻度,甚至拟合对数空间:也就是说,将log(data)视为要拟合的数据,并使用模型计算PP功能的日志。当然,您不会在PP=0的地方设置x<d,但是您可以将其设置为很小的值,例如:

  pparg = (x-d)/c
  pparg[np.where(pparg<0)] = 1.e-30
  # log(pp) = np.log(100 * (1 + pparg**(-a)) **(-b)))
  logpp = np.log(100)  - b * np.log(1 + pparg**(-a))
  return (log(data) - logpp)

希望有帮助。...

答案 2 :(得分:0)

我要感谢所有参与此问题的人。

通过将固定值限制在很小的范围内,我终于得到了想要的东西。

我将在下面显示代码。

import numpy as np
from scipy import optimize
import pylab as pl


def PPM(x,a,b,c,d):  #  model
    """
    The model is pp=100/(1+((x-d)/c)**(-a))**b, d is the fixed parameter
    """
    pp = np.piecewise(x,[x <= d, x > d],[ 0,lambda x: 100/(1+((x-d)/c)**(-a))**b]) 
    return pp

x = [6.2e-5, 1.2e-4, 2.5e-4, 5.0e-4, 1.0e-3, 2.0e-3, 4.2e-3, 8.8e-3, 2.0e-2, 4.6e-2, 1.1e-1, 2.6e-1, 6.0e-1]
y0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.056, 0.478, 3.725, 7.800] #x,yo are lab data
d = 0.071010176 #Calculated value for the fixed parameter
p1 = [1,1,0.5,d] # initial values for parameters
param_bounds = ([0, 0.1, 0.00001,0.99*d],[1, 18, 1, 1.01*d]) 
popt,pcov = optimize.curve_fit(PPM, x, y0, p0=p1,bounds=param_bounds)

#interpolation for plotting
x1 = np.linspace(x[0],x[-1],100)
y1 = [PPM(i,popt[0], popt[1], popt[2], popt[3]) for i in x1]


#plot
pl.semilogx(x, y0, "o", label=u"Lab Data")
pl.semilogx(x1, y1, label=u"Fitted Model")
pl.legend(loc="best")
pl.show

The result from this code

对我来说看起来不错。如果您有任何意见或建议,请发表评论。

再次感谢!

史蒂夫