Составная функция lmfit с возможно разными функциональными формами - PullRequest
0 голосов
/ 01 марта 2019

Я новичок в Python и в настоящее время работаю над проектом, в котором мне нужно оценить параметры функции, которая может быть составлена ​​из нескольких факторов:

Y (X; \ theta) = g (X_g; \ theta_g) \ cdot h (X_h; \ theta_h) \ cdot f (X_f; \ theta_f) \ cdot l (X_l; \ theta_l) \ + \ epsilon

, где g, h, f и l являютсяфункции, которые зависят от его параметров \ theta_g, \ theta_h, \ theta_f, \ theta_l.

В моем случае каждая из этих функций может иметь разные функциональные формы, и размерность ее параметров может варьироваться, т. Е. В случае case \ theta_g может иметь размерность 4, но для конкретного случая \ theta_g будет зафиксировано в 1.То же самое относится и к другим функциям h, f и l.

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

Кажется, что пакет lmfit и классы, такие как Model и Parameters, должны позволить мне достичь этого.Тем не менее, я хотел бы получить некоторые рекомендации о том, как структурировать различные функции так, чтобы я мог использовать fitlm.

def fun_base(inc,par_base,type_fun_base):

    a0,a1,a2,a3 = par_base

    #base_f = 0
    if type_fun_base == 'Not used':
        base_f = 1
        #When the associated factor is not used, return a constant 1. 
    elif type_fun_base == 'Double breaking point':
         inc=inc/100 # this is for the case when incentive has a unit %
         base_f = np.minimum(a3, a0+a1*np.maximum(inc-a2, 0))
    elif type_fun_base  == 'Double-asymptotic':
        base_f = a0+ a1/(1+np.exp(a2+a3*inc))
        #the values of tge double-asymptotic function w.r.t. incentive
    elif type_fun_base =='CPR':
        base_f=a0
        #Return a constant param(1) no matter what the observation is 
    else:
        print("The rate incentive function has not been defined")
        sys.exit()
    return base_f

def fun_seasoning(age,par_season,type_fun_seasoning):
    s0,s1,s2,s3 = par_season
    if type_fun_seasoning == 'Not used':
        seasoning_f = 1
        #When the associated factor is not used, return a constant 1. 
    elif type_fun_seasoning  == 'Hockey stick':
        seasoning_f=np.minimum(s0+1/s1*age, 1)
    elif type_fun_seasoning  == 'Quadratic':
        seasoning_f = (1+s0+s1*age+ s2*age**2)
    elif type_fun_seasoning == 'Exponential AU':
        seasoning_f=s0*(1+np.exp(-s1*age)-np.exp(-s2*age))
    else:
        print("The seasoning function has not been defined")
        sys.exit()
    return seasoning_f


def prep_function(X,par,type_funs):
    par_base = par[0:4]
    inc = X[:,0]
    type_fun_base = type_funs[0]
    par_seasoning = par[4:]
    age = X[:,1]
    type_fun_seasoning = type_funs[1]
    ppr = fun_base(inc,par_base,type_fun_base)*fun_seasoning(age,par_seasoning,type_fun_seasoning)
    return ppr

prep_function представляет Y (X, \ theta), а две другие функции - первые две части в приведенном выше уравнении.

1 Ответ

0 голосов
/ 03 марта 2019

Примечание: обновлено, чтобы отразить тот факт, что компоненты вашей модели используют разные независимые переменные (age, inc, ...).

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

import numpy as np
from lmfit import Model
from lmfit.models import ConstantModel

## Base models, depending on independent variable 'inc'
def double_break(inc, offset, amp, breakpoint, maxval=3, age=None):
     return np.minimum(maxval, offset+amp*np.maximum(inc - breakpoint, 0))

def double_asymptote(inc, offset, amp, decay_off, decay_slope, age=None):
     return offset + amp/(1.0+np.exp(decay_off+decay_slope*inc))

def constant(inc, c, age=None):
     return c

## Seasoning models, depending on independent variable 'age'
def hockey_stick(age, offset, slope, maxval=1, inc=None):
    return np.minimum(offset + slope/age, maxval)

def quadratic(age, offset, slope, quad, inc=None):
    return (offset + slope*age * quad*age*age)

def exponential_au(age, amp, decay1, decay2, inc=None):
    return amp*(1+np.exp(-decay1*age) - np.exp(-decay2*age))

каждый из этихфункции могут быть превращены в lmfit.Model, которые будут иметь имена параметров, которые являются более значимыми, чем s1, и т. д. и могут запускаться индивидуально для тестирования.С этими определениями вы могли бы иметь функцию build_model(), которая могла бы выглядеть как

def build_model(base='double_break', seasoning='hockey_stick'):
   if base == 'double_break':
       basemodel = Model(double_break, prefix='base_', independent_vars=['inc', 'age'])
   elif base == 'double_asymptote':
       basemodel = Model(double_asymptote, prefix='base_', independent_vars=['inc', 'age'])
   else:
       basemodel = Model(constant, prefix='base_', independent_vars=['inc', 'age'])

   if seasoning == 'hockey_stick':
       smodel = Model(hockey_stick, prefix='seasoning_', independent_vars=['inc', 'age'])
   elif seasoning == 'quadratic':
       smodel = Model(quadratic, prefix='seasoning_', independent_vars=['inc', 'age'])
   else:
       smodel = Model(exponential_au, prefix='seasoning_', independent_vars=['inc', 'age'])

   return basemodel*smodel

Модель, которую построит эта функция, будет составной моделью (основа * приправа), а параметры будут сделаны с помощью

   model = build_model(base='double_break', seasoning='quadratic')
   params = model.make_params(base_offset=1, base_amp=2,base_breakpoint=1000,
                              seasoning_offset=1, seasoning_slope=0.02, seasoning_quad=0)

будет иметь более значимые имена, такие как base_offset, seasoning_offset, seasoning_slope и т. Д. Чтобы использовать эту модель, вы можете сделать

   inc = np.linspace(-1, 1, 11)
   age = np.linspace(5, 10, 11)

   test = model.eval(params, age=age, inc=inc)
   result = model.fit(ydata, params, age=age, inc=inc)

Было бы довольно легко добавитьновые функции для «основы» или «приправы», или для добавления другого компонента в модель.

В этой модели есть одна вещь, которая может не соответствовать вашему сценарию использования, в зависимости от того, что вы подразумеваете под"\ CDOT".То есть эти компоненты модели просто умножаются и поэтому независимые переменные должны иметь одинаковую длину.Если у вас есть 1000 inc значений и 10 age значений, неясно, как вы хотите умножить эти две функции.Если вам нужно сделать какой-либо другой продукт или комбинацию моделей компонентов, отличную от базового сложения, вычитания, умножения или деления, вы можете сделать это, но это потребует дополнительных шагов для предоставления функции оператора, используемой для объединения моделей компонентов.За подробностями обращайтесь к документации lmfit (с примером выполнения свертки).

...