news 2026/10/3 13:25:16

基于Python的中美疫情数据可视化分析与展示源码实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python的中美疫情数据可视化分析与展示源码实战

简介:这份资源是一套面向Python数据分析初学者与可视化实践者的完整项目源码,围绕中美两国疫情数据展开处理与展示,适合用作课程设计、毕业设计参考或数据分析练手案例。压缩包共19个文件,约145KB,其中7个HTML页面承担可视化结果展示,6个Python脚本负责数据清洗、预测与图表生成,4个Excel和1个CSV文件提供中美及分省疫情原始数据,另有说明文档辅助理解项目结构。项目覆盖国内疫情、全球疫情、中美对比以及中国与美国未来14天新增预测等分析模块,读者可借此掌握从数据读取、指标计算到网页呈现的完整链路,并参考预测脚本的实现思路。目前已有296人学习,体量轻便,便于快速运行与二次修改。

1. 中美疫情数据可视化到底在做什么:从一份 CSV 到能讲故事的看板

很多人第一次接触「基于Python的中美疫情数据可视化分析与展示源码」这个标题,脑子里浮现的是几张折线图加一个网页。真动手做过就知道,难点从来不是画图,而是数据从哪来、怎么对齐、怎么让两张不同口径的表能放在同一张图上比。中美两国的疫情数据在统计口径、更新频率、字段命名上差异极大,直接 concat 会得到一堆 NaN 和错位的日期。这个方向真正解决的问题是:把两份异构的公开数据整理成统一结构,再用 Python 做清洗、聚合,最后用可视化把趋势、峰值、阶段性差异讲清楚。适合有 Python 基础、想做一个完整数据项目练手的人,也适合需要快速搭一个数据看板原型的开发者。下面这套路径我实际跑过,从环境到出图到排错,能照着复现。

2. 数据获取与清洗:中美两张表怎么对齐到同一根时间轴上

2.1 数据源的字段差异与统一策略

中美疫情数据最常见的来源是两类:一类是按日期累计的全国汇总表,字段通常是date、confirmed、deaths、recovered;另一类是按州/省拆分的明细表,多一层state或province维度。两边的坑在于:美国数据常用mm/dd/yy格式,中国数据常用yyyy-mm-dd;美国累计数偶尔出现回退(数据修正),中国数据在早期有过统计口径调整。统一策略是先把日期全部转成datetime64,再按天重采样,累计值用cummax处理回退,避免曲线出现负增长。

import pandas as pd def load_and_clean(path, date_col, value_cols, region=None): df = pd.read_csv(path) # 日期统一:兼容两种常见格式 df[date_col] = pd.to_datetime(df[date_col], format='mixed', dayfirst=False) df = df.sort_values(date_col) # 累计值回退修正:取历史最大值,防止曲线抖动 for col in value_cols: df[col] = df[col].cummax() if region: df = df[df['region'] == region] df = df.set_index(date_col).resample('D').ffill().reset_index() return df[['date', 'confirmed', 'deaths']]

这段代码的关键参数是format='mixed',它能自动识别多种日期格式,省去手写映射。cummax是处理数据修正的核心,没有它,你会看到某天新增为负的玄学曲线。resample('D').ffill()保证两张表在相同日期索引上对齐,缺失日向前填充。注意:如果原始数据是新增值而非累计值,先去重再累加,不要直接 cummax。

2.2 用 Pandas 做中美数据合并与派生指标

对齐之后不要急着画图,先算几个真正有分析价值的派生指标:每百万人确诊、病死率、7 日滚动新增。中美人口基数差异巨大,绝对数值对比没有意义,必须归一化。7 日滚动能抹平周末上报延迟带来的锯齿。

