scipy 无初始猜测的拟合指数衰减

esbemjvw  于 2022-11-09  发布在  其他
关注(0)|答案(8)|浏览(161)

有没有人知道一个scipy/numpy模块,这将允许拟合指数衰减的数据?
Google搜索返回了一些blog文章,例如-http://exnumerus.blogspot.com/2010/04/how-to-fit-exponential-decay-example-in.html,但是该解决方案需要预先指定y偏移,这并不总是可行的
编辑:
curve_fit可以工作,但是如果没有对参数进行初始猜测,它可能会失败得很惨,这有时是需要的。


# !/usr/bin/env python

import numpy as np
import scipy as sp
import pylab as pl
from scipy.optimize.minpack import curve_fit

x = np.array([  50.,  110.,  170.,  230.,  290.,  350.,  410.,  470.,  
530.,  590.])
y = np.array([ 3173.,  2391.,  1726.,  1388.,  1057.,   786.,   598.,   
443.,   339.,   263.])

smoothx = np.linspace(x[0], x[-1], 20)

guess_a, guess_b, guess_c = 4000, -0.005, 100
guess = [guess_a, guess_b, guess_c]

exp_decay = lambda x, A, t, y0: A * np.exp(x * t) + y0

params, cov = curve_fit(exp_decay, x, y, p0=guess)

A, t, y0 = params

print "A = %s\nt = %s\ny0 = %s\n" % (A, t, y0)

pl.clf()
best_fit = lambda x: A * np.exp(t * x) + y0

pl.plot(x, y, 'b.')
pl.plot(smoothx, best_fit(smoothx), 'r-')
pl.show()

这是可行的,但如果我们删除“p0=guess”,它会失败得很惨。

2exbekwf

2exbekwf1#

您有两个选择:
1.使系统线性化,并在数据日志上拟合一条线。
1.使用非线性求解程序(例如scipy.optimize.curve_fit
第一个选项是目前为止最快和最稳健的。但是,它要求您事先知道y偏移,否则不可能线性化方程。(即,y = A * exp(K * t)可以通过拟合y = log(A * exp(K * t)) = K * t + log(A)线性化,但y = A*exp(K*t) + C只能通过拟合y - C = K*t + log(A)线性化,并且由于y是自变量,C必须事先已知,以使其成为线性系统。
如果使用非线性方法,则a)不能保证收敛并得到解,B)速度会慢得多,c)对参数的不确定性的估计会差得多,d)精度通常会低得多。然而,非线性方法与线性反演相比有一个巨大的优势:它可以解一个非线性方程组,在你的例子中,这意味着你不必事先知道C
仅给予一个例子,让我们使用线性和非线性方法,用一些噪声数据求解y = A * exp(K * t):

import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.optimize

def main():
    # Actual parameters
    A0, K0, C0 = 2.5, -4.0, 2.0

    # Generate some data based on these
    tmin, tmax = 0, 0.5
    num = 20
    t = np.linspace(tmin, tmax, num)
    y = model_func(t, A0, K0, C0)

    # Add noise
    noisy_y = y + 0.5 * (np.random.random(num) - 0.5)

    fig = plt.figure()
    ax1 = fig.add_subplot(2,1,1)
    ax2 = fig.add_subplot(2,1,2)

    # Non-linear Fit
    A, K, C = fit_exp_nonlinear(t, noisy_y)
    fit_y = model_func(t, A, K, C)
    plot(ax1, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, C0))
    ax1.set_title('Non-linear Fit')

    # Linear Fit (Note that we have to provide the y-offset ("C") value!!
    A, K = fit_exp_linear(t, y, C0)
    fit_y = model_func(t, A, K, C0)
    plot(ax2, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, 0))
    ax2.set_title('Linear Fit')

    plt.show()

def model_func(t, A, K, C):
    return A * np.exp(K * t) + C

def fit_exp_linear(t, y, C=0):
    y = y - C
    y = np.log(y)
    K, A_log = np.polyfit(t, y, 1)
    A = np.exp(A_log)
    return A, K

