COVID-19数据可视化实战:Python从清洗到图表全流程
2026/9/2 18:21:28 网站建设 项目流程

简介:面向数据分析初学者和公共卫生研究者的COVID-19每日病例报告分析与可视化资源,基于约翰·霍普金斯大学公民影响中心持续更新的美国县级疫情数据,用Jupyter Notebook搭建了从数据获取、清洗到多维度可视化展示的完整流程。压缩包大小约92.76MB,内容以notebook代码和配套数据集为主,可直接运行学习。已有576人学习下载。资源梳理了多项数据源:县级确诊与死亡病例、纽约自治市病例、年龄段人口与种族、医疗床位分布、人口与贫困就业信息,并包含了状态检测、住院、恢复等主题,同时提供了桌面端与移动端可视化仪表板的实现思路。读者既能跟随代码理解疫情数据整合逻辑,也能参考其中的图表设计方法,将其迁移到其他时序数据分析项目中,是一份兼顾数据认知与可视化实操的参考资料。 做数据分析这几年,我越来越觉得,COVID-19的公开数据,尤其是每日病例报告,是练习"数据分析和可视化"最好的素材之一。它的数据量级足够真实,清洗过程有挑战,时间序列的趋势又特别适合做各种图表。这篇文章,我想完整拆解一下我自己的处理思路:从拿到原始CSV开始,到完成清洗、聚合、计算关键指标,再到输出不同类型的可视化图表,整套流程走下来,你会对"数据分析项目到底该怎么做"有一个很明确的体感。

这个项目适合三类人看:刚学完pandas但不知道做什么练手的人,准备简历里放数据分析项目的求职者,以及日常需要做日报、周报类数据汇报的运营和产品同学。它解决的也不是什么高深的问题,而是把一堆日期、地区、病例数字,变成能看懂、能讲故事的结论。

1. 项目整体思路与方案选型

做这个项目前,得先说清楚一件事:单纯画出图不叫分析,得让图和指标能回答业务问题。我给自己定的目标是,任何一张图拿出去,都能直接说清楚"趋势怎么样、拐点在哪、哪个地区最严重",而不是让对方看了图还问"所以呢"。

1.1 核心需求拆解:这个项目真正考的是什么

表面上看,这就是"下载数据 → 处理一下 → 画几张图"三步。但真正动手后会发现问题全藏在细节里。比如数据格式不统一、日期字段解析报错、某些地区数据缺失、宽表结构和长表结构怎么转换、不同国家的累计病例数量级差太大导致图表没法看。

我最终把需求拆成了四层。第一层是"数据可靠",保证拿到的数据经过清洗后是准确的;第二层是"指标清晰",定义好累计病例、新增病例、7日移动平均、每百万人口病例数这些口径;第三层是"可视化表达",根据不同的使用场景选对图表;第四层是"结论可解释",从图表里提取出明确的趋势判断。这四个层级的顺序不能乱,数据错了,后面全是白做。

1.2 为什么用Python + pandas + plotly这套组合

这个项目我见过有人用Excel做,也有人用Tableau做,但我的建议是学数据分析的人还是优先用Python。原因有三个:一是pandas处理这种时间序列表格数据的能力是最顺手的,宽表和长表的转换、按日期分组聚合、窗口计算,几行代码就搞定;二是Python的可视化生态极度丰富,同一份数据既能出论文风格的静态图,也能出网页交互图;三是这套流程可以完全脚本化,下次换一份数据跑一遍就行。

具体到工具选型,我用了pandas做数据处理,matplotlib出静态图,plotly出交互图,seaborn做了一些统计类的辅助图。数据集用的Johns Hopkins大学的全球COVID-19公开数据和Our World in Data的补充数据,文件是CSV格式,结构清晰,非常适合做案例。如果你也想复现,直接认准这两个来源就行,网上大量教程都基于它们,遇到问题也好搜索。

2. 数据获取与预处理

很多人做数据分析项目不重视预处理,上来就画图,结果图出来全是错的。我用这套COVID数据踩过的坑不在少数,所以单独把预处理环节拎出来讲。一份干净的数据集,是整个项目的地基。

