新冠预测代码复现:从数据集清洗到SIR/SEIR与时序模型实战 简介这份资源面向数据分析初学者、公共卫生方向学生及疫情建模爱好者提供一套可直接运行的COVID-19世界疫情分析与预测方案帮助读者掌握从数据获取到模型预测的完整流程。压缩包共1089个文件约19.3MB以513个Python源码和508个编译文件为主体另含17个CSV数据集、17个可执行程序及少量配置与说明文件覆盖训练集、结果集与提交文件等数据形态。内容围绕疫情数据预处理、matplotlib与PyEcharts可视化展开可绘制柱形图、折线图、地图、玫瑰图及动态条形图并基于SIR模型对美国疫情数据进行模拟预测适合作为课程设计或数据竞赛的参考实现。目前已有3562人学习下载读者可借此理解疫情数据清洗、图表呈现与传染病建模的衔接方式并参考目录结构快速定位源码与数据减少环境配置与调试成本。1. 新冠预测代码复现从一份数据集到一个能跑通的预测管线2020 年之后但凡做过时间序列预测的人手里大概率都跑过 COVID-19 prediction 这类项目。它看起来只是「拿历史确诊数预测未来几天」但真正动手你会发现数据里藏着累计值转日增、检测能力波动、周末报告延迟、多国曲线形态差异巨大这些坑。这份 COVID-19 世界新冠疫情预测代码含数据集要解决的就是把原始各国累计确诊、死亡、恢复数据清洗成可建模的日序列再套上 SIR/SEIR 或时序模型输出未来趋势。它适合两类人一类是想找一个真实、脏、有代表性的数据集练手时间序列的工程师另一类是要快速搭出传染病预测基线、再往上叠模型的研究者。下面我按自己复现时的顺序把选型、清洗、建模、评估和踩坑讲清楚。2. 数据拿到手先别建模COVID-19 数据集的结构与清洗决策2.1 这份数据集通常长什么样常见的 COVID-19 世界疫情数据集来源多是 JHU CSSE 的 time_series 表结构大致是「省份/国家 经纬度 日期列」每个日期列存的是累计确诊、累计死亡、累计恢复。也就是说它不是一行一条记录的长表而是宽表日期是列名。很多人第一次读进来直接df.head()看到一堆日期列就懵了这是正常的。我一般会先把宽表 melt 成长表字段变成Country/Region, Date, Confirmed, Deaths, Recovered。这一步不做后面所有按日期聚合、按国家画图的操作都会很别扭。另一个必须确认的点是这份数据里中国是按省份拆的美国是按县/州拆的而有些国家只有一行。如果你要按国家建模就得先决定是聚合到国家层面还是保留省级粒度。我的经验是做全球对比就聚合到国家做区域传播就保留省级两者不要混。2.2 累计值转日增三个必须处理的边界累计确诊转日增看起来就是diff()但实际有三个坑。第一累计值偶尔会回退比如某国修正数据后总数变小diff()出来就是负数。第二不同国家起始日期不同有的 1 月就有数据有的 3 月才开始直接 diff 会在开头产生 NaN。第三死亡和恢复的累计值更新频率和确诊不一致三者对齐后可能出现日增死亡大于日增确诊的荒谬情况。处理方式我一般固定成一套先按国家分组、按日期排序再对累计值做clip(lower0)防止负回退然后diff().fillna(0)最后把负的日增统一置 0。下面这段是我常用的清洗骨架。import pandas as pd import numpy as np # df_wide: 宽表含 Country/Region, Province/State, Lat, Long 和若干日期列 id_cols [Country/Region, Province/State, Lat, Long] date_cols [c for c in df_wide.columns if c not in id_cols] df_long df_wide.melt( id_varsid_cols, value_varsdate_cols, var_nameDate, value_nameConfirmed ) df_long[Date] pd.to_datetime(df_long[Date]) # 聚合到国家层面避免省级重复计数 df_country ( df_long.groupby([Country/Region, Date], as_indexFalse)[Confirmed] .sum() .sort_values([Country/Region, Date]) ) # 累计转日增处理负回退 df_country[Daily] ( df_country.groupby(Country/Region)[Confirmed] .diff() .fillna(0) .clip(lower0) )逻辑说明melt把日期列转成行groupby sum把省级合并到国家diff在组内做保证不会跨国家相减。参数上clip(lower0)是关键它把修正导致的负日增压成 0比直接删掉更稳因为删掉会破坏时间连续性。如果你要做 7 日滑动平均建议在日增之后做而不是在累计值上做否则平滑的是累计曲线看不出日增的峰。2.3 哪些国家适合建模哪些直接排除不是所有国家都值得放进预测管线。我的筛选标准是三条累计确诊至少几百例、时间序列长度超过 30 天、日增没有长期为 0 的断档。像一些只有几十例的小国曲线基本是平的模型学不到东西反而拉低整体评估。另一个要排除的是早期数据里「钻石公主号」这类特殊条目它不属于任何国家混进去会污染国家聚合。筛选完通常剩下 150 到 180 个国家左右。这个数量对单机跑 SIR 拟合是够的但如果要跑深度学习模型建议再挑 20 到 30 个代表性国家做重点分析全量跑容易在数据质量差的国家上浪费算力。3. 模型怎么选SIR/SEIR 与时序模型的适用边界3.1 SIR/SEIR 的参数含义与拟合思路SIR 是最经典的传染病模型三个仓室易感者 S、感染者 I、移除者 R。核心参数是 β感染率和 γ恢复率R0 β/γ。SEIR 多一个潜伏期仓室 E适合新冠这种有潜伏期的病。用 SIR 做预测本质是拿历史确诊数去拟合 β 和 γ然后往前推。拟合时我一般用scipy.integrate.odeint解微分方程再用scipy.optimize.minimize最小化预测累计确诊和真实累计确诊的误差。这里有个关键选择拟合目标用累计确诊还是日增我的经验是累计更稳因为日增噪声大但累计对早期数据敏感。折中做法是拟合累计评估时看日增。from scipy.integrate import odeint from scipy.optimize import minimize import numpy as np def sir_model(y, t, beta, gamma, N): S, I, R y dS -beta * S * I / N dI beta * S * I / N - gamma * I dR gamma * I return dS, dI, dR def fit_sir(confirmed, N, days): I0 confirmed[0] R0 0 S0 N - I0 - R0 y0 (S0, I0, R0) t np.arange(len(confirmed)) def loss(params): beta, gamma params if beta 0 or gamma 0: return 1e10 sol odeint(sir_model, y0, t, args(beta, gamma, N)) pred sol[:, 1] sol[:, 2] # 累计 I R return np.mean((pred - confirmed) ** 2) res minimize(loss, x0[0.3, 0.1], bounds[(0.01, 1.0), (0.01, 1.0)]) return res.x逻辑说明sir_model是标准 SIR 微分方程N是总人口fit_sir用最小二乘拟合 β 和 γ。参数上bounds很关键不加边界优化器可能跑出负的 β 或 γ物理上无意义。N一般用该国总人口但实际感染人数远小于总人口时S 近似不变模型会退化成指数增长这也是 SIR 在疫情后期预测偏差大的原因。3.2 时序模型ARIMA、LSTM 和 Prophet 的取舍如果你不想碰微分方程时序模型是更直接的选择。ARIMA 适合短期平稳序列但新冠日增有明显的趋势和突变ARIMA 需要差分和调参实际用起来不如 Prophet 省事。Prophet 对缺失值和异常值容忍度高自带趋势和季节项适合快速出基线。LSTM 能学非线性但需要足够长的序列且容易过拟合小国数据根本喂不饱。我的建议是先用 Prophet 跑一版基线看整体趋势对不对再用 SIR/SEIR 做机理解释如果要做多国对比或长序列再上 LSTM 或 Transformer。不要一上来就 LSTM数据清洗没做好模型再深也是白搭。3.3 评估指标别只看 RMSE预测疫情RMSE 和 MAE 是基础但不够。因为疫情曲线有峰峰值时间和峰值高度的预测误差比整体 RMSE 更重要。我一般会额外算两个指标峰值日误差预测峰值日期和真实峰值日期差几天和峰值幅度误差预测峰值和真实峰值的比例。这两个指标能直接反映模型有没有抓住疫情的关键转折。另外评估要分阶段。用前 70% 数据训练后 30% 测试但测试集里如果包含封城、疫苗推广这类政策突变模型误差会突然变大这不是模型的错是数据生成过程变了。所以我在报告结果时会标注哪些时间段有重大干预避免误读。4. 避坑与排查新冠预测代码里最容易翻车的五件事4.1 现象日增曲线出现规律性锯齿每七天一个峰原因周末检测量下降报告延迟导致周六周日确诊数偏低周一集中补报。这是全球数据集的通病不是某个国家的问题。解决对日增做 7 日滑动平均或者用 Prophet 时把 weekly seasonality 打开。滑动平均会损失最新几天的数据所以预测时要么接受这个延迟要么用模型补最新点。4.2 现象SIR 拟合出来的 R0 高达 10 以上明显不合理原因总人口 N 设得太小或者拟合时用了累计确诊但没考虑实际感染人数远大于确诊。另一个常见原因是数据早期增长太快SIR 在 S 近似不变时退化成指数β 被高估。解决检查 N 是否用了该国总人口确认拟合区间避开了疫情最初几天的指数段或者改用 SEIR 加入潜伏期。如果 R0 还是离谱就固定 γ 为文献值比如 1/7只拟合 β。4.3 现象LSTM 训练 loss 很低但预测未来几天全是直线原因序列太短模型学到了「预测均值」这个偷懒策略。新冠日增波动大LSTM 在数据不足时倾向于输出平滑值。解决增加输入序列长度或者改用 Prophet/ARIMA 这类对短序列更稳的模型。如果坚持用 LSTM至少保证每个国家有 100 天以上数据并做差分让序列平稳。4.4 现象不同国家预测结果差异巨大有的准有的完全偏原因各国检测能力、报告标准、人口年龄结构不同同一个模型参数不可能适配所有国家。这不是代码 bug是数据本身的异质性。解决分国家单独拟合参数或者按疫情阶段爆发期、平台期、下降期分段建模。全球统一模型只适合做趋势参考不适合精确预测。4.5 现象累计确诊转日增后开头几天全是 0模型学不到早期动态原因很多国家在累计确诊达到一定数量后才开始报告之前的日增是 0但实际已经有传播。解决截掉开头连续为 0 的区间从第一个非零日增开始建模。如果要做早期预警得用其他数据源补比如航班流量或搜索指数单靠确诊数不够。5. 把预测管线跑稳从单国拟合到多国批量评估的进阶技巧单国跑通之后下一步通常是批量处理几十上百个国家。这时候最大的问题是每个国家都要调参手动跑不现实。我的做法是写一个批量拟合函数对每个国家单独拟合 SIR记录 β、γ、R0 和拟合误差最后用表格汇总。这样能快速看出哪些国家拟合好、哪些差差的再单独排查。def batch_fit(df_country, pop_dict, min_cases500): results [] for country, grp in df_country.groupby(Country/Region): grp grp.sort_values(Date) confirmed grp[Confirmed].values if confirmed[-1] min_cases or len(confirmed) 30: continue N pop_dict.get(country) if not N: continue try: beta, gamma fit_sir(confirmed, N, len(confirmed)) results.append({ Country: country, beta: beta, gamma: gamma, R0: beta / gamma, days: len(confirmed), final_confirmed: confirmed[-1] }) except Exception as e: results.append({Country: country, error: str(e)}) return pd.DataFrame(results)逻辑说明min_cases过滤掉太小国家pop_dict是国家人口字典fit_sir复用前面的拟合函数。参数上min_cases500是我试出来的经验值低于这个数拟合不稳定。days 30也排除序列太短拟合没有意义。跑完这张表你可以按 R0 排序看看哪些国家 R0 异常高再回去检查数据。另一个进阶技巧是滚动预测验证。不要只用一次 train/test split而是用滚动窗口每次用前 N 天预测后 7 天然后窗口往后滑。这样能看出模型在不同阶段的稳定性。我一般会画一张图横轴是预测起始日纵轴是 7 天预测误差误差突然变大的点往往对应政策干预或数据修正这些点值得单独标注。最后说一个我自己的习惯每次跑完预测我都会把预测曲线和真实曲线叠在一起看而不是只看指标。指标好但曲线形态不对的情况太常见了比如模型预测了一个平滑的峰真实曲线是双峰这种误差指标反映不出来但实际决策会出大问题。疫情预测这件事数据清洗占六成时间模型占三成评估和可视化占一成但那一成往往决定你信不信这个结果。希望帮到你。本文还有配套的精品资源点击获取