Подгонка нескольких наборов данных с использованием 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()
Проблема в исходном коде на самом деле заключается в том, что мои наборы данных не имеют одинаковую длину. Однако я совсем не уверен, как справиться с этим самым элегантным способом?