2.1 数据集怎么选:两种主流数据源的对比

目前我接触到的COVID公开数据集主要有两种组织方式。一种是Johns Hopkins的格式,按"省/州、国家/地区、纬度、经度、然后是一长串日期列"排列,属于典型的宽表,一行是一个地区的全时间序列。另一种是Our World in Data的格式,每一行是一个国家在某个日期的记录,附带测试量、疫苗接种、人口、GDP等四十多个字段,属于长表,信息更丰富但更杂。

我两个都用了。练手阶段优先推荐Johns Hopkins的宽表,因为宽表转长表这个操作本身就是数据分析里的高频技能,而且数据结构简单,适合掌握核心逻辑。等熟悉了再切换到OWID数据做深入分析,比如分析疫苗覆盖率对住院率的影响,这种字段只有OWID才有。如果你是想做一个"看起来信息量很大"的项目展示,直接用OWID就够了,字段多自然显得内容多,但代价是数据质量参差不齐,很多列缺失严重。

2.2 清洗过程详解:日期解析、宽表转长表、缺失值处理

宽表转长表是处理这套数据的第一个关键操作。原始数据是一列一个日期,但pandas做时间序列分析最好是一列日期、一列数值、一列地区标识。代码很简单,用melt函数把日期列全部放入新的一列:

import pandas as pd df = pd.read_csv("time_series_covid19_confirmed_global.csv") # 把宽表转成长表 id_vars = ["Province/State", "Country/Region", "Lat", "Long"] df_long = df.melt(id_vars=id_vars, var_name="Date", value_name="Confirmed") df_long["Date"] = pd.to_datetime(df_long["Date"])

这里有一点值得注意。不同的数据集日期格式可能不一样,有的是"1/22/20"这种美式格式,有的是"2020-01-22"这种ISO格式。读进来之后不要急着分析,先跑一遍df.dtypes确认Date列是datetime类型,否则后面按日期切片、绘图时的坐标轴排序都会出问题。我一开始没注意,结果plotly画图的时候日期轴乱序,排查了半天才发现是字符串排序导致的。

缺失值处理也不能忽略。COVID数据里有些国家下面分省,有些没有省这一级,所以Province/State列大量为空。我的处理原则是:做国家层面分析时直接去掉省份维度,按国家分组对Confirmed求和;如果做的分析需要保留省州维度,就把空值填成"Unknown",避免pandas在后续groupby时产生歧义。此外还有一类"缺失"是数据上报延迟造成的,表现是某一天新增为0,这不一定代表真实为0,只是没更新。对这种问题,你可以选择保留原样,也可以在报告里注明"受上报节奏影响"。

3. 核心分析逻辑:指标怎么定义,决定了结论怎么讲

数据清洗完毕,接下来是分析层面。这里的核心不是代码,而是指标口径。我整理了一套固定的分析流程,做任何时间序列数据都能直接用。

3.1 累计病例 vs 新增病例:先想清楚再动手

累计病例和新增病例是解读疫情走势的两个基础视角。累计病例适合看整体规模,但它是一条单调递增的曲线,趋势变化不明显;新增病例才真正反映疫情的传播速度和是否进入拐点。计算新增病例我的习惯是用diff()方法,而不是自己写循环去减,因为diff()在pandas里是向量化操作,处理几十万行数据时快得多。

df_global = df_long.groupby("Date").agg(Confirmed=("Confirmed", "sum")).reset_index() df_global["DailyNew"] = df_global["Confirmed"].diff() df_global["DailyNew"] = df_global["DailyNew"].fillna(0)

注意第一天的diff()结果是NaN,因为前面没有值可以减,这里要fillna成0。另外,如果数据来自多个地区合并,你需要先按日期groupby求和到全球维度,再计算新增,顺序反了会得到错误的趋势线。

3.2 7日移动平均:为什么是7不是3,周效应到底影响多大

疫情数据存在非常明显的周期性,最典型的是"周效应":很多地区周末不上报或上报量减少,导致周一数字偏高、周末偏低。如果直接看每日新增,你会看到锯齿状的剧烈波动,根本没法判断真实趋势。解决方式是用移动平均把周期噪声抹平。

