参数在某些组之间变化,而其他参数适用于所有组?

2024-05-26 09:19:24 发布

您现在位置:Python中文网/ 问答频道 /正文

如何允许函数的一个参数在组之间变化,而其他参数在所有组中都适用?你知道吗

我用lmfit来拟合疾病传播的模型。我希望函数的指数适用于所有数据点,但比例因子需要在不同的组之间变化(作为不同年份不同繁殖率疾病株的代理)。你知道吗

请参见下面的代码:

#### create parameters ####
params = Parameters()
params.add('tau_1', value=1.,min=0.01)
params.add('tau_2', value=1.,min=0.01)
params.add('rho_1', value=1.,min=0.01)
for i in range(0, len(sorted(variable_data.flu_season.unique()))):
    season = str(sorted(variable_data.flu_season.unique())[i])
    params.add('theta_' + season, value=1., min = 0.01)

#### model ####
def pop_dist_inverse_grav(params, dist, host_pop, targ_pop, flu_sea, data):

    parvals = params.valuesdict()
    tau_1 = parvals['tau_1']
    tau_2 = parvals['tau_2']
    rho_1 = parvals['rho_1']

    theta_1 = parvals['theta_' + str(season)]

    grav_model = theta_1 * (np.power(dist, rho_1)) / ((np.power(host_pop, tau_1)) * (np.power(targ_pop, tau_2)))

    return grav_model - data

遵照纽维尔先生的建议,我让这件衣服开始工作了。代码如下:

import pandas as pd
from lmfit import minimize, Parameters, report_fit
import numpy as np

variable_data = pd.read_csv("../Data/variables_for_model_fit.csv", sep=",", header='infer')

season_list = sorted(variable_data.flu_season.unique())


#### create parameters ####
params1 = Parameters()
params1.add('tau_host', value=0.24,min=0, max = 3)
params1.add('tau_targ', value=0.14,min=0, max = 3)
params1.add('rho', value=0.29,min=0, max = 3)
# "global" parameters

for i, season in enumerate(season_list):
    params1.add('theta_%d' % season_list[i], value=1000., min=1, max=1e6)
    # creates theta parameters for each season

#### define model ####
def grav_dist_over_pop(dist, hist_pop, targ_pop, theta, rho, tau_host, tau_targ):
    return theta**(-1) * dist**rho * host_pop**(-tau_host) * targ_pop**(-tau_targ)

#### objective function ####
def objective_1(params, dist, host_pop, targ_pop, flu_sea, data):
    parvals1 = params1.valuesdict()
    resid = np.zeros((len(season_data),len(season_data)))

    for i, data in enumerate(data):

        theta = parvals1['theta_%d' % flu_sea[i]]
        rho = parvals1['rho']#_%d' % flu_sea[i]]
        tau_host = parvals1['tau_host']#_%d' % flu_sea[i]]
        tau_targ = parvals1['tau_targ']#_%d' % flu_sea[i]]
        model = grav_dist_over_pop(dist, host_pop, targ_pop, theta, rho, tau_host, tau_targ)
        resid[i, :] = model - data
    return resid.flatten()

#### fit global variables ####
season_data = variable_data.sample(500)
# my dataset is huge so lmfit takes an age when fitting all of the
# theta values
host_pop = np.asarray(season_data.host_city_pop.values.tolist())
targ_pop = np.asarray(season_data.target_city_pop.values.tolist())
dist = np.asarray(season_data.distance.values.tolist())
data = np.asarray(season_data.time_to_spread.values.tolist())
flu_sea = np.asarray(season_data.flu_season.values.tolist())

result = minimize(objective_1, params1, args=(dist, host_pop, targ_pop, flu_sea, data))

report_fit(result.params)

Tags: addhostdatavaluedistnpparamsmin
1条回答
网友
1楼 · 发布于 2024-05-26 09:19:24

使用lmfit(或scipy.optimize和我知道的所有其他工具)进行拟合总是一种“全局”拟合,为用于最小化1D剩余数组的单个参数集找到最佳值。更清楚的是,您可以优化参数以适应多个数据集(或组或季节),但您必须将问题简化为一组参数来计算一维残差数组。你知道吗

