拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于MATLAB的SIR传染病模型:从原理到实现的完整指南

1. 项目概述从数据到决策的桥梁最近几年无论是公共卫生事件还是网络信息传播对“传染”过程的量化分析和预测需求越来越迫切。作为一名长期和数据、模型打交道的从业者我深刻体会到一个直观、可控且能快速验证想法的模型工具是多么重要。很多人一听到“建模”就觉得高深莫测需要复杂的编程和数学功底其实不然。今天我想分享的就是如何利用我们熟悉的工具——MATLAB来亲手搭建一个属于自己的“传染源模型”。这个模型的核心目标不是追求极致的学术精度而是构建一个理解传播动力学的“沙盘”让你能通过调整几个关键参数直观地看到传染是如何发生、发展乃至被控制的。所谓“传染源模型”在广义上指的是对某种事物如病毒、信息、行为从一个源头出发在特定群体中扩散过程的数学抽象和计算机模拟。它解决的痛点很明确当我们无法在现实中进行大规模、高成本的实验时如何预测趋势、评估干预措施的效果比如一种新产品的口碑如何传播一条关键信息在社交网络中的扩散路径是怎样的或者更经典的一种传染病在人群中的流行规模有多大MATLAB以其强大的矩阵运算能力、丰富的可视化工具和相对友好的语法成为了实现这类模型的原型验证和教学演示的绝佳平台。无论你是相关领域的学生、初涉数据分析的工程师还是希望对复杂系统有更直观理解的管理者跟着这篇手把手的指南你都能从零开始构建并运行一个基础的模型获得对传播过程的“手感”。2. 模型核心思路与框架选择在动手写代码之前我们必须先想清楚模型要模拟什么以及用什么框架来模拟。这就像盖房子前先画图纸决定了后续所有工作的方向和复杂度。2.1 经典模型SIR及其变体在传染病建模领域有几个经久不衰的经典框架它们将人群划分为几个互斥的“仓室”Compartment并通过微分方程描述仓室间个体的流动。最著名的莫过于SIR模型易感者 (S)尚未感染但有可能被感染的个体。感染者 (I)已感染且具备传染能力的个体。移除者 (R)感染后康复并获得长期免疫力或死亡的个体不再参与传播过程。这个模型通过三个核心参数驱动感染率β一个感染者每天能传染多少个易感者、恢复率γ感染者每天康复的比例、以及总人口数N。其微分方程简洁而深刻 dS/dt -β * I * S / N dI/dt β * I * S / N - γ * I dR/dt γ * I这个框架的魅力在于其普适性。你可以很容易地将它改造成SIS模型康复后不免疫直接变回易感者用于模拟某些细菌感染或网络谣言反复出现、SEIR模型增加一个“潜伏者E”仓室用于模拟有潜伏期的疾病如新冠肺炎等等。对于我们的入门目标SIR模型已经足够展示核心逻辑。注意选择SIR作为起点不是因为它最复杂而是因为它要素最全包含了传染、移出两个关键过程且易于理解和扩展。在MATLAB中实现它能让我们集中精力理解模型机制与编程实现的结合而不是被复杂的数学公式吓退。2.2 为何选择确定性模型与常微分方程ODE你可能听说过“基于智能体的模型”ABM它模拟每个个体的独立行为虽然更灵活但计算开销大且结果随机性高。对于快速理解宏观趋势和参数影响我强烈建议从确定性常微分方程ODE模型开始。确定性模型意味着给定一组确定的参数和初始条件模型每次运行的结果都是一样的。它描述的是群体的“平均”行为。我们用ODE来求解上述SIR的微分方程组。MATLAB内置了强大的ODE求解器如ode45我们只需要定义好方程它就能帮我们计算出S、I、R随时间变化的曲线。这比自己去写离散时间的迭代循环更高效、更精确尤其是对于变化剧烈的情况。核心思路总结我们的项目将采用经典的SIR仓室模型框架通过MATLAB的ODE求解器来模拟传染病在封闭人群中的动态传播过程。我们会从定义参数和方程开始然后求解并可视化结果最后再探讨如何调整模型以适应更复杂的场景。3. MATLAB环境准备与模型参数定义工欲善其事必先利其器。让我们先打开MATLAB创建一个新的脚本文件例如SIR_Model.m然后开始定义模型的“原材料”。3.1 关键参数与初始条件设置模型的行为完全由几个参数和初始值决定。我们需要在脚本开头清晰地定义它们% 1. 模型参数定义 total_population 1000; % 总人口数 N initial_infected 1; % 初始感染者数量 I0 initial_recovered 0; % 初始康复者数量 R0 % 初始易感者数量 S0 N - I0 - R0 initial_susceptible total_population - initial_infected - initial_recovered; beta 0.3; % 感染率参数表示一个感染者每天有效接触并传染的人数 gamma 0.1; % 恢复率参数表示感染者每天康复的比例 (平均感染期 1/gamma 10天)这里有几个关键点需要解释beta感染率这是模型中最敏感、最需要琢磨的参数。beta0.3意味着在完全易感的人群中一个感染者平均每天能使0.3个人感染。注意这不是一个概率而是一个比率。它的实际大小与人群接触频率、疾病传染能力密切相关。gamma恢复率其倒数1/gamma代表平均感染期。gamma0.1意味着平均感染期是10天。这个参数通常可以通过临床观察数据估算相对稳定。基本再生数R0这是一个极其重要的衍生指标R0 beta / gamma。它表示在完全易感的人群中一个感染者在其整个传染期内平均能传染多少人。R0 1疾病会流行R0 1疾病会逐渐消失。我们这里R03属于较强传染性。3.2 集成参数与时间设置为了便于向ODE求解器传递参数我们通常将它们打包成一个向量。同时定义模拟的时间范围% 2. 时间跨度设置单位天 simulation_days 150; % 模拟150天 tspan [0 simulation_days]; % 时间向量从第0天到第150天 % 3. 初始状态向量 (顺序很重要 [S; I; R]) initial_state [initial_susceptible; initial_infected; initial_recovered]; % 4. 参数打包用于传递给ODE函数 params.beta beta; params.gamma gamma; params.N total_population;实操心得将参数打包进一个结构体params而不是使用全局变量是一种更清晰、更安全的做法。它避免了变量作用域的混乱尤其在函数调用时非常方便。另外将initial_state明确写成列向量[S; I; R]是因为MATLAB的ODE求解器默认状态变量是列向量这能避免一些不必要的维度错误。4. 核心引擎定义微分方程与求解这是模型的心脏部分。我们需要定义一个函数专门用来计算S、I、R在每个时间点的变化率导数。4.1 编写ODE方程函数在同一目录下创建一个函数文件命名为sir_equations.mfunction dydt sir_equations(t, y, params) % SIR模型微分方程 % 输入 % t: 时间未直接使用但ODE求解器要求此参数 % y: 当前状态向量 [S; I; R] % params: 包含beta, gamma, N的结构体 % 输出 % dydt: 状态向量的导数 [dS/dt; dI/dt; dR/dt] % 从状态向量y中解包出当前S, I, R S y(1); I y(2); R y(3); % 从参数结构体中取出参数 beta params.beta; gamma params.gamma; N params.N; % 核心的SIR微分方程 dS_dt -beta * I * S / N; % 易感者减少的速度 dI_dt beta * I * S / N - gamma * I; % 感染者变化速度新增-移除 dR_dt gamma * I; % 移除者增加的速度 % 将导数组合成列向量输出 dydt [dS_dt; dI_dt; dR_dt]; end为什么这么写方程dS/dt -β * I * S / N中的β * I / N部分可以理解为每个易感者S被感染的概率或力。I/N是感染者占总人口的比例β是接触并传染的有效率。两者相乘再乘以S就是单位时间内新增的感染者数也就是易感者的减少数。感染者增加数等于易感者减少数但同时自身以速率γI康复移出。4.2 调用ODE求解器进行模拟回到主脚本SIR_Model.m我们使用ode45求解器来调用刚才定义的方程% 5. 使用ode45求解微分方程组 % 使用匿名函数将额外的参数params固定传递给sir_equations [t, Y] ode45((t,y) sir_equations(t, y, params), tspan, initial_state); % 6. 提取结果 % Y是一个矩阵每一行对应一个时间点列分别是S, I, R S_t Y(:, 1); % 易感者数量随时间变化 I_t Y(:, 2); % 感染者数量随时间变化 R_t Y(:, 3); % 移除者数量随时间变化ode45是MATLAB中一个非常常用的非刚性微分方程求解器它采用Runge-Kutta方法对于大多数光滑问题都有很好的精度和效率。(t,y) sir_equations(t, y, params)这种写法创建了一个匿名函数将我们定义好的params结构体“绑定”到了方程函数上满足了ode45对微分方程函数必须只接受t和y两个输入的要求。5. 结果可视化与初步分析模型跑完了但一堆数字看不出所以然。可视化是理解模型输出的关键。我们将绘制经典的流行曲线图。5.1 绘制SIR人群动态曲线% 7. 绘制SIR三类人群随时间变化的曲线 figure(Position, [100, 100, 900, 500]) % 设置图形窗口大小 plot(t, S_t, b-, LineWidth, 2, DisplayName, 易感者 (S)); hold on; plot(t, I_t, r-, LineWidth, 2, DisplayName, 感染者 (I)); plot(t, R_t, g-, LineWidth, 2, DisplayName, 移除者 (R)); hold off; grid on; xlabel(时间 (天), FontSize, 12); ylabel(人数, FontSize, 12); title(sprintf(SIR传染病模型模拟 (N%d, \\beta%.2f, \\gamma%.2f, R_0%.2f), ... total_population, beta, gamma, beta/gamma), FontSize, 14); legend(Location, best); set(gca, FontSize, 11); % 设置坐标轴字体大小运行这段代码你会得到一张清晰的图表。红色感染曲线(I)会先上升达到一个峰值然后下降。蓝色易感者曲线(S)单调下降绿色移除者曲线(R)单调上升。最终感染者归零人群被划分为易感者和移除者两部分。峰值的高度和到来的时间是评估疫情严重程度和医疗资源需求的关键。5.2 提取关键流行病学指标从结果中我们可以程序化地计算一些重要指标% 8. 计算关键指标 % 流行峰值最大同时感染人数 [peak_infected, peak_idx] max(I_t); peak_time t(peak_idx); fprintf(疫情峰值发生在第 %.1f 天峰值感染人数为 %.0f 人。\n, peak_time, peak_infected); % 最终感染规模累计感染人数近似为最终的R final_epidemic_size R_t(end); attack_rate final_epidemic_size / total_population * 100; fprintf(最终累计感染人数约为 %.0f 人占总人口的 %.1f%%。\n, final_epidemic_size, attack_rate); % 基本再生数R0理论值 R0_theoretical beta / gamma; fprintf(理论基本再生数 R0 β / γ %.2f。\n, R0_theoretical);这些指标能让你对模拟的疫情有一个量化的认识。例如你可以尝试改变beta观察R0和最终感染规模如何变化直观理解“拉平曲线”的含义。注意事项这里计算的“最终累计感染人数”用R_t(end)近似在SIR模型中是合理的因为最终所有感染者都会进入R仓室。但在现实中有自然出生死亡等情况这个模型就不完全适用了。6. 模型扩展与场景探索基础SIR模型是一个完美的起点但现实世界更复杂。我们可以通过修改方程轻松探索不同场景。6.1 场景一引入疫苗接种假设在疫情开始时有一部分人口通过接种疫苗获得了完全免疫。我们只需要修改初始条件% 扩展考虑初始疫苗接种率 vaccination_rate 0.3; % 30%的人口在初始时已免疫 initial_vaccinated round(total_population * vaccination_rate); initial_susceptible total_population - initial_infected - initial_vaccinated; % R0中包含了已免疫者 initial_state_vax [initial_susceptible; initial_infected; initial_vaccinated]; % 重新求解并绘图略需复制求解和绘图代码修改初始状态和标题你会发现即使R01足够高的初始免疫率群体免疫阈值约为1 - 1/R0也能阻止疫情大规模流行感染曲线只会出现一个小高峰。6.2 场景二模拟隔离或社交疏远措施在疫情发展中我们采取了降低接触率的措施。这可以通过让感染率beta随时间变化来实现% 扩展随时间变化的感染率模拟干预 beta_intervention 0.1; % 干预后的感染率 intervention_start_day 30; intervention_end_day 90; % 修改 sir_equations 函数或者更优雅地在主程序中定义一个动态beta % 方法创建一个新的ODE函数在函数内部根据时间t判断使用哪个beta这需要你修改sir_equations函数使其能够接受一个与时间相关的beta(t)函数。你会看到在干预期间感染曲线迅速被压低形成一个平台或双峰。6.3 从确定性模型到随机性模型确定性ODE给出的是平均路径。但现实充满随机性尤其是在疫情初期感染者很少的时候。我们可以用随机微分方程SDE或Gillespie算法来模拟这种随机性。在MATLAB中这复杂度更高但能展示疫情可能“随机熄灭”或“暴发”的不同命运对于评估小规模输入性风险尤为重要。由于篇幅所限这里不展开代码但思路是将感染和恢复事件视为随机过程用概率分布来决定下一个事件发生的时间和类型。7. 参数估计与模型验证思考我们一直在假设参数是已知的。但现实中beta和gamma或R0是需要从数据中估计的。这才是建模工作中最具挑战性也最有趣的部分。7.1 如何利用现实数据假设你有一段时间内每日新增感染病例的报道数据。你可以模型校准以beta和gamma或初始感染数I0为待估参数以模型模拟出的新增感染曲线dI/dt dR/dt或近似为gamma * I与真实数据之间的差异如最小二乘法为目标函数使用MATLAB的优化工具箱如fminsearch,lsqcurvefit来寻找最优参数。不确定性量化估计出的参数存在不确定性。可以使用马尔可夫链蒙特卡洛MCMC等方法来得到参数的后验分布从而给出预测的置信区间。7.2 模型局限性与改进方向必须清醒认识到这个基础SIR模型的局限性同质性假设它假设人群完全混合每个个体接触他人的机会均等。现实中存在年龄结构、接触网络、空间异质性。无人口动力学没有考虑出生、死亡、迁入迁出。无病态细分感染者状态单一没有区分轻症、重症、无症状。确定性忽略了随机波动尤其在疫情初期。对应的改进方向就是构建更复杂的模型网络SIR模型、元胞自动机模型、基于智能体的模型ABM、考虑年龄结构的接触矩阵模型等。但无论如何经典的SIR模型及其ODE实现都是理解所有这些复杂模型根基的必经之路。8. 常见问题与调试技巧实录在实际操作中你可能会遇到一些典型问题。以下是我在多次建模中总结的一些排查经验问题现象可能原因排查与解决思路感染曲线(I)不上升直接下降感染率beta设置过低或恢复率gamma设置过高导致R0 1。检查beta和gamma的值计算R0 beta/gamma。确保R0 1疫情才会流行。可以尝试增大beta或减小gamma。感染者数量超过总人口微分方程写错或者参数数量级有问题如beta远大于1。仔细核对sir_equations.m中的方程特别是beta * I * S / N这一项确保除以了总人口N。检查beta的定义它通常是一个小于1的数。ODE求解器报错如奇异矩阵初始条件设置不当导致某个状态变量为负数。检查initial_state是否都为正数且S0 I0 R0是否等于N。确保在方程中不会出现除以零的情况虽然SIR模型在S0时导数也有定义但数值计算需注意。图形显示异常曲线重叠或消失绘图时hold on/hold off使用不当或图例标签DisplayName未设置。确保在绘制第一条曲线后使用hold on全部绘制完成后使用hold off。检查每条plot命令中是否都设置了唯一的‘DisplayName’。模拟结果与预期或文献不符参数单位不一致或对模型假设理解有误。确认时间单位天/周gamma的倒数平均感染期单位是否匹配。重温SIR模型的假设完全混合、无免疫力等看是否适用于你的场景。一个关键的调试技巧在开发sir_equations函数时可以先在一个固定的时间点如t0用初始状态y0和参数params手动计算一次导数dydt看看输出是否合理例如dS/dt应为负数dI/dt可能为正可能为负dR/dt为正。这能快速定位方程编码错误。最后我想分享的一点个人体会是传染源建模的魅力不在于复现一个完美的预测——那几乎是不可能的因为现实太复杂。它的价值在于提供一个“如果-那么”的思考框架。通过这个在MATLAB中搭建的沙盘你可以不断地问如果传染性增强20%会怎样如果提前一周采取隔离措施会怎样如果有一半人打了疫苗会怎样然后立刻看到模拟结果。这种快速迭代、量化评估的能力对于形成直觉、支持决策、与他人沟通想法都是无价的。从今天这个简单的SIR模型开始你已经掌握了这个强大工具的核心开关。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门