Data-Science-For-Beginners 实战作业指南:用 Python 完成 COVID-19 疫情建模与论文共现分析
2026/9/13 9:33:44 网站建设 项目流程

Data-Science-For-Beginners 实战作业指南:用 Python 完成 COVID-19 疫情建模与论文共现分析

【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners

本篇技术指南基于开源课程 Data-Science-For-Beginners 第 2 部分第 07 课的课后作业 assignment.md,系统讲解如何延续课程挑战中已开始的分析代码,完成COVID-19 疫情传播建模COVID-19 科研论文文本分析两大实战任务。读完本文,你将掌握 Pandas 时间序列处理(diffrollingapply)、有效再生数 R_t 的滑动窗口估算、死亡率随时间变化的滞后分析,以及基于文本的共现矩阵构建、热力图可视化和正则表达式剂量提取等完整技能,并能直接对照仓库中的 notebook-covidspread.ipynb 与 notebook-papers.ipynb 动手实践。

作业背景与总体结构

本作业要求你在课程两大战役(Challenge)已启动的代码基础上继续深入拓展,共分两部分:

  1. COVID-19 疫情传播建模(Spread Modelling):基于约翰斯·霍普金斯大学 CSSE 提供的时间序列数据,绘制各国 R_t 曲线、研究感染/死亡/康复的相关关系、推断典型病程长度、计算随时间变化的死亡率;
  2. COVID-19 论文分析(Papers Analysis):基于 CORD-19 论文数据集的摘要文本,构建药物共现矩阵并用热力图可视化,另有弦图(Chord Diagram)与正则表达式剂量提取两个加分目标。

作业配套的完整可运行示例代码位于 notebook-covidspread.ipynb 和 notebook-papers.ipynb,本课程的整体背景可参考 07-python/README.md。

第一部分:COVID-19 疫情传播建模

1.1 数据准备:加载并聚合全球时间序列

课程笔记本 notebook-covidspread.ipynb 展示了数据的标准加载方式:可直接从网络拉取最新数据,也可使用仓库data/COVID/目录下的本地副本。仓库已提供三份本地时间序列数据:

  • data/COVID/time_series_covid19_confirmed_global.csv:各国每日累计确诊
  • data/COVID/time_series_covid19_deaths_global.csv:各国每日累计死亡
  • data/COVID/time_series_covid19_recovered_global.csv:各国每日累计康复
import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.rcParams["figure.figsize"] = (10,3) # 放大图像 base_url = "../../data/COVID/" # 从仓库本地加载;也可换成在线 URL infected = pd.read_csv(base_url + "time_series_covid19_confirmed_global.csv") deaths = pd.read_csv(base_url + "time_series_covid19_deaths_global.csv") recovered = pd.read_csv(base_url + "time_series_covid19_recovered_global.csv")

原始表每行是一个「国家/地区 + 省份」组合,日期作为列名(如1/22/208/29/21)。澳大利亚、中国等国家存在多省细分行,因此后续分析前必须按Country/Region汇总:

infected = infected.groupby('Country/Region').sum() deaths = deaths.groupby('Country/Region').sum() recovered = recovered.groupby('Country/Region').sum() # 丢弃非日期元数据列 infected.drop(columns=['Lat','Long','Province/State'], inplace=True) # deaths、recovered 同理

汇总后 DataFrame 以Country/Region为索引,即可用infected.loc['US']按国家取数。笔记本中还用[3:]切片去掉前三个非日期列,两者效果等价。

1.2 核心指标构造:每日新增与滚动平滑

疫情分析的关键是从「累计值」推导「每日新增」。累计确诊序列的一阶差分即为每日新增感染数:

df['ninfected'] = df['infected'].diff()

由于各国报告的波动很大(存在明显的周内周期波动),直接绘图难以看出趋势,因此笔记本用 7 日滚动均值做平滑:

df['ninfav'] = df['ninfected'].rolling(window=7).mean()

完成本部分作业时,可仿照上述模式,为deathsrecovered同样构造差分列ndeathsnrecovered,为后续相关性分析做准备。

1.3 计算有效再生数 R_t

