Data-Science-For-Beginners 第 7 课课后作业全解: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-Working-With-Data / 07-python:Python 与 Pandas 数据处理」一课的配套课后作业(translations/en/2-Working-With-Data/07-python/assignment.md)展开,目标是在课堂两个挑战 Notebook 的基础上,完成一次更深入的疫情数据研究。作业分为两大部分:一是基于约翰斯·霍普金斯大学 CSSE 的全球疫情时序数据,用 Pandas 完成多国再生数对比、感染/死亡/康复相关性分析、典型病程推断与致死率计算;二是基于 CORD-19 论文摘要,构建药物共现矩阵、热力图,并可选地完成弦图可视化与正则提取药物剂量两项挑战任务。读完本文,你将掌握groupby、rolling、diff、apply、fillna等核心 Pandas 技巧在真实时间序列与文本数据上的完整应用链路,并得到一份可直接对照执行的任务清单、代码示例与评分标准自检表。
作业背景:课堂挑战的进阶延伸
本作业要求你对课堂两个挑战(Challenge)的已有代码进行扩充,共两部分:
- Part 1 COVID-19 疫情传播建模:基于 notebook-covidspread.ipynb 中已完成的加载、预处理、
R_t估计等代码,完成 4 项进阶分析任务; - Part 2 COVID-19 论文分析:基于 notebook-papers.ipynb 中已完成的药物/诊断关键词计数、共现矩阵与 Sankey 图代码,完成 2 项必做任务与 2 项可选挑战任务。
作业所需数据在仓库中有离线副本可供直接使用:
- 疫情时序数据位于 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,含
Population字段,用于计算感染人口占比; - CORD-19 论文的
metadata.csv不随仓库提供,需按课堂 Notebook 说明自行下载(后文详述)。
Part 1:COVID-19 疫情传播建模
本部分承接课堂挑战 1。建议先完整执行一遍 notebook-covidspread.ipynb,复用其生成的infected、recovered、deaths、countries、pop等中间变量,再逐项完成下列 4 个任务。
任务 1.1:绘制多国 R 图对比
任务要求:为 5~6 个不同国家绘制再生数R曲线,可在同一张图上叠加对比,也可并排绘制多张子图。
课堂前置知识:再生数衡量疾病传染性。课堂 Notebook 用 8 天窗口估计随时间变化的R_t,核心代码为:
df['Rt'] = df['ninfected'].rolling(8).apply(lambda x: x[4:].sum()/x[:4].sum()) df['Rt'].plot() plt.show()其含义是:以第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天新增感染人数。当R_t > 1时疫情趋于扩散,当R_t < 1时疫情趋于收敛。课堂还演示了缺口处理——滚动窗口起始段不足 8 天会产生NaN,除数为 0 会产生inf,需用replace与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') plt.show()完成任务的建议步骤:
- 数据准备:复用课堂的
mkframe(country)函数,为每个国家生成按日期索引的 DataFrame(含infected/recovered/deaths三列):
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- 批量计算 R_t:对每个国家依次计算新增感染(
diff)、7 天滚动均值平滑(消除课堂中观察到的周报波动)、8 天窗口R_t,并统一清洗inf/NaN:
rts = pd.DataFrame() for c in ['US','India','Brazil','Germany','Japan','China']: d = mkframe(c) d['ninfected'] = d['infected'].diff() d['ninfav'] = d['ninfected'].rolling(window=7).mean() # 平滑曲线 d['Rt'] = d['ninfected'].rolling(8).apply(lambda x: x[4:].sum()/x[:4].sum()) d['Rt'] = d['Rt'].replace(np.inf, np.nan).fillna(method='pad') rts[c] = d['Rt']- 绘制对比图:单图叠加用
rts.plot();并排子图用plt.subplots,并可用ax.axhline(1, ...)标出临界线、统一 y 轴范围:
fig, ax = plt.subplots(1, 2, figsize=(14, 4)) rts.plot(ax=ax[0], title='Rt by country (overlay)') rts[rts.index < "2020-05-01"].plot(ax=ax[1], title='Early stage, Rt<6', subplots=True, figsize=(14, 8), ylim=(0, 6)) plt.show()- 解读与写作:说明哪些国家
R_t长期高于 1(扩散快)、哪些较早回落至 1 以下、是否出现二次暴发(R_t再度抬头)。
任务 1.2:分析死亡、康复与感染的相关性
任务要求:分析死亡数、康复数与感染数的相关关系。
课堂的mkframe已把三者组装进同一 DataFrame,可直接利用 Pandas 的corr()计算皮尔逊相关系数:
df = mkframe('US') df[['infected','recovered','deaths']].corr()进阶建议——考虑滞后:死亡与康复相对感染存在时间滞后,直接算相关系数会低估真实关联。可对死亡序列做 k 天平移后再与感染序列求相关,找到使相关系数最大的平移天数:
best_k, best_corr = 0, -1 for k in range(0, 31): corr = df['infected'].corr(df['deaths'].shift(k)) if corr > best_corr: best_k, best_corr = k, corr print(f"最大相关系数 {best_corr:.3f} 出现在死亡滞后 {best_k} 天")这一步的结果可直接服务于任务 1.3 的病程估计与 1.4 的致死率修正。可视化方面,可并排绘制三条曲线(df.plot()),或用散点图观察infected与deaths是否近似线性。写作时应解释相关系数含义,并说明死亡/康复曲线整体跟随感染曲线但存在时间滞后的原因。
任务 1.3:通过感染率与死亡率的对应推断典型病程
任务要求:通过目视比对感染率与死亡率曲线的时间对应关系,判断典型病程持续多久,并识别异常。可能需要结合多个国家的数据。
方法建议:
- 绘制对比曲线:对同一国家在同一坐标下绘制(建议平滑后的)新增感染
ninfav与新增死亡曲线,观察死亡峰值相对感染峰值的延迟天数。 - 平移对齐:用
df['deaths'].shift(k)将死亡序列平移 k 天后再叠加,找到视觉上对齐最好的 k,即为典型病程的估计值(感染后大约多少天死亡)。 - 多国交叉验证:不同国家报告口径不同,题目明确要求「examine data from different countries」——对多个国家重复上述步骤,检验病程估计的一致性。
- 识别异常:例如新增感染骤降(周末/假期不更新导致的周波动)、死亡数负值或异常尖峰(数据修订、批量补报)等。课堂 Notebook 中 7 天滚动均值
ninfav正是为消除周波动而引入的,比对前务必先平滑。
写作要点:给出病程天数估计范围及其依据(哪两个特征点对齐),并说明各国出现了怎样的异常、如何用rolling/fillna/replace排查处理。
任务 1.4:计算致死率并观察其随时间变化
任务要求:计算致死率(fatality rate)并观察其随时间的变化。提示:计算时应考虑病程天数,将某条时间序列平移后再计算。
朴素致死率(直接用当日累计死亡除以累计确诊):
df['fatality'] = df['deaths'] / df['infected'] df['fatality'].plot()注意偏差:朴素致死率在疫情早期被系统性低估——当日确诊者尚未走完病程,分母被高估;后期又会相对「虚高」。这正是题目提示需要平移的原因。
考虑病程的修正致死率:将死亡序列按任务 1.3 估计的病程天数 k 平移后再除:
k = best_k # 取 1.3 中估计的病程天数 df['fatality_lag'] = df['deaths'].shift(-k) / df['infected'] # 或等价地:df['fatality_lag'] = df['deaths'] / df['infected'].shift(k) df['fatality_lag'].plot()平移方向需在文中说明:shift(-k)使用未来数据会造成数据泄漏,练习场景下两者都可尝试并对比结果差异。写作要点:说明朴素致死率与修正致死率的差异及原因,给出采用的 k 值与依据,并结合多国数据说明致死率随时间及地域变化的特征。
Part 2:COVID-19 论文分析
本部分承接课堂挑战 2。课堂 Notebook(notebook-papers.ipynb)已完成:关键词计数、药物出现频次、按月趋势、药物-诊断共现矩阵、热力图与 Sankey 图。作业在此基础上要求完成 2 项必做任务与 2 项挑战任务。
数据说明:CORD-19 论文metadata.csv不随仓库提供,需按课堂 Notebook 说明自行下载(Kaggle 下载可能需要注册)。课堂代码用pd.read_csv加载后,df['abstract']为论文摘要列,df['publish_time']为发表日期列。
任务 2.1:构建药物-药物共现矩阵并识别高频共现组合
任务要求:构建「药物 × 药物」共现矩阵,找出常在同一篇摘要中同时出现的药物组合;可改编课堂中「药物-诊断共现矩阵」的代码。
课堂可复用代码(药物-诊断共现矩阵):
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关键技巧(必须沿用):匹配时在词前加空格(
' '+m),避免chloroquine被hydroxychloroquine子串误命中;同时用str()强制转换,避免NaN摘要报错。
改编为药物-药物共现矩阵:把内层循环从「遍历诊断」改为「遍历药物」:
n = len(medications) cooc = np.zeros((n, n)) for a in df['abstract']: x = str(a).lower() present = [i for i, me in enumerate(medications) if ' '+me in x] for i in present: for j in present: if i != j: cooc[i, j] += 1从源码结构看,课堂 Notebook 定义的medications列表包含 11 种药物:hydroxychloroquine、chloroquine、tocilizumab、remdesivir、azithromycin、lopinavir、ritonavir、dexamethasone、heparin、favipiravir、methylprednisolone,因此共现矩阵为 11×11。
识别高频共现组合:转为 DataFrame 后,取上三角(排除对角线与重复对)再stack+sort_values:
cm = pd.DataFrame(cooc, index=medications, columns=medications) pairs = cm.where(np.triu(np.ones(cm.shape), k=1).astype(bool)).stack() pairs.sort_values(ascending=False).head(10)写作要点:说明哪些药物对常被联合研究(如抗病毒药与免疫调节剂的组合),并解释共现信息的价值(提示潜在联合用药方案、研究热点)。
任务 2.2:用热力图可视化共现矩阵
任务要求:用热力图可视化任务 2.1 的共现矩阵。
课堂可复用代码(药物-诊断热力图):
plt.imshow(m,interpolation='nearest',cmap='hot') ax = plt.gca() ax.set_yticks(range(len(medications))) ax.set_yticklabels(medications) ax.set_xticks(range(len(diagnosis))) ax.set_xticklabels(diagnosis,rotation=90) plt.show()改编为药物-药物热力图:将m换成cooc,x、y 轴标签都使用medications:
plt.imshow(cooc, interpolation='nearest', cmap='hot') ax = plt.gca() ax.set_yticks(range(len(medications))) ax.set_yticklabels(medications) ax.set_xticks(range(len(medications))) ax.set_xticklabels(medications, rotation=90) plt.show()说明:
matplotlib的imshow不内置数值标签与颜色条。若需在格子上标注共现次数,可考虑 seaborn 的heatmap(sns.heatmap(cm, annot=True, cmap='YlOrRd'));课堂 Notebook 的imshow方案零额外依赖,同样可行。
写作要点:结合热力图指出最亮的「块」对应哪些药物组合,与任务 2.1 的排序结果互相印证。
挑战任务 2.3(stretch goal):用弦图可视化药物共现
任务要求(可选加分项):用弦图(chord diagram)可视化药物共现关系,可使用
chord库辅助绘制。
实现思路:
- 安装依赖:
pip install chord(底层依赖plotly,可复用课堂已有的 Plotly 环境)。 - 将共现矩阵转为 chord 库要求的数据格式并绘制:
from chord import Chord # 可先过滤共现次数过低的边,避免图形过密 Chord(cooc, medications, width=600, height=600).to_html()写作要点:说明弦图与热力图的互补性——热力图适合看精确数值,弦图适合一眼看清「哪些节点之间连接最粗」(共现最强);说明阈值过滤的依据。
挑战任务 2.4(stretch goal):用正则表达式提取药物剂量
任务要求(可选加分项):用正则表达式从摘要中提取不同药物的剂量(如take 400mg of chloroquine daily中的400mg),构建展示各药物不同剂量的 DataFrame。注意:应寻找文本中紧邻药物名附近的数值。
实现思路:
- 剂量模式:典型剂量形如「数字 + 单位」,单位常见
mg、g、mcg/µg、IU等:r'(\d+(?:\.\d+)?)\s*(mg|g|mcg|µg|iu)'(配合ignorecase兼容MG/Mg等写法)。 - 邻近性约束:题目的提示强调「numeric values that are located near the medication name」。因此应先在摘要中定位药物名出现位置,只在其前后一定字符窗口(如前后 60 字符)内搜索剂量,避免把摘要中其他无关剂量误关联到本药物:
import re def extract_dose(abstract, medication, window=60): x = str(abstract).lower() doses = set() for mt in re.finditer(medication.lower(), x): start, end = max(0, mt.start()-window), min(len(x), mt.end()+window) snippet = x[start:end] for dm in re.finditer(r'(\d+(?:\.\d+)?)\s*(mg|g|mcg|µg|iu)', snippet): doses.add(f"{dm.group(1)}{dm.group(2)}") return sorted(doses)- 构建 DataFrame:遍历药物与摘要(可先抽样控制运行时间),汇总每种药物的剂量集合:
records = [] for me in medications: for a in df['abstract'].head(200): # 抽样以控制运行时间 for dose in extract_dose(a, me): records.append({'medication': me, 'dosage': dose}) dose_df = pd.DataFrame(records) dose_df.groupby('medication')['dosage'].apply(list) # 每种药物的剂量集合写作要点:说明正则的匹配逻辑、为什么需要「邻近窗口」约束(避免张冠李戴),以及如何处理大小写、小数点、空格等边界情况。
评分标准(Rubric)与自检清单
作业附带的评分标准如下:
| Exemplary(优秀) | Adequate(达标) | Needs Improvement(需改进) |
|---|---|---|
| 全部任务完成,有图形化展示与说明,并至少完成两个 stretch goal 中的一个 | 完成任务超过 5 项,但未尝试 stretch goal,或结果不清晰 | 完成任务少于 5 项(但多于 3 项),且可视化不能有效说明要点 |
对照评分标准,提交前建议自检:
- Part 1 的 4 项任务是否全部完成,每项是否都配图并附文字解读(曲线含义、异常现象);
- Part 2 的两项共现任务是否完成,热力图是否清晰可读、标签是否完整;
- 是否至少完成了 2.3(弦图)或 2.4(剂量提取)中的一个 stretch goal;
- 可视化是否确实「有效地说明了要点」,而非只贴图不解释;
- 结果是否可复现(运行环境、数据来源、关键参数如窗口大小、平移天数、阈值等是否在文中交代)。
环境与数据准备
- 运行环境:推荐使用 Jupyter Notebook。仓库中两个课堂 Notebook 均以「自顶向下」方式组织,可顺序执行;若还不熟悉 Jupyter 的运行方式,可参考课程 README(2-Working-With-Data/07-python/README.md)中的指引。
- 依赖库:
numpy、pandas、matplotlib(两个挑战均需要);Part 2 的 Sankey 图需要plotly(课堂代码已使用);stretch goal 2.3 需要chord库。 - 数据获取:
- 疫情时序数据可直接读取仓库内 data/COVID/ 下的三个 CSV 快照(课堂 Notebook 也提供在线加载 URL,网络不可用时可改用本地副本);
- 国家人口数据使用 data/UID_ISO_FIPS_LookUp_Table.csv,注意该表同时含国家与省/州级记录,取国家人口时应过滤
Province_State为空的记录(课堂代码有完整示例); - CORD-19 论文
metadata.csv不随仓库提供,需按课堂 Notebook 说明自行下载(Kaggle 下载可能需要注册)。
小结
本作业将课堂学到的 Pandas 数据处理能力落到两个真实研究场景:疫情传播建模考验时间序列的预处理、平滑、滚动窗口与滞后分析;论文分析考验从非结构化文本提取结构化信息、构建共现矩阵并多角度可视化。两部分共用同一套方法论——先清洗数据,再构造指标,最后用可视化验证假设并讲出故事。完成全部必做任务并至少攻克一个 stretch goal,即可完整走通「数据获取 → 处理 → 建模 → 可视化 → 解读」的全流程。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考