【问题标题】:Curve fit exponential growth function in PythonPython中的曲线拟合指数增长函数
【发布时间】:2018-10-07 13:40:12
【问题描述】:

我想要曲线拟合以下数据点:

import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit

t = np.array([15474.6, 15475.6, 15476.6, 15477.6, 15478.6, 15479.6, 15480.6,
              15481.6, 15482.6, 15483.6, 15484.6, 15485.6, 15486.6, 15487.6,
              15488.6, 15489.6, 15490.6, 15491.6, 15492.6, 15493.6, 15494.6,
              15495.6, 15496.6, 15497.6, 15498.6, 15499.6, 15500.6, 15501.6,
              15502.6, 15503.6, 15504.6, 15505.6, 15506.6, 15507.6, 15508.6,
              15509.6, 15510.6, 15511.6, 15512.6, 15513.6])

v = np.array([4.082, 4.133, 4.136, 4.138, 4.139, 4.14, 4.141, 4.142, 4.143,
              4.144, 4.144, 4.145, 4.145, 4.147, 4.146, 4.147, 4.148, 4.148,
              4.149, 4.149, 4.149, 4.15, 4.15, 4.15, 4.151, 4.151, 4.152,
              4.152, 4.152, 4.153, 4.153, 4.153, 4.153, 4.154, 4.154, 4.154,
              4.154, 4.154, 4.155, 4.155])

我想要拟合数据的指数函数是:

表示上述公式的Python函数以及与数据相关的曲线拟合详述如下:

def func(t, a, b, alpha):
    return a - b * np.exp(-alpha * t)


# scale vector to start at zero otherwise exponent is too large
t_scale = t - t[0]

# initial guess for curve fit coefficients
a0 = v[-1]
b0 = v[0]
alpha0 = 1/t_scale[-1]

# coefficients and curve fit for curve
popt4, pcov4 = curve_fit(func, t_scale, v, p0=(a0, b0, alpha0))

a, b, alpha = popt4
v_fit = func(t_scale, a, b, alpha)

ss_res = np.sum((v - v_fit) ** 2)       # residual sum of squares
ss_tot = np.sum((v - np.mean(v)) ** 2)  # total sum of squares
r2 = 1 - (ss_res / ss_tot)              # R squared fit, R^2

与曲线拟合相比的数据如下图所示。还提供了参数和R平方值。

a0 = 4.1550   b0 = 4.0820   alpha0 = 0.0256
a = 4.1490    b = 0.0645    alpha = 0.9246
R² = 0.8473

是否可以使用上述方法更好地拟合数据,还是我需要使用不同形式的指数方程?

我也不确定将什么用于初始值(a0b0alpha0)。在示例中,我从数据中选择了点,但这可能不是最好的方法。关于曲线拟合系数的初始猜测使用什么建议?

【问题讨论】:

  • 我会尝试 1. 不适合通过峰值和 2. 将权重添加到一些会拖累拟合的点。您可以使用 sigma kwarg 为 curve_fit
  • @user3014097 我不知道sigma 关键字。我如何将它应用到我的示例中?此外,在峰值处开始曲线拟合(t[1:]v[1:])效果很好,但这会对popt 值产生多大影响?
  • 方程图右侧平坦,而数据显示明显向上倾斜。我认为这个方程不能很好地拟合这部分数据。

标签: python numpy scipy curve-fitting exponential


【解决方案1】:

在我看来,这看起来更适合多个组件,而不是单个指数。

def func(t, a, b, c, d, e):
    return a*np.exp(-t/b) + c*np.exp(-t/d) + e


# scale vector to start at zero otherwise exponent is too large
t_scale = t - t[0]

# initial guess for curve fit coefficients
guess = [1, 1, 1, 1, 0]

# coefficients and curve fit for curve
popt, pcov = curve_fit(func, t_scale, v, p0=guess)

v_fit = func(t_scale, *popt)

【讨论】:

  • 您使用的方程式提供了更好的拟合。但是,该解决方案似乎对最初的猜测很敏感。例如,a、c 和 e 初始值可以是 v[-1]、v[0] 和 v[-1]。有没有对初始值不那么敏感的方法?
  • 为了使初始值与我原来的问题相似,看起来您的示例中ace 的初始猜测应该是来自v[-1] 的值。这看起来合理吗?
  • 我不确定您最初的问题是什么。想想等式在 t = 0 时的情况。本质上,它应该是两个分量的最小值加上偏移量 (E) 的总和。只要你的猜测是正确的,你应该没问题。通常,当我进行这种拟合时,我更喜欢将时间(你做了)和我的基线(所以,在你的场景v_scale = v - v.max())都归零,这样你就会衰减到 0。不过,只要因为你在猜测你应该没问题。如果 v 变化很大,我会在拟合之前对其进行规范化
  • 另一种解决方案是将 v 转换为线性,这将使拟合步骤更简单。
  • 您指的是线性回归,您将在其中获取方程的对数并拟合该形式?你能举个例子吗?感谢所有的帮助。