对于您的问题,我建议从定义“grav模型”开始,该模型为单个季节或数据集建模。我对这类领域一无所知(但很高兴lmfit可能有用!),但从您的示例来看,可能是这样的(如果不是,请更正)

def grav_season(dist, host_pop, targ_pop, theta, rho, tau_host, tau_targ):
    return theta * dist**rho * host_pop**(-tau_host) * targ_pop**(-tau_targ)

在我看来有3个自变量(disthost_poptarg_pop)和4个可能是变量的参数:thetarhotau_hosttau_targ。同样,如果需要,请更正,但是对于这里的目的,这些细节并不那么重要。你知道吗

为了适应一个季节/数据集,您可以

from lmfit import Parameters, minimize, report_fit

def objective(params, data, dist, host_pop, targ_pop):
     pars = params.valuesdict()
     model = grav_season(dist, host_pop, targ_pop, pars['theta'],
                         pars['rho'], pars['tau_host'], pars['tau_targ'])
     return (model - data)

params = Parameters()
param.add('theta', value=1., min = 0.01)
param.add('rho', value=1., min = 0.01)
param.add('tau_host', value=1., min = 0.01)
param.add('tau_targ, value=1., min = 0.01)

result = minimize(objective, params, args=(data, dist, host_pop, targ_pop))
report_fit(result.params)

现在,对于多个季节,只需为每个季节创建thetarhotau_hosttau_targ参数。但是,在一次拟合中使用所有这些数据并没有多大意义。如果我理解正确,您希望tau_hosttau_targrho对所有季节都具有相同的值。你知道吗

为此,请为全局应用的指数创建参数:

params = Parameters()
param.add('rho', value=1., min = 0.01)
param.add('tau_host', value=1., min = 0.01)
param.add('tau_targ', value=1., min = 0.01)

以及每个季节的参数:

for i, season in enumerate(sorted(variable_data.flu_season.unique())):
     param.add('theta_%d' % i, value=1., min = 0.01)
     param.add('rho_%d' % i,  expr='rho')
     param.add('tau_host_%d' % i, expr='tau_host')
     param.add('tau_targ_%d' % i, expr='tau_targ')

请注意,theta_i将独立变化,但rho_i等将被约束为获取变量rho等的值。这将提供每个季节的完整参数集,但满足您的约束。这种方法允许您轻松地将其中的一个或多个更改为单独变化,如果这看起来可能需要更多的测试。要做到这一点,你可以说:

    params['rho_7'].set(value=2.0, vary=True, expr='')  # vary independently

要使用这组多季节参数,还需要多季节数据。我不确定disthost_poptarg_pop是否意味着随着季节而变化,或者仅仅是data。我假设只有data会随着季节的变化而变化(但如果不是这样的话,就很容易改变)。构建一个包含每个季节数据的列表,然后修改目标函数,如下所示:

def objective(params, season_data, dist, host_pop, targ_pop):
    pars = params.valuesdict()
    resid = np.zeros((len(season_data), len(season_data[0]))
    for i, data in enumerate(season_data):
        theta = pars['theta_%d' % i]
        rho = pars['rho_%d' % i]
        tau_host = pars['tau_host_%d' % i]
        tau_targ = pars['tau_targ_%d' % i]
        model = grav_season(dist, host_pop, targ_pop,
                            theta, rho, tau_host, tau_targ)
        resid[i, :] = model - data
    return resid.flatten()

result = minimize(objective, params, args=(season_data, dist, host_pop, targ_pop))
report_fit(result.params)

希望这能帮助你开始。同样,主要建议是:

  1. 为正在建模的现象创建每季度/每数据集模型函数。你知道吗
  2. 使用“expr”约束每个季节的参数以获取“全局值”。尽管你想要四季分明 要获得相同的值,可能有些参数会随时间线性变化 或者被要求增加一些价值或其他东西,这意味着他们不是真正独立的。你知道吗

相关问题 更多 >