课程笔记本给出了疫情传播力的核心概念:基本再生数 R_0表示一个感染者平均再感染的人数,当 R_0 > 1 时疫情倾向于扩散。而随时间变化的有效再生数 R_t可粗略估算为:取 8 天窗口,用前 4 天与后 4 天新增感染之和的比值

$$R_t=\frac{I_{t-7}+I_{t-6}+I_{t-5}+I_{t-4}}{I_{t-3}+I_{t-2}+I_{t-1}+I_t}$$

其中 I_t 为第 t 天的新增感染数。Pandas 的rolling(8)配合apply可直接实现:

df['Rt'] = df['ninfected'].rolling(8).apply(lambda x: x[4:].sum()/x[:4].sum())

绘图时需要注意数据质量问题:窗口宽度不足会得到NaN,分母为 0 会产生inf。笔记本给出的清理方案是先用replaceinf转为NaN,再用fillna向前填充,随后限定纵轴并叠加参考线:

ax = df[df.index < "2020-05-01"]['Rt'].replace(np.inf, np.nan).fillna(method='pad').plot(figsize=(10,3)) ax.set_ylim([0, 6]) ax.axhline(1, linestyle='--', color='red') # R_t = 1 为疫情扩散与否的分界线

1.4 作业任务一:绘制 5~6 个国家的 R_t 对比图

将 1.1~1.3 的流水线封装成函数(输入国家名,输出以日期为索引、含Rt列的 DataFrame),再对 5~6 个不同国家循环调用。既可以把多条Rt曲线绘制在同一张图内直接对比,也可以使用plt.subplots生成并排子图:

countries = ['US','India','Brazil','UK','Russia','Italy'] fig, axes = plt.subplots(2, 3, figsize=(15, 6), sharey=True) for ax, c in zip(axes.flat, countries): dfc = compute_rt(c) # 自定义函数:加载数据→groupby→diff→rolling(8).apply ax.plot(dfc['Rt']) ax.axhline(1, linestyle='--', color='red', linewidth=0.8) ax.set_title(c) plt.tight_layout(); plt.show()

注意:不同国家疫情起始时间不同,直接对齐日期轴可能使早期噪声(分母为 0 的inf)淹没真实趋势,建议按国家裁剪疫情爆发前的时间段后再对比,这也是作业要求「可能需要在多个国家间查看以得出结论」的原因之一。

1.5 作业任务二:死亡、康复与感染的相关性

在 1.2 构造的ndeathsnrecoveredninfected基础上,可通过三种途径分析:

  • 直接对比曲线:将三个新增序列绘制在同一坐标轴,观察峰值的先后次序与幅度关系;
  • 相关性系数:使用df[['ninfected','ndeaths','nrecovered']].corr()计算皮尔逊相关系数矩阵,量化两两关系;
  • 分国家对比:同一国家内死亡与感染的相关强度会随医疗资源、检测能力不同而差异明显,跨国家比较更能说明问题。

需要特别注意的是:康复与死亡相对于感染存在时间滞后(患者从感染到康复/死亡通常要经过若干天),因此「当天感染 vs 当天死亡」的直接相关会低估真实关联。这就自然引出任务三。

1.6 作业任务三:推断典型病程持续时间

作业要求通过视觉上关联感染率与死亡率曲线并寻找异常来推断典型病程长度。核心思路是:对ndeaths序列整体平移若干天(即引入滞后),寻找使死亡曲线与感染曲线对齐最佳的滞后天数,该天数即可作为病程长度的粗略估计。

for lag in range(0, 30, 2): shifted = df['ndeaths'].shift(lag) # 将死亡序列向后平移 lag 天 corr = df['ninfected'].corr(shifted) print(f"lag={lag}天, 相关系数={corr:.3f}")

绘图时可将df['ndeaths'].shift(lag)df['ninfected'](或各自的 7 日平滑值)叠加,直观观察哪一滞后下两条曲线峰值重合。同时应排查异常:例如某些国家报告口径突变、死亡统计延迟等会造成曲线出现离群段,需要跨多国数据交叉验证后再下结论。

1.7 作业任务四:计算死亡率及其随时间的变化

