Python 是否有方法在scipy.optimize.curve_fit的参数之间添加依赖项

Python 是否有方法在scipy.optimize.curve_fit的参数之间添加依赖项,python,scipy,curve-fitting,Python,Scipy,Curve Fitting,我试图用海洋模型描述70年代核试验引起的碳同位素扩散。 大气信号是一个很强的尖峰信号,它将随着洋流被带到深海(较深的洋流要慢得多) 我的目标是检测在不同深度水平上浓度上升的开始和增加的速度 我假设碳同位素的海洋浓度表现为3段分段线性函数: 直到时间(t\u 0)的恒定初始值(b) 浓度从时间(t_0)到(t_1)的线性增加,速率为m1 时间后浓度的线性下降(t_1),速率m2 我使用python中的以下代码表示函数: import numpy as np import matplotlib.py

我试图用海洋模型描述70年代核试验引起的碳同位素扩散。 大气信号是一个很强的尖峰信号,它将随着洋流被带到深海(较深的洋流要慢得多)

我的目标是检测在不同深度水平上浓度上升的开始和增加的速度

我假设碳同位素的海洋浓度表现为3段分段线性函数:

  • 直到时间(
    t\u 0
    )的恒定初始值(
    b
  • 浓度从时间(
    t_0
    )到(
    t_1
    )的线性增加,速率为
    m1
  • 时间后浓度的线性下降(
    t_1
    ),速率
    m2
  • 我使用python中的以下代码表示函数:

    import numpy as np
    import matplotlib.pyplot as plt
    import scipy.optimize as sio
    
    def piecewise_linear( t, t0, t1, b, m1, m2 ):
        condlist = [ t < t0,
                    (t >= t0 ) & ( t < t1 ),
                    t >= t1
                   ]
        funclist = [lambda t: b,
                    lambda t: b + m1 * ( t - t0 ),
                    lambda t: b + m1 * ( t - t0 ) + m2 * ( t - t1 )
                   ]
        return np.piecewise( t, condlist, funclist )
    

    对于第一种情况,当我在添加一些随机噪声后尝试将函数拟合到信号时,我得到了很好的结果:

    noise = np.random.normal( 0, 1, len( y_full ) ) * 1
    y = y_full
    yy = y_full + noise
    bounds = ( [ 0, 0, 0, 0, -np.inf ], [ np.inf, np.inf, np.inf, np.inf, 0 ] )
    fit,_ = sio.curve_fit( piecewise_linear, t, yy, bounds=bounds )
    print( fit )
    y_fit = piecewise_linear( t, *tuple( fit ) )
    plt.plot( t, yy, color='0.5' )
    plt.plot( t, y_fit, linewidth=3 )
    plt.plot( t, y, linestyle='--', linewidth=3 )
    
    导致

    >>[  5.00001407  10.01945313   2.13055863   1.95208167  -3.95199719]
    

    然而,当我尝试评估第二个案例(深海)时,我通常会得到如下糟糕的结果:

    noise = np.random.normal( 0, 1, len(y_full ) ) * 1#
    y = y_cut
    yy = y_cut+noise
    bounds = ( [ 0, 0, 0, 0, -np.inf], [ np.inf, np.inf, np.inf, np.inf, 0 ] )
    fit,_ = sio.curve_fit( piecewise_linear, t, yy, bounds=bounds )
    print( fit )
    y_fit = piecewise_linear( t, *tuple( fit ) )
    plt.plot( t, yy, color='0.5' )
    plt.plot( t, y_fit, linewidth=3 )
    plt.plot( t, y, linestyle='--', linewidth=3 )
    plt.legend( [ 'noisy data', 'fit', 'original' ] )
    
    我明白了

    优化确定
    t_0
    大于
    t_1
    ,这在本文中是不合理的

    是否有方法将条件
    t\u 0
    构建到曲线拟合中?或者我必须测试哪种类型的曲线是给定的,然后适合于两个不同的函数(3段或2段分段线性函数)


    <> >非常感谢您的帮助,

    < p>您可以考虑使用LMFIT()。 Lmfit为曲线拟合提供了更高级的接口,并使拟合参数成为第一类python对象。除此之外,这可以轻松地修复一些参数,并以比
    scipy.optimize.curve\u fit
    使用的更为简洁的方式设置参数边界。特别是对于您的问题,lmfit参数还支持使用数学表达式作为所有参数的约束表达式

    要将模型函数
    分段线性()
    转换为使用lmfit进行曲线拟合的模型,请执行以下操作

    from lmfit import Model
    
    # make a model
    mymodel = Model(piecewise_linear)
    
    # create parameters and set initial values
    # note that parameters are *named* from the 
    # names of arguments of your model function
    params = mymodel.make_params(t0=0, t1=1, b=3, m1=2, m2=2)
    
    # now, you can place bounds on parameters, maybe like
    params['b'].min = 0
    params['m1'].min = 0
    
    # but what you want is an inequality constraint, so
    #   1. add a new parameter 'tdiff'
    #   2. constrain t1 = t0 + tdiff
    #   3. set a minimum value of 0 for tdiff
    params.add('tdiff', value=1, min=0)
    params['t1'].expr = 't0 + tdiff'
    
    # now perform the fit
    result = mymodel.fit(yy, params, t=t)
    
    # print out results
    print(result.fit_report())
    

    您可以阅读lmfit文档或其他SO问题,了解如何从拟合结果中提取其他信息。

    在这种情况下,
    曲线拟合有几个缺点,因此需要考虑MNewille的解决方案。此外,
    curve\u fit
    没有参数
    args
    (与之相反,例如,
    leastsq
    ),这可能允许关闭第二个坡度。在这里,没有
    m2
    的第二个拟合函数可能是一个解决方案。但是,如果必须使用
    曲线拟合
    ,并且在这两种情况下都需要通用拟合函数,则解决方案可能如下所示(注意从数据中提取的起始参数):

    将numpy导入为np
    将matplotlib.pyplot作为plt导入
    导入scipy.optimize作为sio
    """
    我们知道t0>0,t1>t0,b>0,m1>0,m2<0
    """
    def分段线性(t、t0、a、b、m1、m2):
    t0=abs(t0)
    t1=绝对值(a)*t0
    b=abs(b)
    m1=abs(m1)
    m2=-abs(m2)
    condlist=[t=t0)和(t=t1
    ]
    funclist=[lambda t:b,
    λt:b+m1*(t-t0),
    λt:b+m1*(t-t0)+m2*(t-t1)
    ]
    逐段返回np(t,条件列表,函数列表)
    t=np.arange(0,15,0.1)
    y_full=分段线性(t,5,2,2,2,-4)
    y切=分段线性(t,5,3,2,2,-4)
    ####################
    #~plt.绘图(t,y_full)
    #~plt.绘图(t,y_-cut)
    #~plt.legend(['水面','深海']))
    ####################
    #~noise=np.random.normal(0,1,len(y_full))*1
    #~y=你吃饱了
    #~yy=y_满+噪音
    #~bounds=([0,0,0,0,-np.inf],[np.inf,np.inf,np.inf,np.inf,0])
    #~fit,u=sio.curve_fit(分段线性,t,yy,bounds=bounds)
    #~print(fit)
    #~y_fit=分段线性(t,*元组(fit))
    #~plt.plot(t,yy,color='0.5')
    #~plt.绘图(t,y_-fit,线宽=3)
    #~plt.plot(t,y,线型='--',线宽=3)
    ####################
    噪声=np.随机.正常(0,1,len(y_full))*1
    y=y_切
    yy=y_切削+噪声
    tPos=np.argmax(yy)
    t1Start=t[tPos]
    t0Start=t[tPos//2]
    bStart=yy[0]
    aStart=2
    m1Start=(yy[tPos]-yy[tPos//2])/(t1Start-t0Start)
    p0=[t0Start,aStart,bStart,m1Start,0])
    拟合,曲线拟合(分段线性,t,yy,p0=p0)
    打印(适合)
    y_拟合=分段线性(t,*元组(拟合))
    plt.绘图(t,yy,color='0.5')
    plt.绘图(t,y_拟合,线宽=3)
    plt.plt(t,y,线型='--',线宽=3)
    plt.图例([‘噪音数据’、‘拟合’、‘原始’])
    plt.show()
    
    它对测试数据起作用。必须记住,返回的拟合参数可能为负值。由于函数取模,因此也需要对返回的参数执行此操作。还要注意,
    t1
    不再直接安装,而是作为
    t0
    的倍数安装。因此,需要相应地传播错误。新结构不需要
    边界


    另外请注意,启动参数的选择
    p0
    也适用于情况1

    因为我不知道Python中使用的回归算法是什么,所以我无法真正回答您的问题。该算法可能像往常一样是一个迭代过程

    作为补充信息,我将展示一个非常简单的方法,它可以给出一个近似的答案,而无需迭代过程或初始猜测。基于积分方程拟合的理论可在中找到,分段函数的一些使用示例如下所示:

    对于由三个线性段组成的分段函数,微积分方法见上文第二篇第30页。写一个c是很容易的
    >>[ 1.83838997  0.40000014  1.51810839  2.56982348 -1.0622842 ]
    
    from lmfit import Model
    
    # make a model
    mymodel = Model(piecewise_linear)
    
    # create parameters and set initial values
    # note that parameters are *named* from the 
    # names of arguments of your model function
    params = mymodel.make_params(t0=0, t1=1, b=3, m1=2, m2=2)
    
    # now, you can place bounds on parameters, maybe like
    params['b'].min = 0
    params['m1'].min = 0
    
    # but what you want is an inequality constraint, so
    #   1. add a new parameter 'tdiff'
    #   2. constrain t1 = t0 + tdiff
    #   3. set a minimum value of 0 for tdiff
    params.add('tdiff', value=1, min=0)
    params['t1'].expr = 't0 + tdiff'
    
    # now perform the fit
    result = mymodel.fit(yy, params, t=t)
    
    # print out results
    print(result.fit_report())
    
    import numpy as np
    import matplotlib.pyplot as plt
    import scipy.optimize as sio
    
    """
     we know t0 > 0, t1 > t0, b>0, m1 > 0, m2 < 0
    """
    def piecewise_linear( t, t0, a , b, m1, m2 ):
        t0 = abs( t0 )
        t1 = abs( a ) * t0
        b = abs( b )
        m1 = abs( m1 )
        m2 = - abs( m2 )
        condlist = [ t < t0,
                     ( t >= t0 ) & ( t < t1 ),
                     t >= t1
                   ]
        funclist = [ lambda t: b,
                     lambda t: b + m1 * ( t - t0 ),
                     lambda t: b + m1 * ( t - t0 ) + m2 * ( t - t1 )
                   ]
        return np.piecewise( t, condlist, funclist )
    
    t = np.arange( 0, 15, 0.1 )
    y_full = piecewise_linear( t, 5, 2, 2, 2, -4 )
    y_cut = piecewise_linear( t, 5, 3, 2, 2, -4 )
    
    ####################
    
    #~ plt.plot( t, y_full )
    #~ plt.plot( t, y_cut )
    #~ plt.legend( [ 'surface', 'deep ocean'] )
    
    ####################
    
    #~ noise = np.random.normal( 0, 1, len( y_full ) ) * 1
    #~ y = y_full
    #~ yy = y_full + noise
    
    #~ bounds = ( [ 0, 0, 0, 0, -np.inf ], [ np.inf, np.inf, np.inf, np.inf, 0 ] )
    #~ fit,_ = sio.curve_fit( piecewise_linear, t, yy, bounds=bounds )
    #~ print( fit )
    #~ y_fit = piecewise_linear( t, *tuple( fit ) )
    #~ plt.plot( t, yy, color='0.5' )
    #~ plt.plot( t, y_fit, linewidth=3 )
    #~ plt.plot( t, y, linestyle='--', linewidth=3 )
    
    ####################
    
    noise = np.random.normal( 0, 1, len( y_full ) ) * 1
    y = y_cut
    yy = y_cut + noise
    tPos = np.argmax( yy )
    t1Start = t[ tPos ]
    t0Start = t[ tPos // 2 ]
    bStart = yy[ 0 ]
    aStart = 2
    m1Start = ( yy[ tPos ] - yy[ tPos // 2 ] ) / ( t1Start - t0Start )
    
    p0 = [ t0Start, aStart, bStart, m1Start, 0 ])
    fit,_ = sio.curve_fit( piecewise_linear, t, yy, p0=p0 )
    print( fit )
    y_fit = piecewise_linear( t, *tuple( fit ) )
    
    plt.plot( t, yy, color='0.5' )
    plt.plot( t, y_fit, linewidth=3 )
    plt.plot( t, y, linestyle='--', linewidth=3 )
    plt.legend( [ 'noisy data', 'fit', 'original' ] )
    plt.show()