US_POP = 331_000_000 CN_POP = 1_412_000_000 def add_metrics(df, pop): df['per_million'] = df['confirmed'] / pop * 1_000_000 df['cfr'] = df['deaths'] / df['confirmed'].replace(0, pd.NA) df['new_7d'] = df['confirmed'].diff().rolling(7).mean() return df us = add_metrics(load_and_clean('us.csv', 'date', ['confirmed','deaths'], 'United States'), US_POP) cn = add_metrics(load_and_clean('cn.csv', 'date', ['confirmed','deaths'], 'China'), CN_POP) merged = pd.concat([us.assign(country='US'), cn.assign(country='CN')], ignore_index=True)

per_million是跨国家对比的唯一合理口径,cfr在早期数据里会偏高,因为分母滞后,建议只取确诊数大于 1000 之后的区间。new_7d用diff加rolling,注意第一周会是 NaN,画图时 dropna 或从第 8 天开始截取。合并时用assign加国家标签,比手动加列更干净。

3. 可视化实现:用 Matplotlib 和 Pyecharts 各画一版对比看板

3.1 Matplotlib 静态图:适合报告和论文的四个必画图

静态图的价值在于可复现、可嵌入文档。我一般会画四张:累计确诊对比、每百万人确诊对比、7 日新增双轴图、病死率随时间变化。前两张用折线,第三张用面积加折线,第四张用散点加趋势线。

import matplotlib.pyplot as plt import matplotlib.dates as mdates fig, axes = plt.subplots(2, 2, figsize=(14, 9)) for country, grp in merged.groupby('country'): axes[0,0].plot(grp['date'], grp['confirmed'], label=country) axes[0,1].plot(grp['date'], grp['per_million'], label=country) axes[1,0].plot(grp['date'], grp['new_7d'], label=country) axes[1,1].scatter(grp['date'], grp['cfr'], s=4, label=country) axes[0,0].set_title('Cumulative Confirmed') axes[0,1].set_title('Confirmed per Million') axes[1,0].set_title('7-Day Rolling New Cases') axes[1,1].set_title('Case Fatality Rate') for ax in axes.flat: ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) ax.legend() plt.tight_layout() plt.savefig('covid_compare.png', dpi=150)

figsize和dpi决定输出清晰度,报告用 150 足够,印刷用 300。mdates.DateFormatter控制横轴刻度,不设置会挤成一团。scatter的s=4是点大小,病死率散点太大会糊成一片。注意:cfr在确诊为 0 的日期是 NA,Matplotlib 会自动断开,这是期望行为。

3.2 Pyecharts 交互看板:把折线图变成可缩放的网页

如果目标是展示而非印刷,Pyecharts 更合适,它生成的 HTML 可以缩放、悬停看数值。核心是用Line加Timeline或Grid做多图联动。下面是一个双国家折线的最小可跑版本。

from pyecharts import options as opts from pyecharts.charts import Line def build_line(df, metric, title): line = Line(init_opts=opts.InitOpts(width='900px', height='500px')) for country, grp in df.groupby('country'): line.add_xaxis(grp['date'].dt.strftime('%Y-%m-%d').tolist()) line.add_yaxis(country, grp[metric].round(2).tolist(), is_smooth=True, is_symbol_show=False) line.set_global_opts( title_opts=opts.TitleOpts(title=title), datazoom_opts=[opts.DataZoomOpts(range_start=60, range_end=100)], tooltip_opts=opts.TooltipOpts(trigger='axis'), yaxis_opts=opts.AxisOpts(type_='log' if metric=='confirmed' else 'value') ) return line build_line(merged, 'per_million', 'Confirmed per Million').render('per_million.html')

DataZoomOpts的range_start=60表示默认只显示后 40% 的时间段,因为早期数据噪声大。is_smooth=True让曲线平滑,但会轻微失真,分析场景建议关掉。yaxis_opts里对累计确诊用对数轴,否则中国曲线会被压平。render输出独立 HTML,不依赖服务器,直接浏览器打开即可。

4. 避坑与排查:数据可视化项目里最容易翻车的五个地方

4.1 日期解析失败导致整条曲线错位

