使用Python实现SEICR模型,模拟流行病传播过程
SEICR模型适用于具有慢性期的传染病,其中S代表易感个体(Susceptible),E代表潜伏期个体(Exposed),I代表急性期感染个体(Infected),C代表慢性期个体(Chronic),R代表恢复个体(Recovered)。SEICR模型的基本原理是通过一系列参数化的传播、进展和恢复速率来描述易感个体被感染,潜伏期个体发展为急性感染,急性感染个体转变为慢性感染,以及感染个体最终恢复的过程。
(1)急性感染病例与慢性感染病例具有相同的感染力,急性或慢性感染病例的传染率系数表示为β。
(2)潜伏期为$\frac{1}{ω}$,则在t时刻,由E进展为I的人数为ωE。
(3)感染个体进展到慢性期的比例为q,从 I 到 C 和 R 的速率为γ,则在t时刻,由 I 进展到 C 和 R 的人数分别为 pγI 和 (1−p)γI 。
(4)急性感染期通常症状较轻,不会危及生命,因此急性期不考虑因病死亡。
(5)假设所有的新生儿都易感,自然出生率为br,自然死亡率为dr,疾病的病死率为f。
表1 模型仓室含义
仓室 | 含义 |
---|---|
易感者(S) | 尚未感染但易感的个体 |
潜伏者(E) | 已经被感染,处于潜伏期尚未具有传染性的个体 |
急性期感染者(I) | 感染后进入急性期出现症状并具有传染性的个体 |
慢性期感染者(C) | 感染后从急性期进入慢性期并具有传染性的个体 |
康复者(R) | 已经从疾病中恢复并获得免疫力的个体 |
表2 模型参数含义
参数 | 含义 |
---|---|
β | 病毒的传染率系数 |
ω | 从暴露状态转为感染状态的速率 |
p | 感染者进入慢性期的比例 |
γ | 急性期感染者的恢复/进展速率 |
f | 疾病的病死率 |
br | 人群自然出生率 |
dr | 人群自然死亡率 |
N | 总人口数 |
$$
\begin{cases}
\frac{dS}{dt}=b_rN-\frac{βS(I+C)}{N}-d_rS \\
\frac{dE}{dt}=\frac{βS(I+C)}{N}-ωE-d_rE \\
\frac{dI}{dt}=ωE-γI-d_rI \\
\frac{dC}{dt}=pγI-(d_r+f)C \\
\frac{dR}{dt}=(1-p)γI-d_rR \\
\end{cases}
$$
SEICR模型适用于具有急性期与慢性期特征的传染病,特别是那些感染后可能发展为慢性状态,同时在急性期或慢性期具有传染性的疾病(如丙型肝炎)。该模型通过区分易感、潜伏、急性感染、慢性感染和恢复等不同状态,描述个体从感染到慢性状态的转变过程,以及疾病传播动态。该模型适用于能够通过有效治疗恢复的疾病,同时也考虑了慢性期感染者的疾病相关死亡风险。
表3 模型初始条件
仓室 | 初始值 | 单位 |
---|---|---|
易感人群(S) | 99864 | 人 |
潜伏者(E) | 100 | 人 |
急性期感染者(I) | 36 | 人 |
慢性期感染者(C) | 0 | 人 |
康复者(R) | 0 | 人 |
表4 模型参数取值
参数 | 定义 | 取值 | 单位 |
---|---|---|---|
β | 病毒的传播率系数 | 0.3 | 1 |
ω | 从暴露状态转为感染状态的速率 | 1/1.5 | 月-1 |
p | 急性感染者进入慢性期的比例 | 0.7 | 1 |
γ | 急性期感染者的恢复/进展速率 | 1/6 | 月-1 |
f | 疾病的病死率 | 0.15 | 1 |
br | 人群自然出生率 | 0.00021 | 1 |
dr | 人群自然死亡率 | 0.00016 | 1 |
N | 总人口数 | 100000 | 人 |
这段代码以丙肝为例,使用 Python 实现了一个基于 SEICR 模型(Susceptible-Exposed-Infectious-Chronic-Recovered)的传染病动力学模拟,旨在通过实际观测数据(每日新增感染病例)拟合模型参数,特别是传染率系数β,以模拟和预测疾病的传播动态。以下是代码的工作流程:
(1)模型定义:通过 SEICR_model
函数定义传染病的动力学模型;
(2)准备数据:导入观测数据(I_obs
)并设置初始条件;
(3)拟合模型:定义 model_fit
函数,用于计算模型输出(每日新增病例)并与观测数据进行比较,利用 solve_ivp
求解微分方程组,使用 lmfit
库的 Parameters
类设置模型参数及其初始值和边界,使模型输出与实际数据吻合,最后使用 lmfit
的 Model
类进行拟合,调用 model.fit
方法优化参数;
(4)求解模型:带入拟合得到的参数使用 solve_ivp
求解微分方程,得到模型的状态变量。
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
import pandas as pd
from lmfit import Model, Parameters
# 定义ODE函数
def SEICR_model(t, y, beta, p, omega, gamma, f, br, dr, N):
S, E, I, C, R, X = y
dSdt = br * N - beta * S * (I + C) / N - dr * S
dEdt = beta * S * (I + C) / N - omega * E - dr * E
dIdt = omega * E - gamma * I - dr * I
dCdt = p * gamma * I - (dr + f) * C
dRdt = (1-p) * gamma * I - dr * R
dXdt = omega * E # 累计新发病例
return [dSdt, dEdt, dIdt, dCdt, dRdt, dXdt]
# 原始数据
I_obs = [
36, 37, 58, 55, 39, 52, 56, 38, 54, 34, 48, 51, 34, 38, 46, 49, 40, 65, 55, 61,
59, 46, 57, 85, 64, 41, 78, 72, 67, 75, 63, 74, 77, 56, 81, 71, 52, 69, 83, 100,
112, 95, 97, 114, 103, 90, 102, 106, 82, 70, 118, 113, 131, 120, 120, 153, 156, 124,
117, 138, 139, 124, 149, 171, 176, 182, 176, 165, 200, 150, 186, 186, 165, 115, 219,
234, 218, 235, 269, 191, 232, 176, 254, 282, 189, 132, 265, 246, 238, 257, 235, 254,
249, 244, 250, 270, 180, 182, 296, 252, 266, 293, 271, 253, 247, 239, 284, 269, 252,
145, 320, 304, 308, 267, 290, 300, 303, 244, 361, 339
]
# 初始参数设置参数
beta = 0.3 # 病毒的传播率系数
omega = 1/1.5 # 相对潜伏率
gamma = 1/6 # 急性期感染者的恢复/进展速率
p = 0.7 # 感染者进入慢性期的比例
br = 0.00021 # 人群自然出生率
dr = 0.00016 # 人群自然死亡率
f = 0.15 # 疾病的病死率
N = 100000 # 总人口数
# 初始条件
E0 = 100
I0 = I_obs[0]
S0 = N - E0 - I0
C0 = 0
R0 = 0
X0 = I0
# 初始状态向量
initial_state = [S0, E0, I0, C0, R0, X0]
# 定义拟合函数
def model_fit(t, beta, p, omega, gamma, f, br, dr, N):
# 调用ode求解
result = solve_ivp(SEICR_model, (0, max(t)), initial_state, t_eval=t, args=(beta, p, omega, gamma, f, br, dr, N))
# 计算每日新发病例:dX 的差分
dX = np.diff(result.y[-1]) # X是累计新发病例,取其差值获得新发病例
return np.concatenate([[I_obs[0]], dX]) # 保证返回的数组与原始长度一致
# 创建 lmfit 模型
model = Model(model_fit)
# 设置拟合参数及初始值和边界
params = Parameters()
params.add('beta', value=0.0005, min=0, max=1)
# 固定参数,不参与拟合
params.add('omega', value=1/1.5, vary=False)
params.add('gamma', value=1/6, vary=False)
params.add('p', value=0.7, vary=False)
params.add('br', value=0.00021, vary=False)
params.add('dr', value=0.00016, vary=False)
params.add('f', value=0.15, vary=False)
params.add('N', value=100000, vary=False)
# 模拟月份数
t = np.linspace(0, 119, 120)
# 使用 lmfit 进行拟合
result = model.fit(I_obs, params, t=t)
# 输出拟合报告
print(result.fit_report())
# 使用 solve_ivp 求解微分方程
solution = solve_ivp(SEICR_model, (0, max(t)), initial_state, args=(result.params['beta'], p, omega, gamma, f, br, dr, N), t_eval=t, method='RK45')
[[Model]]
Model(model_fit)
[[Fit Statistics]]
# fitting method = leastsq
# function evals = 29
# data points = 120
# variables = 1
chi-square = 248907.034
reduced chi-square = 2091.65574
Akaike info crit = 918.481160
Bayesian info crit = 921.268652
R-squared = 0.74402581
[[Variables]]
beta: 0.12785792 +/- 3.6004e-04 (0.28%) (init = 0.0005)
omega: 0.6666667 (fixed)
gamma: 0.1666667 (fixed)
p: 0.7 (fixed)
br: 0.00021 (fixed)
dr: 0.00016 (fixed)
f: 0.15 (fixed)
N: 100000 (fixed)
这段代码使用 Matplotlib
库绘制了传染病模型的拟合结果,具体解决了以下问题:
plt.plot(t, I_obs, 'ro')
)展示实际的每日新增病例数据(I_obs
)。plt.plot(t, result.best_fit, 'b-')
)展示模型拟合的每日新增病例数据(result.best_fit
)。plt.xlabel
和 plt.ylabel
分别标注横轴(时间,单位为月)和纵轴(每日新增病例数)。plt.legend
添加图例,区分实际数据和拟合数据。plt.savefig
将绘制的图像保存为 PNG 文件(fitted_results.png
)。plt.show
在屏幕上显示绘制的图像。# 绘制拟合结果
plt.figure(figsize=(10,6))
plt.plot(t, I_obs, 'ro', label='Actual Daily Cases')
plt.plot(t, result.best_fit, 'b-', label='Fitted Daily Cases')
plt.xlabel('Time (months)')
plt.ylabel('Daily Cases')
plt.legend()
# 保存图像
plt.savefig("fitted_results.png") # 保存为 PNG 文件
plt.show()
这段代码使用 Matplotlib
库对SEICR 模型的模拟结果进行可视化,具体解决了以下问题:
solution
对象中提取了时间 t
和各状态变量(S
, E
, I
, C
, R
, X
),分别表示易感者、暴露者、感染者、慢性感染者、康复者和累计新发病例。S
)随时间的变化,展示易感人群的动态。E
)、感染者(I
)、慢性感染者(C
)和康复者(R
)随时间的变化,展示其他人群的动态。set_xlabel
和 set_ylabel
为每个子图添加横轴(时间,单位为月)和纵轴(人数)的标签。set_title
为每个子图添加标题。legend
添加图例,区分不同的状态变量。grid(True)
为每个子图添加网格线,便于观察数据趋势。plt.tight_layout()
调整子图布局,避免重叠。plt.savefig
将绘制的图像保存为 PNG 文件(SEICR.png
)。plt.show
在屏幕上显示图像。import numpy as np
import matplotlib.pyplot as plt
# 提取结果
t = solution.t
S, E, I, C, R, X = solution.y
# 创建一个2x1的子图
fig, axs = plt.subplots(2, 1, figsize=(12, 12))
# 绘制易感人群子图 (S)
axs[0].plot(t, S, label="Susceptible (S)", color="blue")
axs[0].set_xlabel("Time (months)")
axs[0].set_ylabel("Number of people")
axs[0].set_title("SEICR Simulation (S)")
axs[0].legend()
axs[0].grid(True)
# 绘制剩余人群部分的子图 (E, I, C, R)
axs[1].plot(t, E, label="Exposed (E)", color="orange")
axs[1].plot(t, I, label="Infected (I)", color="red")
axs[1].plot(t, C, label="Chronic (C)", color="gray")
axs[1].plot(t, R, label="Recovered (R)", color="green")
axs[1].set_xlabel("Time (months)")
axs[1].set_ylabel("Number of people")
axs[1].set_title("SEICR Simulation (E, I, C, R)")
axs[1].legend()
axs[1].grid(True)
# 调整布局
plt.tight_layout()
# 保存图像
plt.savefig("SEICR.png") # 保存为 PNG 文件
# 显示图像
plt.show()
(1)易感人群(S):易感人群数量呈持续下降趋势,从接近10万人减少到约8.4万人左右。这种下降是因为易感人群逐渐通过暴露(E)转化为感染者(I)或慢性患者(C),最终减少了总易感人群。这一结果表明疾病正在社区传播,健康个体逐渐被暴露于疾病的风险中,这可能是由于传染性强或者接触率较高。
(2)暴露人群(E):暴露人群缓慢上升,但数量较低,始终在较低水平。这表明暴露阶段较短,个体快速转化为感染者(I),符合丙肝潜伏期较短的特性。
(3)感染人群(I):感染人群增长显著,约在60个月后(5年),感染人群达到高峰,随后增速减缓。
(4)慢性患者(C):慢性患者人数缓慢增加,到120个月(10年)时达到稳定值。这表明部分急性感染者转化为慢性患者,符合丙肝的特点(急性转慢性几率高)。
(5)恢复人群(R):恢复人群显著增长,到120个月时成为最大人群。通过自然免疫或治疗措施,感染者逐渐康复,形成免疫,减少了传播链。
(1)疫苗接种:疫苗接种可以使一部分易感者直接获得免疫力,从而从易感人群(S)仓室转移到康复者(R)仓室(假设疫苗提供完全免疫)。这相当于减少了初始的易感人群数量。在模型中,可以通过增加疫苗接种率 φ 和 S→R 的流向来表示。
(2)引入抗病毒治疗:引入针对感染者 I 和慢性感染者 C 的抗病毒治疗机制,将部分感染者转化为恢复者R。假设治疗比例为假设治疗比例为θ,且慢性患者治疗成功比例为θc,参数 θ 和 θc 分别用于调整感染者(I)和慢性感染者(C)转化为康复者(R)的速率,体现在模型中对 I 和 C 的动态方程中。可以用于模拟抗病毒药物的推广效果,评估治疗覆盖率和治疗成功率对疾病流行的影响。
(3)隔离措施(减少 I 仓室的人群数量):引入隔离措施,增加隔离比例q、隔离仓室 Q 以及 I→Q、C→Q 的流向,模拟将急性和慢性期病人隔离后的传播能力。
(4)引入免疫衰退机制:考虑部分恢复者的免疫会随着时间衰退,重新回到易感状态,添加免疫衰退率 δ 和 R→S 的流向。