我选的窗口是7天,因为疫情的传播周期恰好以周为单位。代码是df_global["SmoothNew"] = df_global["DailyNew"].rolling(window=7).mean()。这里有个细节,rolling默认会取前6天加上当天共7天的均值,所以画图时前6天是NaN,图上会有一段空白,可以在画图时用dropna()处理。

窗口选3天可以吗?可以,但平滑效果很差,只是把锯齿减轻了而已。选14天呢?曲线很光滑,但反应速度慢,峰值的出现会明显偏晚。我做了几次对比后发现,7日移动平均是兼顾时效性和平滑度的最佳方案,这也是流行病学报告里最常用的口径。

3.3 增长率、环比与每百万人口标准化:让不同地区可对比

除了绝对数字,我还算了两类派生指标。第一类是日环比增长率,计算公式是(当日新增 - 前一日新增) / 前一日新增 * 100,这个指标能捕捉疫情是加速还是减速。但要注意,当基数很小时,环比会剧烈波动,比如昨天新增10例今天新增20例,增长率是100%,这种数字在报告里要谨慎使用,最好配合绝对数一起看。

第二类是每百万人口病例数,计算方式是病例数 / 人口数 * 1000000。为什么需要这个指标?因为不同国家人口规模差异悬殊,绝对病例数高的国家不一定每百万人病例数高,后者更公平地反映了疫情在某国的渗透程度。如果要比较两个国家或地区,我会把它们每百万人口的新增病例画在同一张图上,而不是直接对比绝对数,否则人口大国永远"看起来更严重"。

4. 可视化实现:从静态图到交互大屏

可视化是这个项目最出彩的部分,也是最容易翻车的部分。我做了一个三类图表的组合方案:静态图用于报告和文档,交互图用于网页展示,地图用于全局概览。三者各司其职,效果会好于只堆砌一种图。

4.1 静态趋势图:matplotlib画出论文级别的曲线

静态图用途是存档和打印,所以要尽量自包含信息。我最常用的一张图是"全球每日新增病例 + 7日移动平均曲线",它回答的问题是"全球疫情周期到底走完了几波"。绘制时我叠加了两条线:浅色细线是原始每日新增,深色粗线是7日移动平均,一虚一实对比强烈,读者一眼就能看到"平滑后的主趋势"。

import matplotlib.pyplot as plt import matplotlib.dates as mdates fig, ax = plt.subplots(figsize=(12, 6)) ax.plot(df_global["Date"], df_global["DailyNew"], linewidth=0.8, alpha=0.5, label="Daily New") ax.plot(df_global["Date"], df_global["SmoothNew"], linewidth=2.2, label="7-day Average") ax.xaxis.set_major_locator(mdates.MonthLocator(interval=6)) ax.set_title("Global Daily New COVID-19 Cases") ax.legend() plt.xticks(rotation=45) plt.tight_layout() plt.savefig("covid_trend.png", dpi=200)

这里有个重要的技巧:x轴的日期刻度要设置间隔,否则几百个日期标签叠在一起,图就废了。我用的是MonthLocator(interval=6),每6个月显示一个标签,保证可读性。如果你在画多国家对比图,建议取对数坐标plt.yscale("log"),因为不同国家的数量级能差到100倍以上,普通坐标轴会把小数字压成一条直线。

4.2 交互式图表与大屏展示:plotly让数据"活"起来

静态图适合深度阅读,但放到网页或大屏上,交互式图表明显更合适。plotly express提供了两行代码出图的体验,非常适合快速验证想法。比如我想做一个能支持悬停查看数据的全球新增病例趋势图:

import plotly.express as px fig = px.line( df_long_country, x="Date", y="Confirmed", color="Country/Region", title="COVID-19 Confirmed Cases by Country", ) fig.update_layout(hovermode="x unified") fig.show()

