복붙노트

[PYTHON] Python과 lmfit : 공유 매개 변수로 여러 데이터 세트를 맞추는 방법?

PYTHON

Python과 lmfit : 공유 매개 변수로 여러 데이터 세트를 맞추는 방법?

필자는 lmfit 모듈을 사용하여 가변 개수의 데이터 세트에 함수를 맞추고 일부 공유 매개 변수와 개별 매개 변수를 사용하고자합니다.

다음은 가우시안 데이터를 생성하고 각 데이터 세트에 개별적으로 맞추는 예제입니다.

import numpy as np
import matplotlib.pyplot as plt
from lmfit import minimize, Parameters, report_fit

def func_gauss(params, x, data=[]):
    A = params['A'].value
    mu = params['mu'].value
    sigma = params['sigma'].value
    model = A*np.exp(-(x-mu)**2/(2.*sigma**2))

    if data == []:
        return model
    return data-model

x  = np.linspace( -1, 2, 100 )
data = []
for i in np.arange(5):
    params = Parameters()
    params.add( 'A'    , value=np.random.rand() )
    params.add( 'mu'   , value=np.random.rand()+0.1 )
    params.add( 'sigma', value=0.2+np.random.rand()*0.1 )
    data.append(func_gauss(params,x))

plt.figure()
for y in data:
    fit_params = Parameters()
    fit_params.add( 'A'    , value=0.5, min=0, max=1)
    fit_params.add( 'mu'   , value=0.4, min=0, max=1)
    fit_params.add( 'sigma', value=0.4, min=0, max=1)
    minimize(func_gauss, fit_params, args=(x, y))
    report_fit(fit_params)

    y_fit = func_gauss(fit_params,x)
    plt.plot(x,y,'o',x,y_fit,'-')
plt.show()


# ideally I would like to write:
#
# fit_params = Parameters()
# fit_params.add( 'A'    , value=0.5, min=0, max=1)
# fit_params.add( 'mu'   , value=0.4, min=0, max=1)
# fit_params.add( 'sigma', value=0.4, min=0, max=1, shared=True)
# minimize(func_gauss, fit_params, args=(x, data))
#
# or:
#
# fit_params = Parameters()
# fit_params.add( 'A'    , value=0.5, min=0, max=1)
# fit_params.add( 'mu'   , value=0.4, min=0, max=1)
#
# fit_params_shared = Parameters()
# fit_params_shared.add( 'sigma', value=0.4, min=0, max=1)
# call_function(func_gauss, fit_params, fit_params_shared, args=(x, data))