最基本的形式是累计死亡率deaths / infected。但作业特别强调:在进行计算之前,可能需要把某一时间序列平移数天。这是因为感染到死亡之间存在病程滞后,直接用同一天的累计死亡除以累计确诊,会因分母「还没有走完病程」而低估真实致死风险。

实现建议:

# 用滞后 N 天的确诊作分母,抵消感染→死亡的平均延迟 N = 14 df['fatality_rate_lag'] = df['deaths'] / df['infected'].shift(N) # 或使用新增值口径的时变死亡率 df['daily_fatality'] = df['ndeaths'] / df['ninfected'].shift(N)

绘图观察死亡率随时间的演化趋势,通常疫情初期检测不足、分母偏小,会导致死亡率虚高,随后逐步回落并趋于稳定,这一模式本身也值得在报告中解释。笔记本还展示了「新增病例的日差」(即二阶差分df['ninfected'].diff())作为疫情加速/减速的指示指标,可用其辅助判断死亡率变化与疫情阶段的关系。

第二部分:COVID-19 论文分析

2.1 数据集说明

本部分使用的CORD-19 论文数据集(截至写作时含 7000+ 篇 COVID 相关论文)并未随仓库附带。作业要求你自行下载其metadata.csv元数据文件(包含论文标题、摘要abstract与发布时间publish_time等字段)。下载后,在 notebook-papers.ipynb 中通过pd.read_csv载入即可。若不想注册下载全部全文,也可只使用元数据文件。

2.2 基于关键词计数的药物/诊断列构造

笔记本采用最朴素的做法:手工维护「可能用于治疗 COVID 的药物」与「诊断」两个关键词列表,然后在每篇论文的摘要中统计各词出现次数,生成新的布尔/计数列:

medications = ['hydroxychloroquine','chloroquine','tocilizumab','remdesivir', 'azithromycin','lopinavir','ritonavir','dexamethasone', 'heparin','favipiravir','methylprednisolone'] diagnosis = ['covid','sars','pneumonia','infection','diabetes','coronavirus','death'] for m in medications: df[m] = df['abstract'].apply(lambda x: str(x).lower().count(' ' + m)) for m in diagnosis: df[m] = df['abstract'].apply(lambda x: str(x).lower().count(' ' + m))

这里有两个重要的工程细节(笔记本以警示形式强调):

  • 关键词前必须加空格' ' + m):否则chloroquine会被错误匹配进hydroxychloroquine的子串,导致计数失真;
  • 必须用str(x)强制转换:摘要列可能含NaN,直接调用.lower()会抛异常——尝试删掉str()观察报错即可验证。

随后可以取出仅含药物计数的子帧dfm = df[medications],累加各列得到累计出现次数,从而识别「最常被研究的药物」。

2.3 作业任务一:构建药物共现矩阵

共现矩阵(Co-occurrence Matrix)是一个二维矩阵,第 (i, j) 个元素表示同一篇摘要中同时出现第 i 个药物与第 j 个药物的论文篇数。笔记本为「药物×诊断」矩阵给出的代码骨架如下,作业要求将其改造为「药物×药物」矩阵:

m = np.zeros((len(medications), len(medications))) for a in df['abstract']: present = [str(a).lower().count(' ' + me) > 0 for me in medications] for i in range(len(medications)): for j in range(len(medications)): if present[i] and present[j]: m[i, j] += 1

理解该代码的关键:

  • 外层循环遍历所有摘要;
  • 内层先统计当前摘要中出现了哪些药物(布尔向量present);
  • 再对出现药物两两组合在矩阵对应位置累加 1。

改造为「药物×药物」后,矩阵对角线的意义是「药物自身出现的论文数」(通常大于非对角线),非对角线元素越大,说明两个药越常被联合研究——例如chloroquinehydroxychloroquinelopinavirritonavir(常联用)通常会有较高的共现值。

2.4 作业任务二:用热力图可视化共现矩阵

构建完矩阵后,作业要求用**热力图(Heatmap)**呈现。笔记本可视化「药物×诊断」矩阵的参考做法:

