Все еще возникают проблемы с подгонкой кривой

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

У меня снова возникли проблемы с установкой двух или более пиков. Первая проблема возникает с вычисляемым примером функции.

xg = np.random.uniform(0,1000,500)
mu1 = 200
sigma1 = 20
I1 = -2

mu2 = 800
sigma2 = 20
I2 = -1

yg3 = 0.0001*xg
yg1 = (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (xg - mu1)**2 / (2 * sigma1**2) )
yg2 = (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (xg - mu2)**2 / (2 * sigma2**2) )
yg=yg1+yg2+yg3

plt.figure(0, figsize=(8,8))
plt.plot(xg, yg, 'r.')

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

1-я попытка:

import numpy as np
from lmfit.models import PseudoVoigtModel, LinearModel, GaussianModel, LorentzianModel
import sys
import matplotlib.pyplot as plt

gauss1 = PseudoVoigtModel(prefix='g1_')
pars.update(gauss1.make_params())

pars['g1_center'].set(200)
pars['g1_sigma'].set(15, min=3)
pars['g1_amplitude'].set(-0.5)
pars['g1_fwhm'].set(20, vary=True)
#pars['g1_fraction'].set(0, vary=True)

gauss2  = PseudoVoigtModel(prefix='g2_')
pars.update(gauss2.make_params())

pars['g2_center'].set(800)
pars['g2_sigma'].set(15)
pars['g2_amplitude'].set(-0.4)
pars['g2_fwhm'].set(20, vary=True)
#pars['g2_fraction'].set(0, vary=True)

mod = gauss1 + gauss2 + LinearModel()

pars.add('intercept', value=0, vary=True)
pars.add('slope', value=0.0001, vary=True)

init = mod.eval(pars, x=xg)
out = mod.fit(yg, pars, x=xg)

print(out.fit_report(min_correl=0.5))
plt.figure(5, figsize=(8,8))
out.plot_fit()

Когда я включаю параметр «фракция», я часто получаю

'NameError: name 'pv1_fraction' is not defined in expr='<_ast.Module object at 0x00000000165E03C8>'.

хотя это должно быть определено. Я также получаю эту ошибку для реальных данных с этим подходом.

2-я попытка:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import lmfit

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

def linear(x, m, n):
    return m*x + n

peak1 = lmfit.model.Model(gauss, prefix='p1_')
peak2 = lmfit.model.Model(gauss, prefix='p2_')
lin = lmfit.model.Model(linear, prefix='l_')
model = peak1 + lin + peak2
params = model.make_params()

params['p1_mu'] = lmfit.Parameter(value=200, min=100, max=250)
params['p2_mu'] = lmfit.Parameter(value=800, min=100, max=1000)
params['p1_sigma'] = lmfit.Parameter(value=15, min=0.01)
params['p2_sigma'] = lmfit.Parameter(value=20, min=0.01)
params['p1_A'] = lmfit.Parameter(value=-2, min=-3)
params['p2_A'] = lmfit.Parameter(value=-2, min=-3)
params['l_m'] = lmfit.Parameter(value=0)
params['l_n'] = lmfit.Parameter(value=0)

out = model.fit(yg, params, x=xg)

print out.fit_report()
plt.figure(8, figsize=(8,8))
out.plot_fit()

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

введите здесь описание изображения

введите здесь описание изображения

Кто-нибудь знает этот тип ошибки? Или есть решения для этого? И кто-нибудь знает, как избежать NameError при вызове функции модели из lmfit с этими подходами?


person kire    schedule 22.12.2015    source источник
comment
привет, не могли бы вы скопировать/вставить свои значения данных? (через запятую) Мне нравится этот вопрос, и у меня может быть идея, которую я хотел бы попробовать.   -  person user1269942    schedule 23.12.2015
comment
не берите в голову! вы сделали данные ... получили его!   -  person user1269942    schedule 23.12.2015
comment
нашел этот сайт. Возможно, это поможет pico.dreamhosters.com/PeakFitWithPython.html   -  person Moritz    schedule 24.12.2015
comment
Вы уверены, что проблема с подгонкой, а не только с отображением данных? Вы не показываете результаты подгонки параметров, а только график данных, начальную и окончательную подгонку. Из графиков видно, что подобранная функция изменилась. К сожалению, вы видите много ложных линий на графике, потому что вы рисуете данные точками, а соответствие — линиями. С вашим определением xg = np.random.uniform(0,1000,500) xg не будет монотонно возрастать, и такие сюжетные артефакты неизбежны.   -  person M Newville    schedule 16.01.2016
comment
К сожалению, я не могу показать вам подходящие данные, потому что их там нет. Все, что я получил, это нули для всех параметров. Так что я все еще думаю, что что-то не так с посадкой. И я не понимаю, что вы говорите о данных примера. В каком-то смысле они вполне реалистичны, потому что имитируют эксперимент, в котором для каждой точки данных x измеряется точка данных y (она сканирует весь диапазон с определенным размером шага). Итак, в эксперименте я бы получил массив для x, который похож на данные этого примера.   -  person kire    schedule 21.01.2016


