使用Python通过Scipy&将数据拟合到ODE系统中脾气暴躁的 [英] Fitting data to system of ODEs using Python via Scipy & Numpy

查看:136
本文介绍了使用Python通过Scipy&将数据拟合到ODE系统中脾气暴躁的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在通过Scipy& amp;将其MATLAB代码转换为Python时遇到了一些麻烦脾气暴躁.我一直在研究如何为我的ODE系统找到最佳参数值(k0和k1)以适合我的十个观测数据点.我目前对k0和k1有一个初步的猜测.在MATLAB中,我可以使用称为"fminsearch"的函数,该函数采用ODE的系统,观察到的数据点以及ODE的系统的初始值.然后它将计算一对新的参数k0和k1,这些参数将适合观察到的数据.我已经包含了我的代码,以查看是否可以帮助我实现某种"fminsearch"以找到适合我的数据的最佳参数值k0和k1.我想将任何代码添加到我的lsqtest.py文件中.

I am having some trouble translating my MATLAB code into Python via Scipy & Numpy. I am stuck on how to find optimal parameter values (k0 and k1) for my system of ODEs to fit to my ten observed data points. I currently have an initial guess for k0 and k1. In MATLAB, I can using something called 'fminsearch' which is a function that takes the system of ODEs, the observed data points, and the initial values of the system of ODEs. It will then calculate a new pair of parameters k0 and k1 that will fit the observed data. I have included my code to see if you can help me implement some kind of 'fminsearch' to find the optimal parameter values k0 and k1 that will fit my data. I want to add whatever code to do this to my lsqtest.py file.

我有三个.py文件-ode.py,lsq.py和lsqtest.py

I have three .py files - ode.py, lsq.py, and lsqtest.py

ode.py:

def f(y, t, k): 
return (-k[0]*y[0],
      k[0]*y[0]-k[1]*y[1],
      k[1]*y[1])

lsq.py:

import pylab as py
import numpy as np
from scipy import integrate
from scipy import optimize
import ode
def lsq(teta,y0,data):
    #INPUT teta, the unknowns k0,k1
    # data, observed 
    # y0 initial values needed by the ODE
    #OUTPUT lsq value

    t = np.linspace(0,9,10)
    y_obs = data #data points
    k = [0,0]
    k[0] = teta[0]
    k[1] = teta[1]

    #call the ODE solver to get the states:
    r = integrate.odeint(ode.f,y0,t,args=(k,))


    #the ODE system in ode.py
    #at each row (time point), y_cal has
    #the values of the components [A,B,C]
    y_cal = r[:,1] #separate the measured B
    #compute the expression to be minimized:
    return sum((y_obs-y_cal)**2)

lsqtest.py:

lsqtest.py:

import pylab as py
import numpy as np
from scipy import integrate
from scipy import optimize
import lsq


if __name__ == '__main__':

    teta = [0.2,0.3] #guess for parameter values k0 and k1
    y0 = [1,0,0] #initial conditions for system
    y = [0.000,0.416,0.489,0.595,0.506,0.493,0.458,0.394,0.335,0.309] #observed data points
    data = y
    resid = lsq.lsq(teta,y0,data)
    print resid

推荐答案

对于此类拟合任务,您可以使用软件包 lmfit .拟合结果将如下所示;如您所见,数据复制得很好:

For these kind of fitting tasks you could use the package lmfit. The outcome of the fit would look like this; as you can see, the data are reproduced very well:

现在,我固定了初始浓度,也可以根据需要将它们设置为变量(只需删除以下代码中的vary=False).您获得的参数是:

For now, I fixed the initial concentrations, you could also set them as variables if you like (just remove the vary=False in the code below). The parameters you obtain are:

[[Variables]]
    x10:   5 (fixed)
    x20:   0 (fixed)
    x30:   0 (fixed)
    k0:    0.12183301 +/- 0.005909 (4.85%) (init= 0.2)
    k1:    0.77583946 +/- 0.026639 (3.43%) (init= 0.3)
[[Correlations]] (unreported correlations are <  0.100)
    C(k0, k1)                    =  0.809 

用于复制情节的代码如下所示(可以在嵌入式注释中找到一些解释):

The code that reproduces the plot looks like this (some explanation can be found in the inline comments):

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from lmfit import minimize, Parameters, Parameter, report_fit
from scipy.integrate import odeint


def f(y, t, paras):
    """
    Your system of differential equations
    """

    x1 = y[0]
    x2 = y[1]
    x3 = y[2]

    try:
        k0 = paras['k0'].value
        k1 = paras['k1'].value

    except KeyError:
        k0, k1 = paras
    # the model equations
    f0 = -k0 * x1
    f1 = k0 * x1 - k1 * x2
    f2 = k1 * x2
    return [f0, f1, f2]


def g(t, x0, paras):
    """
    Solution to the ODE x'(t) = f(t,x,k) with initial condition x(0) = x0
    """
    x = odeint(f, x0, t, args=(paras,))
    return x


def residual(paras, t, data):

    """
    compute the residual between actual data and fitted data
    """

    x0 = paras['x10'].value, paras['x20'].value, paras['x30'].value
    model = g(t, x0, paras)

    # you only have data for one of your variables
    x2_model = model[:, 1]
    return (x2_model - data).ravel()


# initial conditions
x10 = 5.
x20 = 0
x30 = 0
y0 = [x10, x20, x30]

# measured data
t_measured = np.linspace(0, 9, 10)
x2_measured = np.array([0.000, 0.416, 0.489, 0.595, 0.506, 0.493, 0.458, 0.394, 0.335, 0.309])

plt.figure()
plt.scatter(t_measured, x2_measured, marker='o', color='b', label='measured data', s=75)

# set parameters including bounds; you can also fix parameters (use vary=False)
params = Parameters()
params.add('x10', value=x10, vary=False)
params.add('x20', value=x20, vary=False)
params.add('x30', value=x30, vary=False)
params.add('k0', value=0.2, min=0.0001, max=2.)
params.add('k1', value=0.3, min=0.0001, max=2.)

# fit model
result = minimize(residual, params, args=(t_measured, x2_measured), method='leastsq')  # leastsq nelder
# check results of the fit
data_fitted = g(np.linspace(0., 9., 100), y0, result.params)

# plot fitted data
plt.plot(np.linspace(0., 9., 100), data_fitted[:, 1], '-', linewidth=2, color='red', label='fitted data')
plt.legend()
plt.xlim([0, max(t_measured)])
plt.ylim([0, 1.1 * max(data_fitted[:, 1])])
# display fitted statistics
report_fit(result)

plt.show()

如果您有其他变量的数据,则只需更新函数residual.

If you have data for additional variables, you can simply update the function residual.

这篇关于使用Python通过Scipy&amp;将数据拟合到ODE系统中脾气暴躁的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