Scipy.optimize — подгонка кривой с фиксированными параметрами

#numpy #scipy #curve-fitting

#numpy #scipy #подгонка кривой

Вопрос:

Я выполняю подгонку кривой с scipy.optimize.leastsq помощью . Например. для гауссовского:

 def fitGaussian(x, y, init=[1.0,0.0,4.0,0.1]):
    fitfunc = lambda p, x: p[0]*np.exp(-(x-p[1])**2/(2*p[2]**2)) p[3] # Target function
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Distance to the target function
    final, success = scipy.optimize.leastsq(errfunc, init[:], args=(x, y))
    return fitfunc, final
  

Теперь я хочу при необходимости исправить значения некоторых параметров в подгонке. Я обнаружил, что предложения заключаются в использовании другого пакета lmfit, которого я хочу избежать, или являются очень общими, как здесь .
Поскольку мне нужно решение, которое

  1. работает с numpy / scipy (никаких дополнительных пакетов и т. Д.)
  2. не зависит от самих параметров,
  3. является гибким, в котором параметры фиксированы или нет,

Я придумал следующее, используя условие для каждого из параметров:

 def fitGaussian2(x, y, init=[1.0,0.0,4.0,0.1], fix = [False, False, False, False]):
    fitfunc = lambda p, x: (p[0] if not fix[0] else init[0])*np.exp(-(x-(p[1] if not fix[1] else init[1]))**2/(2*(p[2] if not fix[2] else init[2])**2)) (p[3] if not fix[3] else init[3]) 
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Distance to the target function
    final, success = scipy.optimize.leastsq(errfunc, init[:], args=(x, y))
    return fitfunc, final
  

Хотя это работает нормально, это не практично и не красиво.
Итак, мой вопрос: существуют ли лучшие способы выполнения подгонки кривой в scipy для фиксированных параметров? Или существуют обертки, которые уже включают такое исправление параметров?

Комментарии:

1. Было бы неплохо иметь эту функциональность в scipy, но я не уверен, существует ли она. Из любопытства, почему вы хотите избежать lmfit? По моему опыту, это довольно просто использовать.

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

Ответ №1:

При использовании scipy нет встроенных опций, о которых я знаю. Вам всегда придется выполнять обходной путь, подобный тому, который вы уже сделали.

Однако, если вы готовы использовать пакет-оболочку, могу ли я порекомендовать свой собственный symfit ? Это оболочка scipy с удобочитаемостью и меньшим количеством шаблонного кода в качестве основных принципов. В symfit , ваша проблема будет решена как:

 from symfit import parameters, variables, exp, Fit, Parameter

a, b, c, d = parameters('a, b, c, d')
x, y = variables('x, y')

model_dict = {y: a * exp(-(x - b)**2 / (2 * c**2))   d}

fit = Fit(model_dict, x=xdata, y=ydata)
fit_result = fit.execute()
  

Линия a, b, c, d = parameters('a, b, c, d') образует четыре Parameter объекта. Чтобы, например, вернуть параметру c его начальное значение, выполните следующие действия в любом месте перед вызовом fit.execute() :

 c.value = 4.0
c.fixed = True
  

Таким образом, возможным конечным результатом может быть:

 from symfit import parameters, variables, exp, Fit, Parameter

a, b, c, d = parameters('a, b, c, d')
x, y = variables('x, y')

c.value = 4.0
c.fixed = True

model_dict = {y: a * exp(-(x - b)**2 / (2 * c**2))   d}

fit = Fit(model_dict, x=xdata, y=ydata)
fit_result = fit.execute()
  

Если вы хотите быть более динамичным в своем коде, вы можете Parameter сразу создавать объекты с помощью:

 c = Parameter(4.0, fixed=True)
  

Для получения дополнительной информации ознакомьтесь с документами: http://symfit.readthedocs.io/en/latest/tutorial.html#simple-example

Ответ №2:

Приведенный выше пример с использованием symfit, несомненно, будет просто синтаксисом подхода подгонки, однако действительно ли приведенный пример ограничивает переменную c?

Если вы посмотрите fit_result.param , вы получите следующее:

OrderedDict([('a', 16.374368575343127),
('b', 0.49201249437123556),
('c', 0.5337962977235504),
('d', -9.55593614465743)])

Параметр c не равен 4.0.

Комментарии:

1. На самом деле это не ответ, не так ли? Вместо этого вы можете прокомментировать другой ответ, чтобы попросить разъяснений. Но в любом случае symfit — это не то, что задает этот вопрос.

2. Это оказалось ошибкой в версии 0.4.0 и теперь исправлено :). Но @ImportanceOfBeingErnest прав в том, что это должен быть комментарий к моему ответу, а не отдельный ответ.