基于 Python 与 Pandas 的 COVID-19 数据实战作业全解析(Data-Science-For-Beginners 第 7 课)
【免费下载链接】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-Working-With-Data/07-python 配套的 assignment.md 展开,完整拆解"COVID-19 传播建模"与"COVID-19 论文分析"两大作业的全部任务与两个扩展目标,并结合仓库内两个挑战笔记本的源码给出可直接运行的实现思路、关键代码与评分要点。读完本文,你将掌握用 Pandas/NumPy 处理时序疫情数据、构建药物共现矩阵、用热图与弦图可视化、以及用正则表达式从非结构化文本中抽取剂量信息的完整实战方案。
作业背景:从课程挑战代码继续扩展
本作业是 07-python(Python 与 Pandas 库) 一课的课后任务。课程通过两个挑战笔记本演示了如何处理结构化数据(表格型疫情时间序列)与非结构化数据(科学论文摘要文本):
- notebook-covidspread.ipynb:建模 COVID-19 的疫情传播,数据来自约翰斯·霍普金斯大学 CSSE,仓库在 data/COVID/ 下提供本地副本(
time_series_covid19_confirmed_global.csv、time_series_covid19_deaths_global.csv、time_series_covid19_recovered_global.csv,另有国家元数据 data/UID_ISO_FIPS_LookUp_Table.csv)。 - notebook-papers.ipynb:分析 CORD-19 科研论文数据集,从摘要中统计药物与诊断的出现频次、构建共现矩阵并可视化。
作业要求"扩展挑战中开始编写的代码",即:先阅读并运行两个笔记本,理解已有代码,然后在其基础上完成更深层的数据分析任务。因此,动手前建议先完整阅读这两个笔记本。
第一部分:COVID-19 传播建模
这部分基于 notebook-covidspread.ipynb 的代码框架,共 4 个任务,围绕疫情时间序列的对比、相关性与病死率展开。
0. 前置代码:数据加载与国家子集构造
笔记本中的核心前置代码值得先复现。数据加载既可从互联网读取,也可直接使用仓库本地副本:
base_url = "../../data/COVID/" # 本地副本;也可指向网络源 infected = pd.read_csv(base_url + "time_series_covid19_confirmed_global.csv") recovered = pd.read_csv(base_url + "time_series_covid19_recovered_global.csv") deaths = pd.read_csv(base_url + "time_series_covid19_deaths_global.csv")表格的行是国家/省份,列是日期(从1/22/20开始)。笔记本中定义了一个mkframe函数,把某个国家的三组数据合并成以日期为索引的 DataFrame,并统一转为时间戳索引,这是后续所有任务的基础:
def mkframe(country): df = pd.DataFrame({ 'infected' : infected.loc[country], 'recovered': recovered.loc[country], 'deaths' : deaths.loc[country]}) df.index = pd.to_datetime(df.index) return df df = mkframe('US') df此外,笔记本用df['infected'].diff()计算每日新增感染数,用于观察传播速度:
df['ninfected'] = df['infected'].diff()这两个片段(mkframe与diff)是实现本部分全部 4 个任务的基础工具。
任务 1:绘制多国 R 曲线对比
R(基本再生数/有效再生数)是衡量疫情传播速度的核心指标,可以理解为"每个感染者平均传染的人数"。本任务要求将 5~6 个不同国家的 R 曲线画在同一个图(或并排多个子图)上做对比。
实现思路(基于笔记本的ninfected计算):
countries = ['US', 'Italy', 'Brazil', 'India', 'Russia', 'UK'] for c in countries: d = mkframe(c) d['ninfected'] = d['infected'].diff() # 用 7 日滑动窗口平滑每日新增的剧烈波动 d['smooth'] = d['ninfected'].rolling(7).mean() # 相邻两期新增的比值即"传播因子",近似刻画 R 的变化 d['R'] = d['smooth'] / d['smooth'].shift(1) plt.plot(d.index, d['R'], label=c) plt.legend() plt.show()要点:
- 新增感染序列噪声很大,先用
rolling(7).mean()做 7 日滑动平均平滑,再取相邻比值,曲线更稳定、可比性更强; - 单图对比适合展示趋势差异,也可以使用
plt.subplots()为每个国家单独画子图并排展示; - 日志/线性坐标、图例与轴标签等细节会直接影响"比较"任务的可读性。
从笔记本源码看,其已经完成单国数据的组装与新增感染计算(notebook-covidspread.ipynb 中mkframe与diff单元格),本任务正是在此基础上扩展为多国循环与 R 的推算。
任务 2:死亡/康复与感染病例数的相关性
本任务要求分析死亡人数、康复人数与感染病例数之间的相关关系。
推荐的实现路径:
import numpy as np countries = ['US', 'Italy', 'Brazil'] for c in countries: d = mkframe(c) print(c, { 'corr(deaths, infected)' : np.corrcoef(d['deaths'], d['infected'])[0, 1], 'corr(recovered, infected)': np.corrcoef(d['recovered'], d['infected'])[0, 1], })分析要点:
- 由于感染、死亡、康复三者都是累积量,随时间单调上升,直接算相关系数往往都接近 1,意义有限;
- 更有价值的做法是考察每日新增(
diff()后的序列)之间、或滞后若干天后的相关性; - 可配合散点图(
plt.scatter(d['infected'], d['deaths']))直观观察线性关系与异常点,并用热图展示多个国家、多组指标间的相关矩阵。
任务 3:判断典型病程持续时间
本任务要求通过视觉上关联感染率与死亡率曲线、寻找异常点,推断一种典型疾病的持续时长(从感染到死亡的天数),并可能需要对比多个国家才能得出稳健结论。
核心方法:把死亡曲线按天数向后平移(shift),找到与感染曲线(或新增感染曲线)对齐最好的偏移量:
d = mkframe('US') d['ninfected'] = d['infected'].diff() d['ndeaths'] = d['deaths'].diff() best_shift, best_corr = 0, -1 for shift in range(1, 40): corr = np.corrcoef(d['ninfected'].iloc[shift:], d['ndeaths'].iloc[:-shift] if shift else d['ndeaths'])[0, 1] if corr > best_corr: best_corr, best_shift = corr, shift print(f"最佳对齐偏移量约 {best_shift} 天,相关系数 {best_corr:.3f}")注意事项:
- 平移方向:感染先于死亡出现,因此通常将死亡序列向过去平移(即用
shift(-k)或截断比较); - 不同国家的报告延迟、检测能力、人口结构会导致结果差异,所以题目明确提示"可能需要查看不同国家的数据";
- 平滑处理(7 日滑动平均)能显著提升相关性估计的稳定性;
- 曲线形态上的异常点(如某一时段的死亡突增)往往对应医疗资源挤兑或统计口径变化,可作为深入讨论的素材。
任务 4:病死率及其随时间的变化
本任务要求计算病死率(CFR,fatality rate)并观察其随时间的变化,且提示"可能需要考虑疾病持续天数,先对某个时间序列进行偏移再计算"。
基础公式为deaths / infected,实现如下:
d = mkframe('US') d['cfr_naive'] = d['deaths'] / d['infected'] d['cfr_naive'].plot()但朴素病死率存在系统性偏差:感染发生到死亡结局之间存在数天滞后,直接用当日死亡除以当日累计感染,会在疫情上升期低估病死率。改进方法正是作业强调的"时间序列偏移":
shift_days = best_shift # 复用任务 3 求得的病程天数 d['cfr_shifted'] = d['deaths'] / d['infected'].shift(shift_days)要点:
- 用"向前偏移的累计感染"作为分母,使分母与分子的时间口径一致,得到的病死率更接近真实水平;
- 观察病死率曲线的整体趋势与突变点(如医疗系统承压期、治疗方案变化期),并在最终报告中给出合理解释;
- 绘图时注意早期数据噪声大(分子分母都很小),可设置起始时间阈值或做滑动平均。
第二部分:COVID-19 论文分析
这部分基于 notebook-papers.ipynb,针对 CORD-19 论文数据集(含metadata.csv与摘要字段)做文本挖掘,包含 2 个必做任务与 2 个扩展目标。
注意:仓库未随附论文数据集副本,需要自行按课程说明获取
metadata.csv;笔记本中通过df['abstract']列访问摘要文本。
任务 1:构建药物共现矩阵
本任务要求为不同药物构建共现矩阵(co-occurrence matrix),找出经常在同一篇摘要中同时被提及的药物组合。作业提示可直接"修改用于构建药物与诊断共现矩阵的代码"。
笔记本中已经给出了"药物 × 诊断"共现矩阵的完整实现(notebook-papers.ipynb),其核心是逐篇摘要遍历 + 命中计数:
m = np.zeros((len(medications), len(diagnosis))) for a in df['abstract']: x = str(a).lower() for i, d in enumerate(diagnosis): if ' ' + d in x: for j, me in enumerate(medications): if ' ' + me in x: m[j, i] += 1把该代码改造为"药物 × 药物"共现矩阵非常直接——外层遍历换为药物列表、内层只统计同篇摘要中成对药物是否同时出现:
n = len(medications) m = np.zeros((n, n)) for a in df['abstract']: x = str(a).lower() hit = [(' ' + me) in x for me in medications] for i in range(n): if not hit[i]: continue for j in range(i + 1, n): # 只需上三角,避免重复计数 if hit[j]: m[i, j] += 1 m[j, i] += 1 # 对称填充,方便热图显示值得沿用的笔记本技巧(源码中有明确注释说明):
- 单词前加空格再匹配:统计
' '+m而不是m,否则chloroquine会被错误计入hydroxychloroquine内部; - 先转小写
str(x).lower(),避免大小写不一致漏匹配; - 强制
str()转换:摘要列存在缺失值时,直接apply会报错,str(x)可消除该问题(笔记本建议读者尝试去掉str观察报错)。
任务 2:用热图可视化共现矩阵
在得到对称的共现矩阵后,用seaborn.heatmap或matplotlib的imshow渲染热图,可直接看出哪些药物组合共现频繁(越亮/数值越大代表越常一起出现):
import seaborn as sns plt.figure(figsize=(10, 8)) sns.heatmap(m, xticklabels=medications, yticklabels=medications, annot=True, fmt='.0f', cmap='YlOrRd') plt.title('Medication Co-occurrence Matrix (same abstract)') plt.tight_layout() plt.show()解读方向:
- 对角线元素是各药物自身出现总次数,可同时反映各药物的总体热度;
- 关注非对角线上的高值组合,例如同属一套治疗方案(如
lopinavir+ritonavir常联合给药)的药物会呈现明显的高共现; - 可结合
dfm = df[medications]按列累加(笔记本中有dfm.sum()的用法)得到最热门药物列表,作为解读共现矩阵的补充上下文。
扩展目标 1:用弦图可视化药物共现
弦图(Chord Diagram)将共现关系表达为圆周节点之间的弧带,带宽代表共现强度,适合展示多对多的关联结构。作业建议使用 chord 库(pip install chord)。
from chord import Chord # 输入为共现矩阵(DataFrame 形式,行列均为药物名) df_m = pd.DataFrame(m, index=medications, columns=medications) Chord(df_m.values, df_m.index.tolist()).to_html('medication_chord.html')实现提示:
- chord 库依赖 holoviews/bokeh 等渲染后端,首次使用前按官方文档安装依赖;
- 若矩阵过大导致弦图拥挤,可先过滤低共现阈值(只保留共现次数超过阈值的药物对),这与笔记本中 sankey 函数允许设置 threshold 的思路一致;
- 弦图适合用于报告中的"关系总览",热图则更适合精确读取数值,两者可互补呈现。
扩展目标 2:用正则表达式提取药物剂量
本任务要求使用正则表达式从摘要文本中提取不同药物的剂量(例如从take 400mg of chloroquine daily中提取400mg),并构建一个展示"不同药物 → 不同剂量"的 DataFrame。作业特别提示:要关注文本中靠近药物名称的数值。
实现方案:
import re def extract_dosages(abstract, drug): """在摘要中药物名称附近查找剂量数值(如 400mg / 5 mg / 1000 mg/kg)""" text = str(abstract).lower() pattern = r'(\d+(?:\.\d+)?\s*(?:mg|mcg|g|kg|µg|ml|l|iu|units|mg/kg)\b)' hits = [] # 在药物名称前后各 40 个字符的窗口内查找剂量 for match in re.finditer(drug, text): start = max(0, match.start() - 40) end = min(len(text), match.end() + 40) window = text[start:end] hits.extend(re.findall(pattern, window)) return hits drug = 'chloroquine' dosages = [] for a in df['abstract']: dosages.extend(extract_dosages(a, drug)) pd.DataFrame({drug: dosages})构建"药物 × 剂量"DataFrame 的完整流程:
records = {} for med in medications: all_dosages = [] for a in df['abstract']: all_dosages.extend(extract_dosages(a, med)) records[med] = pd.Series(all_dosages).value_counts() df_dosages = pd.DataFrame(records).fillna(0).astype(int) df_dosages.head(10)要点与陷阱:
- 窗口策略:剂量数值必须"靠近"药物名才有意义,作业特意提示这一点。实现上以药物名匹配位置为中心取一个文本窗口(如前后 40 字符),再在窗口内查找剂量模式,可避免把摘要中无关的数值(如病例数、样本量)误判为剂量;
- 剂量单位模式:常见单位包括
mg、g、kg、mcg/µg、ml、iu、mg/kg等,正则中应显式列出并用\b防止匹配400mgx之类的超集; - 大小写与空白:文本先
lower(),数值与单位之间允许 0 或多个空格; - 去重与频次:同一摘要内重复剂量可先
set()去重再统计;最终用value_counts()统计各药物各剂量档的出现频次,方便回答"某药最常用剂量是多少"。
评分标准与自检清单
作业给出三级评分表(Rubric),用于自我评估完成质量:
| 等级 | 要求 |
|---|---|
| 优秀(Exemplary) | 所有任务均完成,有图形化展示且解释清楚,并至少完成两个扩展目标之一 |
| 合格(Adequate) | 完成超过 5 个任务,未尝试扩展目标,或结果不够清晰 |
| 需改进(Needs Improvement) | 完成少于 5 个(但超过 3 个)任务,且可视化未能有效说明问题 |
对照自检:
- 第一部分 4 个任务是否都给出了"图形 + 文字解释",而非仅贴代码;
- 第二部分 2 个必做任务(共现矩阵 + 热图)是否完成,热图是否能直接读出"哪对药物常共现";
- 是否至少尝试了弦图或剂量提取中的一项扩展目标;
- 结论是否有数据与图表支撑(例如"典型病程约 X 天"需给出对齐偏移量与相关系数,"药物 A+B 常联合"需给出共现次数)。
相关仓库资源索引
- 作业原文:2-Working-With-Data/07-python/assignment.md
- 课程讲义:2-Working-With-Data/07-python/README.md
- 挑战笔记本 1(疫情传播):notebook-covidspread.ipynb
- 挑战笔记本 2(论文分析):notebook-papers.ipynb
- 疫情数据本地副本:data/COVID/time_series_covid19_confirmed_global.csv、data/COVID/time_series_covid19_deaths_global.csv、data/COVID/time_series_covid19_recovered_global.csv
- 国家元数据:data/UID_ISO_FIPS_LookUp_Table.csv
通过本文的拆解与代码示例,你可以直接在两个笔记本的基础上逐项完成作业:第一部分掌握"多国对比、相关性分析、时间序列平移对齐、病死率计算"四类时序数据分析技能;第二部分掌握"词项计数、共现矩阵构建、热图/弦图可视化、正则剂量抽取"四类文本挖掘技能——这套组合正是数据科学中"结构化数据 + 非结构化数据"处理的典型工作流。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考