Подгонка нескольких наборов данных с использованием lmfit без написания целевой функции

В этом разделе описывается, как согласовать несколько наборов данных с помощью lmfit: Python и lmfit: Как сопоставить несколько наборов данных с общими параметрами?

Однако он использует подходящую / целевую функцию, написанную пользователем.

Мне было интересно, можно ли разместить несколько наборов данных, используя lmfit, не написав целевой функции и не используя метод model.fit() класса модели.

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

import numpy as np 
from lmfit import Model, Parameters
from lmfit.models import GaussianModel

def gauss(x, amp, cen, sigma):
    return amp*np.exp(-(x-cen)**2/(2.*sigma**2))

x1= np.arange(0.,100.,0.1)
x2= np.arange(0.,100.,0.09)
y1= gauss(x1, 1.,50.,5.)+ np.random.normal(size=len(x1), scale=0.1)
y2= gauss(x2, 0.8,48.4.,4.5)+ np.random.normal(size=len(x2), scale=0.1)

mod= GaussianModel()
params= mod.make_params()

mod.fit([y1,y2], params, x= [x1, x2])

Я думаю, если это возможно, данные должны быть переданы в mod.fit в правильном типе. В документации только сказано, что mod.fit принимает ввод данных в виде массива.

Я пытался дать ему списки и массивы. Если я передаю различные наборы данных в виде списка, я получаю ValueError: установка элемента массива с последовательностью

Если я передаю массив, я получаю AttributeError: "numpy.ndarray" не имеет атрибута "exp"

Так что я просто пытаюсь сделать что-то невозможное или я делаю что-то не так?

2 ответа

Ну, я думаю, что ответ "вроде". lmfit.Model класс предназначен для представления модели для массива данных. Так что, если вы можете отобразить несколько наборов данных в Numpy ndarray (скажем, с np.concatenate), вы, вероятно, можете написать функцию Model для представления этого, создав подмодели для разных наборов данных и объединяя их одинаковым образом.

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

def model_function(x, a, b, c):
   ### do some calculation with x, a, b, c values
   result = a + x*b + x*x*c
   return result

может стать

def objective_function(params, x, data):
     vals = params.valuesdict()
     return data - model_function(x, vals['a'], vals['b'], vals['c'])

Если это do_calc() делает что-то сложное, дополнительная нагрузка распаковки параметров и вычитания данных довольно мала. И, особенно, если некоторые параметры будут использоваться для нескольких наборов данных, а некоторые только для конкретных наборов данных, вам придется управлять этим либо в функции модели, либо в целевой функции. В примере, на который вы ссылаетесь, мой ответ включал цикл по наборам данных, выбирая параметры по имени для каждого набора данных. Возможно, вы захотите сделать что-то подобное. Вы, вероятно, могли бы сделать это в модельной функции, думая об этом как о моделировании объединенных наборов данных, но я не уверен, что вы действительно много выиграете, сделав это.

Я нашел проблему. На самом деле, model.fit() прекрасно обрабатывает массивы нескольких наборов данных и выполняет правильную подгонку. Правильный вызов model.fit() с несколькими наборами данных:

import numpy as np 
from lmfit import Model, Parameters
from lmfit.models import GaussianModel
import matplotlib.pyplot as plt

def gauss(x, amp, cen, sigma):
   "basic gaussian"
    return amp*np.exp(-(x-cen)**2/(2.*sigma**2))

x1= np.arange(0.,100.,0.1)
x2= np.arange(0.,100.,0.1)
y1= gauss(x1, 1.,50.,5.)+ np.random.normal(size=len(x1), scale=0.01)
y2= gauss(x2, 0.8,48.4,4.5)+ np.random.normal(size=len(x2), scale=0.01)

mod= GaussianModel()
params= mod.make_params()

params['amplitude'].set(1.,min=0.01,max=100.)
params['center'].set(1.,min=0.01,max=100.)
params['sigma'].set(1.,min=0.01,max=100.)

result= mod.fit(np.array([y1,y2]), params,method='basinhopping',
x=np.array([x1,x2]))

print(result.fit_report(min_correl=0.5))

fig, ax = plt.subplots()

plt.plot(x1,y1, lw=2, color='red')
plt.plot(x2,y2, lw=2, color='orange')
plt.plot(x1,result.eval(x=x1), lw=2, color='black')

plt.show()

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

Другие вопросы по тегам