
简介这份资源面向数据分析初学者、公共卫生研究者及机器学习入门者提供一套完整的新冠疫情分析与预测实战方案帮助读者掌握从数据获取到建模预测的全流程技能。包内共1089个文件以513个Python源码和508个编译文件为核心辅以17个CSV数据集、17个可执行程序及若干配置与说明文件压缩包约19.3MB结构清晰便于按模块查阅。已有3562人学习下载热度较高。资源涵盖疫情数据获取与预处理、matplotlib与PyEcharts可视化柱形图、折线图、地图、玫瑰图、动态条形图并基于SIR模型对美国疫情数据进行模拟预测读者可借此理解传染病建模思路、掌握多维度可视化技巧并参考完整代码与数据集快速复现实验适合作为课程设计或数据分析练手项目。1. 从一份新冠预测代码说起时序建模到底在预测什么2020 年之后几乎每个做数据的人都被问过同一个问题明天新增多少这个问题听起来简单落到代码里却是一整套时间序列建模的活。COVID-19 prediction 这类项目核心不是预测疫情这么宏大的命题而是把每日确诊、死亡、康复这些数字当成一条时间序列用历史窗口去推未来窗口。它解决的是给定过去 N 天未来 M 天大概什么走势适合有 Python 基础、想入门时序预测的工程师也适合需要快速搭一个 baseline 做对比的研究者。我见过太多人一上来就上 LSTM结果连数据对齐都没做对预测曲线直接平移错位。这篇就把这条链路从数据到模型到评估完整走一遍重点讲清楚哪些参数真正影响结果、哪些坑会让你白跑一晚上。2. 数据准备从原始 CSV 到可训练的滑动窗口2.1 先搞清楚数据长什么样常见的 COVID-19 数据集一般是按国家/地区 日期组织的宽表或长表。长表更通用典型字段是date、country、confirmed、deaths、recovered。拿到手第一件事不是画图是确认三件事时间是否连续、累计值还是增量、缺失值怎么标的。累计值转增量是最容易翻车的地方。很多数据集给的是累计确诊直接拿去训练模型学到的是一条单调递增曲线预测出来永远只涨不跌看着很准其实毫无意义。正确做法是先做一阶差分import pandas as pd import numpy as np # 读取长表数据假设列名为 date, country, confirmed, deaths df pd.read_csv(covid_data.csv, parse_dates[date]) # 按国家和日期排序保证差分方向正确 df df.sort_values([country, date]).reset_index(dropTrue) # 累计值转每日新增groupby 保证不跨国家差分 df[new_confirmed] df.groupby(country)[confirmed].diff().fillna(0) # 差分后可能出现负数数据修正导致统一截断到 0 df[new_confirmed] df[new_confirmed].clip(lower0) print(df[[date, country, confirmed, new_confirmed]].head(10))这段代码的关键在groupby(country).diff()。如果直接对整个 DataFrame 做diff()第一个国家的最后一行会和第二个国家的第一行相减产生一个巨大的异常值。clip(lower0)是后悔药因为真实数据里经常出现累计值回退统计口径调整差分后是负数不处理会污染训练集。2.2 滑动窗口的构造逻辑时序预测的监督学习化本质是把一条序列切成 (输入窗口, 输出窗口) 的样本对。假设用过去 14 天预测未来 7 天def make_windows(series, input_len14, output_len7): X, y [], [] total len(series) for i in range(total - input_len - output_len 1): X.append(series[i : i input_len]) y.append(series[i input_len : i input_len output_len]) return np.array(X), np.array(y) # 对单个国家的 new_confirmed 序列构造样本 series df[df[country] US][new_confirmed].values.astype(float) X, y make_windows(series, input_len14, output_len7) print(X.shape, y.shape) # (样本数, 14) (样本数, 7)input_len和output_len是最需要调的参数。14 天对应新冠常见潜伏期和报告延迟7 天对应一周的周期节律。如果你发现预测曲线整体滞后先把input_len加大到 21 试试如果预测太平滑、抓不住峰值把output_len缩小到 3 或 5。2.3 归一化别用全局均值新手最爱犯的错是用整个数据集的均值和方差做归一化。时序数据必须按训练集统计量来否则验证集的信息泄漏进训练。更稳妥的是按国家分别归一化因为美国和一个小国的日增数量级差几个数量级。from sklearn.preprocessing import MinMaxScaler # 只用训练段拟合 scaler再变换全段 split int(len(series) * 0.8) scaler MinMaxScaler() scaler.fit(series[:split].reshape(-1, 1)) series_scaled scaler.transform(series.reshape(-1, 1)).flatten() X, y make_windows(series_scaled, 14, 7)预测完之后记得scaler.inverse_transform还原否则你评估的误差是在 0 到 1 的尺度上算的没有实际意义。3. 模型选型LSTM、GRU 还是 Prophet怎么选不纠结3.1 三类方案的适用边界时序预测在这个场景下有三条主流路线各有各的脾气。统计模型ARIMA、Prophet适合数据量小、趋势平稳、需要可解释性的场景。Prophet 对缺失值和异常值容忍度高几行代码就能出结果但遇到新冠这种政策突变导致的断崖式下跌它反应很慢。RNN 系LSTM、GRU适合有足够样本、需要捕捉非线性模式的场景。GRU 比 LSTM 少一个门参数少约 25%在小数据集上往往更快收敛且不容易过拟合。我的经验是样本少于 500 条用 GRU多于 2000 条再考虑 LSTM。Transformer 系Informer、Autoformer理论上更强但对这个规模的数据集属于杀鸡用牛刀训练成本高、调参复杂不建议作为第一个 baseline。3.2 一个能跑通的 GRU baselineimport torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset class GRUForecaster(nn.Module): def __init__(self, input_len14, output_len7, hidden64, layers2): super().__init__() self.gru nn.GRU( input_size1, hidden_sizehidden, num_layerslayers, batch_firstTrue, dropout0.2 if layers 1 else 0.0 ) self.fc nn.Linear(hidden, output_len) def forward(self, x): # x: (batch, input_len, 1) out, _ self.gru(x) last out[:, -1, :] # 取最后一个时间步的隐状态 return self.fc(last) # (batch, output_len) # 构造 DataLoader X_t torch.tensor(X, dtypetorch.float32).unsqueeze(-1) # 加特征维 y_t torch.tensor(y, dtypetorch.float32) loader DataLoader(TensorDataset(X_t, y_t), batch_size32, shuffleTrue) model GRUForecaster() optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.MSELoss() for epoch in range(50): model.train() total_loss 0 for xb, yb in loader: pred model(xb) loss criterion(pred, yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() if (epoch 1) % 10 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.6f})hidden64和layers2是起点不是终点。如果 loss 下降很慢先把hidden加到 128如果训练 loss 降但验证 loss 涨把layers降到 1 并加大dropout到 0.3。batch_firstTrue别漏否则维度对不上会报一堆看不懂的错。3.3 训练集、验证集、测试集怎么切时序数据不能随机切分必须按时间顺序切。常见比例是 70/15/15 或 80/10/10。切完之后验证集用来调超参测试集只在最后评估一次。我见过有人反复在测试集上调参最后报告的数字好看但上线就崩这就是典型的过拟合测试集。n len(X) train_end int(n * 0.7) val_end int(n * 0.85) X_train, y_train X[:train_end], y[:train_end] X_val, y_val X[train_end:val_end], y[train_end:val_end] X_test, y_test X[val_end:], y[val_end:]4. 评估与调参MAPE 好看不代表模型能用4.1 选对评估指标MSE 和 RMSE 对量级敏感不同国家的数值没法横向比。MAPE平均绝对百分比误差更直观但日增为 0 的日子会导致除零。实际用的时候我一般同时看三个MAE 看绝对误差、MAPE 看相对误差、还有峰值捕捉率——预测的峰值和真实峰值差了几个时间步。def evaluate(y_true, y_pred): mae np.mean(np.abs(y_true - y_pred)) # 加一个小量避免除零 mape np.mean(np.abs((y_true - y_pred) / (y_true 1e-6))) * 100 return mae, mape # 预测并还原尺度 model.eval() with torch.no_grad(): pred_scaled model(torch.tensor(X_test, dtypetorch.float32).unsqueeze(-1)).numpy() pred scaler.inverse_transform(pred_scaled.reshape(-1, 1)).reshape(pred_scaled.shape) true scaler.inverse_transform(y_test.reshape(-1, 1)).reshape(y_test.shape) mae, mape evaluate(true, pred) print(fMAE: {mae:.2f}, MAPE: {mape:.2f}%)4.2 三个真正影响结果的参数第一个是input_len。它决定了模型能看到多长的历史。太短抓不住趋势太长引入噪声且训练变慢。我的做法是从 7 开始每次加 7看验证集 MAPE 什么时候不再下降。第二个是学习率。1e-3是 Adam 的默认值但时序预测里5e-4往往更稳。如果 loss 曲线震荡厉害先降学习率别急着改模型结构。第三个是output_len。预测步长越长误差累积越严重。如果必须预测 14 天可以考虑滚动预测每次只预测 1 天把预测值拼回输入窗口再预测下一天。这样误差不会一次性放大但推理时间线性增长。4.3 滚动预测的实现def rolling_forecast(model, init_window, steps14): model.eval() window init_window.copy() # (input_len,) preds [] for _ in range(steps): x torch.tensor(window, dtypetorch.float32).reshape(1, -1, 1) with torch.no_grad(): next_val model(x).item() # 这里模型输出单步 preds.append(next_val) window np.append(window[1:], next_val) # 滑窗前移 return np.array(preds)注意这个函数要求模型输出维度是 1和前面output_len7的模型不兼容。实际用的时候要么改模型最后一层要么用多步模型但接受误差累积。两种方案没有绝对优劣看你对峰值精度的要求。5. 避坑指南那些让我重跑一晚上的问题5.1 现象预测曲线整体平移形状对但时间错位原因滑动窗口构造时y的起始位置算错了。常见错误是y.append(series[iinput_len : iinput_lenoutput_len])里索引偏移了一位导致预测的是第 input_len1 天到 input_lenoutput_len 天而不是紧接着输入窗口的下一天。解决构造完窗口后手动打印第一组样本验证。X[0]应该是series[0:14]y[0]应该是series[14:21]。对不上就调索引。5.2 现象训练 loss 正常下降验证 loss 从第 5 个 epoch 开始涨原因过拟合。数据量小、模型参数多、训练轮数太多三者占一个就会这样。解决先加 dropout0.2 到 0.4再减层数最后考虑加 L2 正则。如果还不行说明数据量根本不够换 Prophet 或简单移动平均更实际。5.3 现象MAPE 只有 3%但画图一看预测全是直线原因日增序列里大量接近 0 的值拉低了 MAPE模型学会了预测平均值这个偷懒策略。这种情况在疫情平稳期特别常见。解决评估时过滤掉真实值小于某个阈值的样本或者改用峰值区域的加权评估。更直接的办法是看预测曲线的方差方差远小于真实方差就说明模型没学到动态。5.4 现象换一个国家数据模型完全失效原因不同国家的数值量级、报告频率、政策干预节奏差异巨大。用美国数据训练的模型直接套到小国归一化参数就不对。解决要么按国家分别训练要么在输入里加入国家相关的协变量人口、检测量。跨国家泛化是这个任务里最难的部分别指望一个模型打天下。5.5 现象GPU 利用率极低训练一晚上没跑完原因DataLoader的num_workers默认是 0数据加载在主进程串行执行。或者 batch_size 太小GPU 一直在等数据。解决num_workers设成 CPU 核数的一半左右batch_size从 32 加到 64 或 128。另外确认数据已经转成 tensor别在__getitem__里做 pandas 操作。6. 进阶技巧用残差和集成把 MAPE 再压两个点单模型跑通之后想再提升精度最划算的两个方向是残差建模和模型集成。残差建模的思路是先用一个简单模型比如 7 天移动平均拿到基础预测再用 GRU 去学真实值和基础预测之间的残差。这样模型不用从零学趋势只需要学移动平均哪里没预测准收敛更快对小数据集尤其友好。# 基础预测7 天移动平均 def moving_average_baseline(series, window7): return pd.Series(series).rolling(window).mean().shift(1).bfill().values base moving_average_baseline(series) residual series - base # 对残差构造窗口训练 GRU X_res, y_res make_windows(residual, 14, 7) # ... 训练流程同上最后预测值 base 预测残差集成则是训练多个不同初始化的 GRU预测结果取平均。别小看这个操作方差能降 20% 到 30%。如果嫌训练多个模型麻烦至少跑三个不同随机种子的版本取中位数比单模型稳得多。还有一个容易被忽略的点把星期几作为特征加进去。新冠报告有明显的周内效应周末检测少、周一集中报告这个周期性如果模型自己学需要大量样本直接作为 one-hot 特征喂进去立竿见影。# 给每个时间步加星期特征 dow pd.to_datetime(df[date]).dt.dayofweek.values dow_onehot np.eye(7)[dow] # (T, 7) # 构造窗口时同步切分 dow_onehot和 X 拼接最后说个我自己的习惯每次跑完实验不管结果好坏都把配置、随机种子、评估指标记到一个 csv 里。看起来麻烦但当你跑到第 20 组参数、完全不记得哪组用了什么学习率的时候这个习惯能救命。时序预测这行玄学成分不少但可复现的记录能把玄学变成经验。希望帮到你。本文还有配套的精品资源点击获取