线性拟合包括NumPy / SciPy的所有错误

时间:2014-05-30 10:01:07

标签: python numpy scipy

我有很多x-y数据点,y需要适应非线性函数。在某些情况下,这些函数可以是线性的,但更通常是指数衰减,高斯曲线等。 SciPy支持scipy.optimize.curve_fit这种拟合,我也可以指定每个点的权重。这给了我加权的非线性拟合,这很好。从结果中,我可以提取参数及其各自的错误。

只有一点需要注意:错误仅用作权重,但未包含在错误中。如果我将所有数据点的误差加倍,我预计结果的不确定性也会增加。所以我构建了一个测试用例(source code)来测试它。

适合scipy.optimize.curve_fit给我:

Parameters: [ 1.99900756  2.99695535]
Errors:     [ 0.00424833  0.00943236]

但与2 * y_err

相同
Parameters: [ 1.99900756  2.99695535]
Errors:     [ 0.00424833  0.00943236]

相同但有2 * y_err:

所以你可以看到值是相同的。这告诉我算法没有考虑到这些,但我认为值应该是不同的。

我也在这里阅读了另一种合适的方法,所以我也尝试使用scipy.odr

Beta: [ 2.00538124  2.95000413]
Beta Std Error: [ 0.00652719  0.03870884]

但与20 * y_err

相同
Beta: [ 2.00517894  2.9489472 ]
Beta Std Error: [ 0.00642428  0.03647149]

值略有不同,但我认为这可以解释错误的增加。我认为这只是围绕错误或有点不同的权重。

是否有一些软件包允许我调整数据并获得实际错误?我在书中有公式,但如果我不需要,我不想自己实现。


我现在在另一个问题中阅读了linfit.py。这很好地处理了我的想法。它支持两种模式,第一种是我需要的。

Fit with linfit:
Parameters: [ 2.02600849  2.91759066]
Errors:     [ 0.00772283  0.04449971]

Same but with 20 * y_err:
Parameters: [ 2.02600849  2.91759066]
Errors:     [ 0.15445662  0.88999413]

Fit with linfit(relsigma=True):
Parameters: [ 2.02600849  2.91759066]
Errors:     [ 0.00622595  0.03587451]

Same but with 20 * y_err:
Parameters: [ 2.02600849  2.91759066]
Errors:     [ 0.00622595  0.03587451]

我应该回答我的问题,还是现在关闭/删除它?

3 个答案:

答案 0 :(得分:4)

请注意,请参阅curvefit的文档:

  

sigma:无或N长度序列       如果不是None,则此向量将用作相对权重       最小二乘问题。

此处的关键点是作为相对权重,因此,第53行中的yerr和第57行中的2*yerr应该为您提供相似的结果(如果不是相同的话)。

当您增加实际残留错误时,您会看到协方差矩阵中的值变大。如果我们将y += random更改为y += 5*random中的generate_data()

Fit with scipy.optimize.curve_fit:
('Parameters:', array([ 1.92810458,  3.97843448]))
('Errors:    ', array([ 0.09617346,  0.64127574]))

与原始结果相比:

Fit with scipy.optimize.curve_fit:
('Parameters:', array([ 2.00760386,  2.97817514]))
('Errors:    ', array([ 0.00782591,  0.02983339]))

另请注意,参数估计值现在远离(2,3),正如我们预期的那样,残差误差增加,参数估计值的置信区间更大。

答案 1 :(得分:3)

一种运行良好且实际上提供更好结果的方法是bootstrap方法。当给出有错误的数据点时,使用参数自举,并让每个xy值描述高斯分布。然后,我们将从每个分布中绘制一个点,并获得一个新的自举样本。执行简单的未加权拟合可为参数提供一个值。

这个过程重复大约300到几千次。最终将得到拟合参数的分布,其中可以采用均值和标准差来获得值和误差。

另一个巧妙的事情是,结果并没有获得单一的拟合曲线,而是很多。对于每个插值x值,可以再次获取多个值f(x, param)的均值和标准差,并获得错误带:

enter image description here

然后使用各种拟合参数再次进行数百次分析中的其他步骤。然后,这将考虑拟合参数的相关性,如上图所示:尽管对称函数适合于数据,但误差带是不对称的。这意味着左边的内插值比右边的内插值更大。