해결법

  1. ==============================

    1.나는 네가 그곳에있는 대부분의 방법이라고 생각한다. 모든 데이터 세트에 대해 단일 매개 변수 세트를 사용하여 모든 데이터 세트를 minimize () 및 맞추기 위해 제공하는 단일 전역 범용 함수에서 사용할 수있는 배열 또는 구조에 데이터 세트를 넣어야합니다. 원하는대로 데이터 세트간에이 세트를 공유 할 수 있습니다. 약간의 예제를 확장하면, 아래의 코드는 5 가지 가우시안 함수에 하나의 적합성을 적용합니다. 데이터 세트간에 매개 변수를 묶는 예를 보려면 동일한 값인 5 개의 데이터 세트에 대해 거의 동일한 값을 사용했습니다. 저는 5 개의 다른 시그마 매개 변수 ( 'sig_1', 'sig_2', ..., 'sig_5')를 만들었지 만, 이들이 수학적 제약 조건을 사용하여 동일한 값을 갖도록했습니다. 따라서 문제에는 11 개의 변수가 있고 15가 아닙니다.

    나는 네가 그곳에있는 대부분의 방법이라고 생각한다. 모든 데이터 세트에 대해 단일 매개 변수 세트를 사용하여 모든 데이터 세트를 minimize () 및 맞추기 위해 제공하는 단일 전역 범용 함수에서 사용할 수있는 배열 또는 구조에 데이터 세트를 넣어야합니다. 원하는대로 데이터 세트간에이 세트를 공유 할 수 있습니다. 약간의 예제를 확장하면, 아래의 코드는 5 가지 가우시안 함수에 하나의 적합성을 적용합니다. 데이터 세트간에 매개 변수를 묶는 예를 보려면 동일한 값인 5 개의 데이터 세트에 대해 거의 동일한 값을 사용했습니다. 저는 5 개의 다른 시그마 매개 변수 ( 'sig_1', 'sig_2', ..., 'sig_5')를 만들었지 만, 이들이 수학적 제약 조건을 사용하여 동일한 값을 갖도록했습니다. 따라서 문제에는 11 개의 변수가 있고 15가 아닙니다.

    import numpy as np
    import matplotlib.pyplot as plt
    from lmfit import minimize, Parameters, report_fit
    
    def gauss(x, amp, cen, sigma):
        "basic gaussian"
        return amp*np.exp(-(x-cen)**2/(2.*sigma**2))
    
    def gauss_dataset(params, i, x):
        """calc gaussian from params for data set i
        using simple, hardwired naming convention"""
        amp = params['amp_%i' % (i+1)].value
        cen = params['cen_%i' % (i+1)].value
        sig = params['sig_%i' % (i+1)].value
        return gauss(x, amp, cen, sig)
    
    def objective(params, x, data):
        """ calculate total residual for fits to several data sets held
        in a 2-D array, and modeled by Gaussian functions"""
        ndata, nx = data.shape
        resid = 0.0*data[:]
        # make residual per data set
        for i in range(ndata):
            resid[i, :] = data[i, :] - gauss_dataset(params, i, x)
        # now flatten this to a 1D array, as minimize() needs
        return resid.flatten()
    
    # create 5 datasets
    x  = np.linspace( -1, 2, 151)
    data = []
    for i in np.arange(5):
        params = Parameters()
        amp   =  0.60 + 9.50*np.random.rand()
        cen   = -0.20 + 1.20*np.random.rand()
        sig   =  0.25 + 0.03*np.random.rand()
        dat   = gauss(x, amp, cen, sig) + np.random.normal(size=len(x), scale=0.1)
        data.append(dat)
    
    # data has shape (5, 151)
    data = np.array(data)
    assert(data.shape) == (5, 151)
    
    # create 5 sets of parameters, one per data set
    fit_params = Parameters()
    for iy, y in enumerate(data):
        fit_params.add( 'amp_%i' % (iy+1), value=0.5, min=0.0,  max=200)
        fit_params.add( 'cen_%i' % (iy+1), value=0.4, min=-2.0,  max=2.0)
        fit_params.add( 'sig_%i' % (iy+1), value=0.3, min=0.01, max=3.0)
    
    # but now constrain all values of sigma to have the same value
    # by assigning sig_2, sig_3, .. sig_5 to be equal to sig_1
    for iy in (2, 3, 4, 5):
        fit_params['sig_%i' % iy].expr='sig_1'
    
    # run the global fit to all the data sets
    result = minimize(objective, fit_params, args=(x, data))
    report_fit(result.fit_params)
    
    # plot the data sets and fits
    plt.figure()
    for i in range(5):
        y_fit = gauss_dataset(result.fit_params, i, x)
        plt.plot(x, data[i, :], 'o', x, y_fit, '-')
    
    plt.show()
    

    그것이 가치있는 일이라면, 다차원 배열이 아닌 사전 또는 DataSet 클래스 목록에 여러 데이터 세트를 보유하는 것을 고려할 것입니다. 어쨌든, 이것이 당신이 정말로해야 할 일을 수행하는 데 도움이되기를 바랍니다.

  2. ==============================

    2.나는 간단한 접근법을 사용했다 : 함수 firs n (= cargsnum) 인수를 정의하는 것이 일반적이다. 모든 데이터 세트는 개인용입니다. {

    나는 간단한 접근법을 사용했다 : 함수 firs n (= cargsnum) 인수를 정의하는 것이 일반적이다. 모든 데이터 세트는 개인용입니다. {

    def likelihood_common(var, xlist, ylist, mlist, cargsnum):
        cvars = var[:cargsnum]
        iargnum = [model.func_code.co_argcount - 1 - cargsnum for model in mlist]
        argpos = [cargsnum,] + list(np.cumsum(iargnum[:-1]) + cargsnum)
        args = [list(cvars) + list(var[pos:pos+iarg]) for pos, iarg in zip(argpos, iargnum)]
        res = [likelihood(*arg) for arg in zip(args, xlist, ylist, mlist)]
        return np.sum(res)
    

    } 여기서 각 데이터 세트의 무게가 같다고 가정합니다. 이 접근 방식에서 직면 한 문제는 지루한 계산 속도와 불안정성입니다. 파라미터 및 데이터 세트가 많은 경우.

  3. from https://stackoverflow.com/questions/20339234/python-and-lmfit-how-to-fit-multiple-datasets-with-shared-parameters by cc-by-sa and MIT license