【解决方案2】:

我能找到的最好的单个 3 参数方程,R-squared = 0.9952,是一个 x 位移幂函数:

y = pow((a + x), b) + Offset

带参数:

a = -1.5474599569484271E+04
b =  6.3963649365056151E-03
Offset =  3.1303674911990789E+00

【讨论】:

    【解决方案3】:

    如果你删除第一个数据点,你会得到更好的拟合。

    使用 lmfit (https://lmfit.github.io/lmfit-py),它提供了一个更高级且更易于使用的曲线拟合界面,您的拟合脚本将如下所示:

    import matplotlib.pyplot as plt
    import numpy as np
    from lmfit import Model
    
    t = np.array([15474.6, 15475.6, 15476.6, 15477.6, 15478.6, 15479.6, 15480.6,
                  15481.6, 15482.6, 15483.6, 15484.6, 15485.6, 15486.6, 15487.6,
                  15488.6, 15489.6, 15490.6, 15491.6, 15492.6, 15493.6, 15494.6,
                  15495.6, 15496.6, 15497.6, 15498.6, 15499.6, 15500.6, 15501.6,
                  15502.6, 15503.6, 15504.6, 15505.6, 15506.6, 15507.6, 15508.6,
                  15509.6, 15510.6, 15511.6, 15512.6, 15513.6])
    
    v = np.array([4.082, 4.133, 4.136, 4.138, 4.139, 4.14, 4.141, 4.142, 4.143,
                  4.144, 4.144, 4.145, 4.145, 4.147, 4.146, 4.147, 4.148, 4.148,
                  4.149, 4.149, 4.149, 4.15, 4.15, 4.15, 4.151, 4.151, 4.152,
                  4.152, 4.152, 4.153, 4.153, 4.153, 4.153, 4.154, 4.154, 4.154,
                  4.154, 4.154, 4.155, 4.155])
    
    def func(t, a, b, alpha):
        return a + b * np.exp(-alpha * t)
    
    # remove first data point, take offset from t
    tx = t[1:] - t[0]
    vx = v[1:]
    
    # turn your model function into a Model
    amodel = Model(func)
    # create parameters with initial values.  Note that parameters
    # are named from the arguments of your model function.
    params = amodel.make_params(a=v[0], b=0, alpha=1.0/(t[-1]-t[0]))
    
    # fit the data to the model with the parameters
    result = amodel.fit(vx, params, t=tx)
    
    # print the fit statistics and resulting parameters
    print(result.fit_report())
    
    # plot data and fit
    plt.plot(t, v, 'o', label='data')
    plt.plot(t, result.eval(result.params, t=(t-t[0])), '--', label='fit')
    plt.legend()
    plt.show()
    

    这将打印出这些结果

    [[Model]]
        Model(func)
    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 44
        # data points      = 39
        # variables        = 3
        chi-square         = 1.1389e-05
        reduced chi-square = 3.1635e-07
        Akaike info crit   = -580.811568
        Bayesian info crit = -575.820883
    [[Variables]]
        a:      4.15668660 +/- 5.0662e-04 (0.01%) (init = 4.082)
        b:     -0.02312772 +/- 4.1930e-04 (1.81%) (init = 0)
        alpha:  0.06004740 +/- 0.00360126 (6.00%) (init = 0.02564103)
    [[Correlations]] (unreported correlations are < 0.100)
        C(a, alpha) = -0.945
        C(a, b)     = -0.682
        C(b, alpha) =  0.465
    

    并显示此图以进行拟合:

    【讨论】:

    • 为了比较,您能否使用与 OP 在他们的问题中使用的图形相同的图形缩放比例重新绘制?
    • 我不明白为什么有人反对这个答案。 “删除第一点”是我看到问题中的图表时首先想到的。这个答案让我省去了找出它的工作效果的麻烦——结果很好!
    • @JamesPhillips 这是一个很好的建议。我更新了情节以包括第一点。这确实说明了该点有多大的异常值。
    • 如果第一点和第二点之间有更多的数据,那将是另一回事,但这里不是这样。如果可能的话,我建议在这个范围内获取额外的数据。由于缺乏这些额外数据,您有充分的理由通过回归分析删除该点。感谢您更新的情节。
    • @wigging 对我来说,第一点看起来像是一个异常值。您可以使用t[:]v[:] 而不是t[1:]v[1:] 轻松检查包含它的结果。卡方将增加约 75 倍(更差的拟合),a 约为 4.1,b 约为 -0.064,alpha 约为 0.92。合身看起来和你原来的问题一样糟糕。正如此处的其他答案所指出的那样,第一点的数据与您的模型不匹配——他们提出了其他更好的模型。忽略第一点,你的数据可以匹配你的模型。
    猜你喜欢
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-13
    • 2014-09-08
    • 2023-04-09
    • 2021-01-30
    • 2020-07-29
    相关资源
    最近更新 更多