Ответы (2)


У меня есть несколько приемлемое решение для вас. Поскольку я не знаю, насколько изменчивы ваши данные, я не могу сказать, что это будет работать в общем смысле, но должно помочь вам начать работу. Если ваши данные находятся в диапазоне 0-1000 и имеют два пика или провала вдоль линии, как вы показали, то это должно работать.

Я использовал scipy curve_fit и объединил все компоненты функции в одну функцию. Можно передать начальные местоположения в функцию curve_fit. (вероятно, вы можете сделать это с помощью используемой вами библиотеки, но я с ней не знаком). Существует цикл в цикле, в котором я варьирую параметры mu, чтобы найти те, у которых квадрат ошибки меньше всего. Если вам нужно подгонять свои данные много раз или в каком-то сценарии в реальном времени, то это не для вас, но если вам просто нужно подгонять некоторые данные, запустите этот код и возьмите кофе.

from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
import pylab
from matplotlib import cm as cm
import time

def my_function_big(x, m, n, #lin vars
                       sigma1, mu1, I1,  #gaussian 1
                       sigma2, mu2, I2):  #gaussian 2

  y = m * x + n + (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (x - mu1)**2 / (2 * sigma1**2) ) + (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (x - mu2)**2 / (2 * sigma2**2) )
  return y


#make some data
xs = np.random.uniform(0,1000,500)
mu1 = 200
sigma1 = 20
I1 = -2

mu2 = 800
sigma2 = 20
I2 = -1

yg3 = 0.0001 * xs
yg1 = (I1 / (sigma1 * np.sqrt(2 * np.pi))) * np.exp( - (xs - mu1)**2 / (2 * sigma1**2) )
yg2 = (I2 / (sigma2 * np.sqrt(2 * np.pi))) * np.exp( - (xs - mu2)**2 / (2 * sigma2**2) )
ys = yg1 + yg2 + yg3

xs = np.array(xs)
ys = np.array(ys)

#done making data



#start a double loop...very expensive but this is quick and dirty
#it would seem that the regular optimizer has trouble finding the minima so i 
#found that having the near proper mu values helped it zero in much better

start = time.time()

serr = []
_x = []
_y = []
for x in np.linspace(0, 1000, 61):
  for y in np.linspace(0, 1000, 61):
    cfiti = curve_fit(my_function_big, xs, ys, p0=[0, 0, 1, x, 1, 1, y, 1], maxfev=20000000)
    serr.append(np.sum((my_function_big(xs, *cfiti[0]) - ys) ** 2))
    _x.append(x)
    _y.append(y)

serr = np.array(serr)
_x = np.array(_x)
_y = np.array(_y)

print 'done loop in loop fitting'
print 'time: %0.1f' % (time.time() - start)

gridsize=20
plt.subplot(111)
plt.hexbin(_x, _y, C=serr, gridsize=gridsize, cmap=cm.jet, bins=None)
plt.axis([_x.min(), _x.max(), _y.min(), _y.max()])
cb = plt.colorbar()
cb.set_label('SE')
plt.show()   

ix = np.argmin(serr.ravel())
mustart1 = _x.ravel()[ix]
mustart2 = _y.ravel()[ix]
print mustart1
print mustart2

cfit = curve_fit(my_function_big, xs, ys, p0=[0, 0, 1, mustart1, 1, 1, mustart2, 1], maxfev=2000000000)

xp = np.linspace(0, 1000, 1001)

plt.figure()
plt.scatter(xs, ys) #plot synthetic dat
plt.plot(xp, my_function_big(xp, *cfit[0]), '-', label='fit function')  #plot data evaluated along 0-1000
plt.legend(loc=3, numpoints=1, prop={'size':12})
plt.show()    
pylab.close()

визуализируйте, как квадрат ошибки ведет себя в пространстве мю.

подходящая функция

Удачи!

person user1269942    schedule 23.12.2015

В вашей первой попытке:

pars['g1_fraction'].set(0, vary=True)

Дробь должна быть значением от 0 до 1, но я считаю, что это не может быть ноль. Попробуйте поставить что-то вроде 0.000001, и все заработает.

person Camila    schedule 28.09.2018