答案 2 :(得分:1)

简短回答

对于包含y的不确定性的绝对值(和odr情况下的x):

  • scipy.odr案例中使用stddev = numpy.sqrt(numpy.diag(cov)) 其中cov是输出中odr给出的协方差矩阵。
  • scipy.optimize.curve_fit案例中使用absolute_sigma=True
    标志。

对于相对值(不包括不确定性):

  • scipy.odr案例中,使用输出中的sd值。

  • 在scipy.optimize.curve_fit案例中使用absolute_sigma=False标记。

  • 像这样使用numpy.polyfit:

p, cov = numpy.polyfit(x, y, 1,cov = True) errorbars = numpy.sqrt(numpy.diag(cov))

答案很长

所有功能中都有一些未记录的行为。我的猜测是混合相对值和绝对值的函数。最后,这个答案是代码,根据你处理输出的方式给出你想要的东西(或者没有)(有一个bug?)。此外,curve_fit可能已经获得了'absolute_sigma'最近举旗?

我的观点是在输出中。似乎odr计算标准差,因为没有不确定性,类似于polyfit,但如果标准偏差是根据协方差矩阵计算的,则存在不确定性。 curve_fit使用absolute_sigma=True标志执行此操作。以下是包含

的输出
  1. 协方差矩阵cov(0,0)和
  2. 的对角元素
  3. COV(1,1),
  4. 错误的方式,用于斜率和
  5. 输出的标准偏差
  6. 错误常量的方式,
  7. 正确方式,以获得斜率和
  8. 输出的标准偏差
  9. 正确常量
  10. 的方式

    odr: 1.739631e-06 0.02302262 [ 0.00014863 0.0170987 ] [ 0.00131895 0.15173207] curve_fit: 2.209469e-08 0.00029239 [ 0.00014864 0.01709943] [ 0.0004899 0.05635713] polyfit: 2.232016e-08 0.00029537 [ 0.0001494 0.01718643]

    请注意,odr和polyfit具有完全相同的标准偏差。 Polyfit 将不确定性作为输入,因此 odr在计算标准差时不会使用不确定性。协方差矩阵使用它们,如果在情况下,则根据协方差矩阵计算标准差,并且如果不确定性增加则它们会发生变化。在下面的代码中摆弄dy将显示它。

    我在这里写这篇文章主要是因为知道何时找出错误限制很重要(而且scipy所指的fortran odrpack指南有一些误导性的信息:标准差应该是协方差矩阵的平方根,如指南所说但它不是)。

    import scipy.odr
    import scipy.optimize
    import numpy
    
    x = numpy.arange(200)
    y = x + 0.4*numpy.random.random(x.shape)
    dy = 0.4
    
    def stddev(cov): return numpy.sqrt(numpy.diag(cov))
    
    def f(B, x): return B[0]*x + B[1]
    
    linear = scipy.odr.Model(f) 
    mydata = scipy.odr.RealData(x, y,  sy = dy)
    myodr = scipy.odr.ODR(mydata, linear, beta0 = [1.0, 1.0], sstol = 1e-20, job=00000)
    myoutput = myodr.run()
    cov = myoutput.cov_beta
    sd  = myoutput.sd_beta
    p   = myoutput.beta 
    print 'odr:        ', cov[0,0], cov[1,1], sd, stddev(cov)
    
    p2, cov2 = scipy.optimize.curve_fit(lambda x, a, b:a*x+b, 
                                        x, y, [1,1],
                                        sigma = dy,
                                        absolute_sigma = False,
                                        xtol = 1e-20)
    
    p3, cov3 = scipy.optimize.curve_fit(lambda x, a, b:a*x+b, 
                                        x, y, [1,1],
                                        sigma = dy,
                                        absolute_sigma = True,
                                        xtol = 1e-20)
    
    print 'curve_fit:  ', cov2[0,0], cov2[1,1], stddev(cov2), stddev(cov3)
    
    p, cov4 = numpy.polyfit(x, y, 1,cov = True)
    print 'polyfit:    ', cov4[0,0], cov4[1,1], stddev(cov4)