import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(10, 8)) im = ax.imshow(m) # m 为共现矩阵(numpy 数组) ax.set_yticks(range(len(medications))) ax.set_yticklabels(medications) ax.set_xticks(range(len(medications))) ax.set_xticklabels(medications, rotation=90) plt.colorbar(im) plt.tight_layout(); plt.show()

关键点:medications列表的顺序必须与矩阵的行/列顺序严格一致(矩阵在构造时即按该列表枚举),否则标签与数据会错位。可结合dfm.sum().sort_values(ascending=False)先对药物按总频次排序,再同步重排矩阵行列,使高共现的药物聚在热力图一角,更易阅读。

2.5 加分目标一:弦图(Chord Diagram)可视化药物共现

作业的进阶挑战是使用弦图呈现药物共现关系:弦图在圆周上排列各药物节点,节点间的弦越粗表示共现频次越高。笔记本身实现了通用的sankey函数(接收源类别列表、目标类别列表与共现矩阵,并支持阈值参数以省略过弱的连接),同一套「矩阵驱动可视化」的思路同样适用于弦图。参考实现时注意:

  • 将共现矩阵转换为「节点 + 连边权重」的边列表(对每一对药物输出源、目标、权重);
  • 可设置阈值只保留权重较大的连边,避免图形过于复杂(与 sankey 函数的 threshold 参数思想一致);
  • 若矩阵对称,通常只绘制上三角以避免重复弦。

2.6 加分目标二:用正则表达式提取药物剂量

第二个加分挑战是提取不同药物的剂量,例如从句子take 400mg of chloroquine daily中提取400mg。作业给出的核心提示是:考虑出现在药物名称附近文本位置的数值。结合笔记本的代码模式,可行方案如下:

import re def extract_dosage(abstract, medication): # 匹配药物名附近(前后各 80 字符内)的“数字+单位”模式,如 400mg、5 mg、1000 mg/day pattern = r'([\d.,]+\s*(?:mg|g|mcg|µg|iu|ml))\b' text = str(abstract).lower() # 先定位药物出现位置,再在其邻域内搜索剂量 results = [] for match in re.finditer(medication, text): start = max(0, match.start() - 80) end = min(len(text), match.end() + 80) results += re.findall(pattern, text[start:end]) return results dosage_rows = [] for _, row in df.iterrows(): for me in medications: for d in extract_dosage(row['abstract'], me): dosage_rows.append({'medication': me, 'dosage': d}) dosage_df = pd.DataFrame(dosage_rows)

该方案的要点:

  • 正则模式([\d.,]+\s*(?:mg|g|mcg|µg|iu|ml))匹配「数值 + 单位」组合,\b防止mg误匹配mgt之类的子串;
  • 邻域窗口(示例取前后 80 字符)体现「数值需在药物名称的附近文本中出现」这一要求,避免把整篇摘要中无关的数字剂量与药物错误关联;
  • 最终用pd.DataFrame汇总得到「药物 × 剂量」表,可进一步按药物分组、统计出现最多的剂量值。

评价标准(Rubric)

作业官方评价标准如下,完成时可对照自查:

优秀(Exemplary)合格(Adequate)待改进(Needs Improvement)
所有任务均已完成、图形化展示并加以解释,且至少完成两个加分目标中的一个完成超过 5 项任务,未尝试加分目标,或结果不清晰完成任务少于 5 项(但多于 3 项),可视化未能帮助说明要点

环境与运行建议

  • 运行两份笔记本需要 Python 环境及 Pandas、NumPy、Matplotlib 等库;文本分析部分还建议准备正则相关(标准库re)及可选的chord绘图库;
  • 疫情时间序列部分可直接使用仓库 data/COVID/ 下的本地数据,无需联网;论文部分需自行获取 CORD-19 的metadata.csv
  • 建议按「数据加载 → 指标构造 → 绘图 → 结论解释」的顺序逐任务推进,并将每个任务的代码、输出图与文字结论对应记录,以满足评价标准中「图形化展示并解释」的要求。

通过完成本作业,你将把课程中 Series/DataFrame、diffrollinggroupbyapply、文本关键词计数与矩阵可视化等知识串联成一条完整的数据科学实践链路,具备独立完成「时间序列流行病学分析」与「科研文本共现挖掘」两类典型任务的能力。

【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询