def fit_exp_nonlinear(t, y):
    opt_parms, parm_cov = sp.optimize.curve_fit(model_func, t, y, maxfev=1000)
    A, K, C = opt_parms
    return A, K, C

def plot(ax, t, y, noisy_y, fit_y, orig_parms, fit_parms):
    A0, K0, C0 = orig_parms
    A, K, C = fit_parms

    ax.plot(t, y, 'k--', 
      label='Actual Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A0, K0, C0))
    ax.plot(t, fit_y, 'b-',
      label='Fitted Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A, K, C))
    ax.plot(t, noisy_y, 'ro')
    ax.legend(bbox_to_anchor=(1.05, 1.1), fancybox=True, shadow=True)

if __name__ == '__main__':
    main()

请注意,线性解决方案提供的结果更接近实际值。但是,我们必须提供y偏移值才能使用线性解决方案。非线性解决方案不需要这种先验知识。

nwlls2ji

nwlls2ji2#

拟合指数的过程,无需初始猜测,无需迭代过程:

这来自论文(pp.16-17):https://fr.scribd.com/doc/14674814/Regressions-et-equations-integrales
如果需要,这可以用于初始化非线性回归演算,以便选择特定的优化标准。
示例:
乔·金顿给出的例子很有趣。不幸的是,数据没有显示,只显示了图表。因此,下面的数据(x,y)来自图表的图形扫描,因此,数值可能不完全是乔·金顿使用的那些。然而,考虑到点的广泛分散,“拟合”曲线的相应方程彼此非常接近。

上图是金顿图的副本。
下图显示了用上述程序获得的结果。
UPDATE:一个变体

ckx4rj1h

ckx4rj1h3#

我会使用scipy.optimize.curve_fit函数,它的doc字符串甚至有一个拟合指数衰减的例子,我将复制到这里:

>>> import numpy as np
>>> from scipy.optimize import curve_fit
>>> def func(x, a, b, c):
...     return a*np.exp(-b*x) + c

>>> x = np.linspace(0,4,50)
>>> y = func(x, 2.5, 1.3, 0.5)
>>> yn = y + 0.2*np.random.normal(size=len(x))

>>> popt, pcov = curve_fit(func, x, yn)

拟合的参数会因随机噪声的加入而变化,但我得到的a,b和c分别是2.47990495,1.40709306,0.53753635,所以在噪声的情况下,这还不算太糟,如果我拟合到y而不是yn,我得到的是精确的a,b和c值。

hxzsmxv2

hxzsmxv24#

正确的方法是进行Prony估计,并将结果作为最小二乘拟合(或其他更稳健的拟合例程)的初始猜测。Prony估计不需要初始猜测,但它确实需要许多点来产生良好的估计。
以下是概述
http://www.statsci.org/other/prony.html
在Octave中,它被实现为expfit,因此您可以基于Octave库函数编写自己的例程。
Prony估计确实需要知道偏移量,但如果您深入到衰减中“足够远”,就可以对偏移量进行合理的估计,因此您只需移动数据,将偏移量设置为0。无论如何,Prony估计只是为其他拟合例程获得合理初始猜测的一种方法。

6yt4nkrj

6yt4nkrj5#

我从来没有让curve_fit正常工作过,就像你说的,我不想猜测任何东西。我试图简化Joe Kington的例子,这就是我的工作。我的想法是把“嘈杂”的数据转换成log,然后再转换回来,并使用polyfit和polyval来计算参数:

model = np.polyfit(xVals, np.log(yVals) , 1);   
splineYs = np.exp(np.polyval(model,xVals[0]));
pyplot.plot(xVals,yVals,','); #show scatter plot of original data
pyplot.plot(xVals,splineYs('b-'); #show fitted line
pyplot.show()

其中xVals和yVals只是列表。

trnvg8h3

trnvg8h36#

我不懂python,但我知道一种简单的方法,可以通过非迭代的方式估计带有偏移量的指数衰减系数,给定三个数据点,它们的独立坐标具有固定的差值。您的数据点在它们的独立坐标中具有固定的差值(您的x值间隔为60),因此我的方法可以应用于它们。您当然可以将数学转换为python。
假设

y = A + B*exp(-c*x) = A + B*C^x

其中C = exp(-c)
给定y_0,y_1,y_2,对于x = 0,1,2,我们求解

y_0 = A + B
y_1 = A + B*C
y_2 = A + B*C^2

按如下方式求出A、B、C:

A = (y_0*y_2 - y_1^2)/(y_0 + y_2 - 2*y_1)
B = (y_1 - y_0)^2/(y_0 + y_2 - 2*y_1)
C = (y_2 - y_1)/(y_1 - y_0)

对应的指数正好经过三个点(0,y_0)、(1,y_1)和(2,y_2)。如果数据点不在x坐标0、1、2处,而是在k、k + s和k + 2*s处,则

y = A′ + B′*C′^(k + s*x) = A′ + B′*C′^k*(C′^s)^x = A + B*C^x

所以可以用上面的公式求出A,B,C,然后计算

A′ = A
C′ = C^(1/s)
B′ = B/(C′^k)

产生的系数对y坐标中的误差非常敏感,如果外推超出三个所用数据点定义的范围,则可能导致较大误差,因此最好从尽可能远的三个数据点(同时它们之间仍具有固定距离)计算A、B、C。
您的数据集有10个等距数据点。让我们挑选三个数据点(第110页,第2391页)(第350、786页)(590,263)用于使用― ―这些具有最大可能的固定距离(240),则在独立坐标系中,y_o = 2391,y_l = 786,y_2 = 263,k = 110,s = 240,则A = B = 2380.799,C = 0.3258567,A ′ = 10.20055,B′ = 3980.329,C′ = 0.9953388。指数为

y = 10.20055 + 3980.329*0.9953388^x = 10.20055 + 3980.329*exp(-0.004672073*x)

您可以使用此指数作为非线性拟合算法中的初始猜测值。
计算A的公式与Shanks变换(http://en.wikipedia.org/wiki/Shanks_transformation)所使用的公式相同。

gzjq41n4

gzjq41n47#

@ Jacquelin的解决方案的Python实现。我需要一个近似的基于非解决方案的解决方案,没有初始猜测,所以@ Jacquelin的答案真的很有帮助。最初的问题是作为python numpy/scipy请求提出的。我采用了@johanvdw的漂亮干净的R代码,并将其重构为python/numpy。希望对某些人有用:https://gist.github.com/friendtogeoff/00b89fa8d9acc1b2bdf3bdb675178a29

import numpy as np

"""
compute an exponential decay fit to two vectors of x and y data
result is in form y = a + b * exp(c*x).
ref. https://gist.github.com/johanvdw/443a820a7f4ffa7e9f8997481d7ca8b3
"""
def exp_est(x,y):
    n = np.size(x)
    # sort the data into ascending x order
    y = y[np.argsort(x)]
    x = x[np.argsort(x)]

    Sk = np.zeros(n)

    for n in range(1,n):
        Sk[n] = Sk[n-1] + (y[n] + y[n-1])*(x[n]-x[n-1])/2
    dx = x - x[0]
    dy = y - y[0]

    m1 = np.matrix([[np.sum(dx**2), np.sum(dx*Sk)],
                    [np.sum(dx*Sk), np.sum(Sk**2)]])
    m2 = np.matrix([np.sum(dx*dy), np.sum(dy*Sk)])

    [d, c] = (m1.I * m2.T).flat

    m3 = np.matrix([[n,                  np.sum(np.exp(  c*x))],
                    [np.sum(np.exp(c*x)),np.sum(np.exp(2*c*x))]])

    m4 = np.matrix([np.sum(y), np.sum(y*np.exp(c*x).T)])

    [a, b] = (m3.I * m4.T).flat

    return [a,b,c]
vi4fp9gy

vi4fp9gy8#

如果衰减不是从0开始,请用途:

popt, pcov = curve_fit(self.func, x-x0, y)

其中x0是衰减的起点(拟合的起点)。然后再次使用x0进行绘图:

plt.plot(x, self.func(x-x0, *popt),'--r', label='Fit')

其中函数为:

def func(self, x, a, tau, c):
        return a * np.exp(-x/tau) + c

相关问题