现象:合并后某些日期出现重复或缺失,折线图出现垂直跳变。原因:中美数据一个用2020/1/1,一个用01-01-2020,pd.to_datetime默认按美式解析,把03/04当成 3 月 4 日而非 4 月 3 日。解决:显式指定format或先用format='mixed'加dayfirst参数逐列验证,打印df['date'].head(20)人工确认。

4.2 累计值回退被当成负增长画进图里

现象:新增曲线出现深 V 负值。原因:数据源做了历史修正,累计数下调。解决:对累计列做cummax,新增列用diff后把负值 clip 到 0,再 rolling。不要直接对原始累计值画 diff。

4.3 人口基数用错导致每百万人指标差一个量级

现象:中国每百万人确诊看起来比美国低很多,但实际趋势相反。原因:把中国人口写成 14 亿但单位用了「万」,或者美国人口用了 3.3 亿却除以了 100 万两次。解决:统一用「人」为单位,per_million = confirmed / pop * 1_000_000,写完后手动验算一条:100 万确诊 / 3.31 亿 ≈ 3021。

4.4 Pyecharts 在 Jupyter 里不显示或空白

现象:render_notebook()调用后无图。原因:JupyterLab 与 Pyecharts 版本不匹配,或缺少pyecharts-jupyter-installer。解决:改用render('out.html')输出文件再打开,或者固定pyecharts==2.0.3加jupyterlab扩展。不要在生产看板里依赖 notebook 内嵌。

4.5 中文字体缺失导致标题和标签变方块

现象:Matplotlib 图上中文全是方框。原因:默认字体不含 CJK。解决:plt.rcParams['font.sans-serif'] = ['SimHei']或['Noto Sans CJK SC'],同时plt.rcParams['axes.unicode_minus'] = False修复负号。Linux 服务器上先fc-list :lang=zh确认有中文字体再设。

5. 进阶技巧:把静态脚本变成可复用的数据管道

5.1 用配置文件驱动多国对比,避免硬编码

写到后面你会发现,每加一个国家就改一次代码很蠢。我一般会抽一个config.yaml,把数据路径、人口、字段映射、颜色都放进去,主脚本只读配置循环处理。这样换数据源或加国家不用动核心逻辑。

import yaml with open('config.yaml') as f: cfg = yaml.safe_load(f) frames = [] for name, meta in cfg['countries'].items(): df = load_and_clean(meta['path'], meta['date_col'], meta['value_cols'], meta['region']) df = add_metrics(df, meta['population']) frames.append(df.assign(country=name)) merged = pd.concat(frames, ignore_index=True)

config.yaml里每个国家一个块,population写整数,value_cols写列名列表。这样新增国家只需加一段配置,不动 Python。注意 YAML 缩进必须用空格,Tab 会报错。

5.2 验证数据质量的三行检查

出图前跑三行检查,能省掉大量返工:merged.groupby('country')['date'].agg(['min','max','count'])看时间范围是否一致;merged.isna().sum()看缺失分布;merged.groupby('country')['confirmed'].apply(lambda s: (s.diff() < 0).sum())看还有没有负增长。这三行跑完没问题,图基本不会翻车。

5.3 一个我常犯的错

早期我总想把所有指标塞进一张图,结果读者什么都看不清。后来强迫自己一张图只讲一件事:对比就只放对比,趋势就只放趋势。看板不是数据仓库,是叙事工具。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 13:25:15

DRV8818PWPR+STM32F746ZG工业级步进电机精准电流控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 13:24:54

嵌入式C语言面试核心:指针、内存管理与字符串陷阱解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 13:24:15

NRI神经关系推理:从图结构潜变量到轨迹预测的PyTorch实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 13:22:15

CSP词频统计题的工程化读题与C++状态机实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 13:21:34

STM32实现OOK无线收发:低成本方案与CubeMX配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 13:19:56

数据库设计实战:从函数依赖到3NF分解的完整推演

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华