如何使用具有时间相关变量的scipy.integrate.odeint求解ODE系统

Bob*_*y M 3 python scipy ode

我正在使用scipy食谱中的Zombie Apocalypse 示例,以了解有关使用python解决ODE系统的信息。

在此模型中,有一个方程式可以根据出生率,死亡率和初始人口提供每天的人口。然后根据人口数量,计算出有多少僵尸被制造和杀死。

我有兴趣用一系列可以告诉我们每个时间步长人口的数据列表来代替人口微分方程。我收到以下错误:

TypeError: can't multiply sequence by non-int of type 'float'
Run Code Online (Sandbox Code Playgroud)

正如人们指出的那样,这是因为将单个数字乘以列表没有意义。我不确定如何在每次T时从列表中为微分方程提供数字。

这是两次尝试的代码

# solve the system dy/dt = f(y, t)
def f(y, t):
    Si = [345, 299, 933, 444, 265, 322] # replaced an equation with list
    Zi = y[0]
    Ri = y[1]
    # the model equations (see Munz et al. 2009)
    f0 = B*Si*Zi + G*Ri - A*Si*Zi
    f1 = d*Si + A*Si*Zi - G*Ri
    return [f0, f1]
Run Code Online (Sandbox Code Playgroud)

我也尝试过

numbers = [345, 299, 933, 444, 265, 322]
for t in [0, 5]:
    Si = numbers

# solve the system dy/dt = f(y, t)
def f(y, t):

    Zi = y[0]
    Ri = y[1]
    # the model equations (see Munz et al. 2009)
    f0 = B*Si*Zi + G*Ri - A*Si*Zi
    f1 = d*Si + A*Si*Zi - G*Ri
    return [f0, f1]
Run Code Online (Sandbox Code Playgroud)

两次尝试都存在向列表提供整个列表f0f1不是从列表中反复提供1个数字的相同问题。

Cle*_*leb 5

据我从问题下方的评论中了解到,您尝试合并可能会带来噪音的测量数据。可以直接使用这些数据来适应您的时程,而不是直接插入数据。在这里,我显示了变量的结果S

在此处输入图片说明

这些green dots是从您提供的ODE系统的解决方案中抽样的。为了模拟测量误差,我在这些数据(blue dots)中添加了一些噪声。然后,您可以安装ODE系统以尽可能地重现这些数据(red line)。

对于这些任务,您可以使用lmfit。复制该图的代码如下所示(可以在嵌入式注释中找到一些解释):

# zombie apocalypse modeling
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


# solve the system dy/dt = f(y, t)
def f(y, t, paras):

    Si = y[0]
    Zi = y[1]
    Ri = y[2]

    try:
        P = paras['P'].value
        d = paras['d'].value
        B = paras['B'].value
        G = paras['G'].value
        A = paras['A'].value

    except:
        P, d, B, G, A = paras
    # the model equations (see Munz et al. 2009)
    f0 = P - B * Si * Zi - d * Si
    f1 = B * Si * Zi + G * Ri - A * Si * Zi
    f2 = d * Si + A * Si * Zi - G * Ri
    return [f0, f1, f2]


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


def residual(paras, t, data):
    x0 = paras['S0'].value, paras['Z0'].value, paras['R0'].value
    model = g(t, x0, paras)
    s_model = model[:, 0]
    return (s_model - data).ravel()

# just for reproducibility reasons
np.random.seed(1)

# initial conditions
S0 = 500.              # initial population
Z0 = 0                 # initial zombie population
R0 = 0                 # initial death population
y0 = [S0, Z0, R0]     # initial condition vector
t = np.linspace(0, 5., 100)         # time grid

P = 12      # birth rate
d = 0.0001  # natural death percent (per day)
B = 0.0095  # transmission percent  (per day)
G = 0.0001  # resurect percent (per day)
A = 0.0001  # destroy percent  (per day)

# solve the DEs
soln = odeint(f, y0, t, args=((P, d, B, G, A), ))
S = soln[:, 0]
Z = soln[:, 1]
R = soln[:, 2]

# plot results
plt.figure()
plt.plot(t, S, label='Living')
plt.plot(t, Z, label='Zombies')
plt.xlabel('Days from outbreak')
plt.ylabel('Population')
plt.title('Zombie Apocalypse - No Init. Dead Pop.; No New Births.')
plt.legend(loc=0)
plt.show()

# generate fake data
S_real = S[0::8]
S_measured = S_real + np.random.randn(len(S_real)) * 100
t_measured = t[0::8]

plt.figure()
plt.plot(t_measured, S_real, 'o', color='g', label='real data')

# add some noise to your data to mimic measurement erros
plt.plot(t_measured, S_measured, 'o', color='b', label='noisy data')

# set parameters including bounds; you can also fix parameters (use vary=False)
params = Parameters()
params.add('S0', value=S0, min=490., max=510.)
params.add('Z0', value=Z0, vary=False)
params.add('R0', value=R0, vary=False)
params.add('P', value=10, min=8., max=12.)
params.add('d', value=0.0005, min=0.00001, max=0.005)
params.add('B', value=0.01, min=0.00001, max=0.01)
params.add('G', value=G, vary=False)
params.add('A', value=0.0005, min=0.00001, max=0.001)

# fit model
result = minimize(residual, params, args=(t_measured, S_measured), method='leastsq')  # leastsq nelder
# check results of the fit
data_fitted = g(t, y0, result.params)

plt.plot(t, data_fitted[:, 0], '-', linewidth=2, color='red', label='fitted data')
plt.legend()
# display fitted statistics
report_fit(result)

plt.show()
Run Code Online (Sandbox Code Playgroud)