你看代码连手工配置hover内容都省了,plotly会自动把所有字段显示出来。做数据大屏时,我会把几个关键卡片放在上方——累计确诊、今日新增、7日均值、累计死亡——下方放趋势图和地图。大屏的布局逻辑是"上KPI、中趋势、下地图",视野从左到右分布,信息密度从高到低递进。用plotly的subplot或者直接把多个div塞进一个网页框架都行,重点不是技术,而是叙事节奏。

4.3 热力地图:从全局视角看扩散路径

地图是全局概览的最好形式。plotly的choropleth_mapboxchoropleth都能实现,前者需要申请地图token,后者可以直接用内置地理数据。我用的choropleth画过一版全球累计确诊分布图,颜色越深代表累计病例越多,鼠标悬停能查看具体数值。

fig = px.choropleth( world_cases, locations="iso_alpha", color="Confirmed", hover_name="Country/Region", color_continuous_scale="YlOrRd", title="Worldwide COVID-19 Confirmed Cases", ) fig.show()

这里最容易踩的坑是:国家名称的写法(China vs CN vs CHN)三套体系不一样,plotly默认需要ISO三个字母代码,如果你的原始数据里是国家全称,就得先做一次映射,否则地图上会大量显示为灰色。我的做法是在数据清洗阶段就手动加一列iso_alpha,用pycountry库或字典映射都行,这一步逃不掉。

5. 常见问题与排查技巧实录

写代码没有不踩坑的,踩坑不可怕,可怕的是不知道怎么排查。我做这个项目过程中遇到过不少问题,这里挑几个最典型的写下来,都是实打实会遇到的情况。

5.1 高频报错与处理方式速查表

问题现象产生原因排查思路与解决方案
日期列排序错乱日期是字符串类型,按字母序而非时间序排序pd.to_datetime转换,然后sort_values
matplotlib图片中文变方块系统中文字体缺失指定字体,如plt.rcParams["font.sans-serif"] = ["SimHei"]并加plt.rcParams["axes.unicode_minus"] = False
FutureWarning: Groupby操作pandas版本升级后的行为变更阅读警告详情,按提示换用新API,如observed=True参数
plotly地图大部分为灰国家名称不是ISO代码使用pycountry库做名称映射,转换后传入locations
数据量过大导致浏览器卡死绘制过多国家过多点先聚合到世界或大洲维度,或用downsample抽样
diff()首日为NaN第一行没有前值.fillna(0)dropna()处理
图表坐标轴标签重叠日期/文本标签过多设置plt.xticks间隔或旋转角度,用MaxNLocator控制密度

这张表基本覆盖了我见过的高频问题。如果你遇到表外的报错,我的排查习惯是先把报错信息拷进搜索引擎看前三条结果,绝大多数情况下已经有人踩过同样的坑了。

5.2 独家经验:数据分析项目的落地心得

最后分享几个从项目中沉淀下来的实操心得,这些经验比具体的函数更有长期价值。

第一个心得是:参数化一切路径。写脚本时不要一条路径写死,用pathlib.Path构建路径,用config.py集中管理列名、日期格式、文件URL,这样你换一份新数据时只需要改配置文件,而不是通篇搜索替换。

第二个心得是:保留数据快照。原始数据每次从网上下载都可能变化,所以我会在下载后第一时间复制一份副本作为原始快照,后续清洗在副本上进行。这保证了项目的可复现性,别人拿到代码跑出来的结果和你文章里截图对得上。

第三个心得是:分析要有"why"层的结论。很多人做数据分析项目最后只写了"数据呈上升趋势"这种废话,那不算结论。真正的结论应该是"东南亚地区在2022年3月后出现持续性上升,与当地放宽检测政策在时间上吻合,需要结合具体政策进一步验证"。就算不做因果验证,也要在结论里明确说明数据现象、时间窗口和可能的原因假设。

如果你也在做类似的数据分析项目,我建议不要只满足于把官方示例跑通。把数据集换成你所在地区的数据,增加一两个你自己定义的指标,整份报告做出来的感觉会和抄教程完全不一样。COVID数据只是一个很好的起点,把它玩熟了,你会发现自己掌握的不只是绘图函数,而是一套"拿任何数据都能讲故事"的框架能力。

本文还有配套的精品资源,点击获取

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

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

立即咨询