Линейная пригонка включая все ошибки с NumPy/SciPy

у меня есть много точек данных x-y с ошибками на y, которые мне нужно соответствовать нелинейным функциям. Эти функции могут быть линейными в некоторых случаях, но чаще являются экспоненциальным распадом, кривыми Гаусса и т. д. SciPy поддерживает этот вид подгонки с scipy.optimize.curve_fit, и я также могу указать вес каждой точки. Это дает мне взвешенный нелинейный фитинг, который великолепен. Из результатов я могу извлечь параметры и их соответствующие ошибки.

есть только одно предостережение: ошибки используются только в качестве весов, но не включены в ошибку. Если я удвою ошибки во всех моих точках данных, я ожидаю, что неопределенность результата также увеличится. Поэтому я построил тестовый пример (исходный код), чтобы проверить это.

Fit с 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 ответов


Пожалуйста, обратите внимание, что из документации curvefit:

Сигма: нет или N-длина последовательности Если нет, этот вектор будет использоваться в качестве относительного веса в проблема наименьших квадратов.

ключевым моментом здесь является как относительные веса, поэтому yerr в строке 53 и 2*yerr в 57 должен дать вам аналогичный, если не тот же результат.

при увеличении на самом деле ошибка остатков , вы увидите, что значения в ковариационной матрице увеличиваются. Скажите, если мы изменим y += random to 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), как и следовало ожидать от увеличенной ошибки остатка и большего доверительного интервала оценок параметров.


один из способов, который работает хорошо и на самом деле дает лучший результат, - это метод bootstrap. Когда даны точки данных с ошибками, используется параметрический бутстрап и пусть каждый x и y значение опишите гауссово распределение. Затем один из них нарисует точку из каждого из этих дистрибутивов и получит новый загрузочный образец. Выполнение простой невзвешенной подгонки дает одно значение для параметров.

этот процесс повторяется от 300 до нескольких тысяч раз. Одна воля в конечном итоге с распределением параметров подгонки, где можно взять среднее и стандартное отклонение для получения значения и ошибки.

еще одна аккуратная вещь заключается в том, что в результате не получается ни одной кривой подгонки, но их много. Для каждого интерполированного x значение можно снова взять среднее и стандартное отклонение многих значений f(x, param) и получить диапазон ошибок:

enter image description here

дальнейшие шаги в анализе затем снова выполняется сотни раз с различными параметрами подгонки. Это также будет учитывать корреляцию параметров подгонки, как ясно видно на графике выше: хотя симметричная функция была установлена на данные, полоса ошибок асимметрична. Это будет означать, что интерполированные значения слева имеют большую неопределенность, чем справа.


короткий ответ:

для абсолютных значений, которые включают неопределенность в y (и в X для случая odr):

  • на scipy.odr case use stddev = numpy.sqrt(numpy.diag(cov)) где cov - ковариационная матрица, которую odr дает на выходе.
  • на scipy.optimize.curve_fit пример использования absolute_sigma=True
    флаг.

для относительных значений (исключая неопределенность):

  • на scipy.odr случае используйте значение sd из выходных данных.

  • В составляющей.оптимизировать.curve_fit case use absolute_sigma=False флаг.

  • используйте numpy.polyfit такой:

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

ответ

существует некоторое недокументированное поведение во всех функциях. Я предполагаю, что функции смешивают относительные и абсолютные значения. В конце этот ответ-это код, который либо дает то, что вы хотите (или нет) на основе того, как вы обрабатываете вывод (есть ошибка?). Кроме того, curve_fit, возможно, недавно получил флаг "absolute_sigma"?

моя точка находится в выходе. Кажется, что odr вычисляет стандартное отклонение, поскольку нет неопределенностей, подобных polyfit, но если стандартное отклонение вычисляется из ковариационной матрицы, неопределенности существуют. Curve_fit делает это с absolute_sigma=True флаг. Ниже приведен вывод, содержащий

  1. диагональные элементы ковариационной матрицы cov (0,0) и
  2. ков(1,1),
  3. неправильно путь для стандартного отклонения от выходов для наклона и
  4. неправильно путь для постоянного и
  5. право путь для стандартного отклонения от выходов для наклона и
  6. право путь к константа

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 не использует неопределенности при расчете стандартного отклонения. Ковариационная матрица использует их, и если в случае odr стандартное отклонение вычисляется из неопределенности ковариационной матрицы, они изменяются при увеличении неопределенности. Возиться с dy в коде ниже покажет это.

я пишу это здесь в основном потому, что это важно знать при поиске пределов ошибок (и руководство fortran odrpack, куда ссылается scipy, имеет некоторую вводящую в заблуждение информацию об этом: стандартное отклонение должно быть квадратным корнем ковариационной матрицы, как говорит руководство, но это не так).

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)