Python疫情数据可视化项目:从CSV到HTML的完整分析链路

发布时间:2026/9/23 11:22:04
Python疫情数据可视化项目:从CSV到HTML的完整分析链路 简介这套基于Python的中美疫情数据可视化分析与展示源码面向需要快速上手数据分析与可视化项目的学习者、竞赛备赛者以及对疫情趋势感兴趣的研究者完整展示了从读取Excel/CSV数据、用Python进行数据处理与预测到生成HTML交互页面的全链路过程。压缩包共19个文件核心包括6个Python脚本、7个HTML页面、4个Excel数据源以及CSV与txt说明文件整体仅145KB结构清晰且便于按模块研读。脚本覆盖国内分省疫情分析、中美疫情对比和未来14天新增趋势预测等典型场景HTML页面则呈现全球/国内疫情总览、中美对比与预测结果读者可直接运行源码、复现图表并在此基础上替换数据或调整算法开展二次开发。已有296人学习适合想通过完整项目案例提升Python数据分析和可视化能力的人群。1. 疫情数据可视化项目20个文件把Python分析和HTML展示串成了一条线假设你手上有一份疫情CSV不光想画折线图还要输出一份带预测、可以传给别人的网页报告这时候单靠Excel透视表是撑不住的。这个基于Python的中美疫情数据可视化项目就是一个完整的源码包6个Python脚本负责数据清洗、分析和预测7个HTML页面负责展示中间用CSV和Excel文件衔接。它解决了“数据从哪来、怎么算、怎么展示”三个问题输出的每一个HTML都能单独用浏览器打开也适合拿去跟同伴讲解数据趋势。适合正在学Python数据分析、看完教程想动手做一个完整项目的人也适合手里已有数据、想快速跑通可视化链路并做二次开发的人。下面我从文件结构开始逐个把脚本逻辑和踩坑点拆开讲。2. 项目全景20个文件的分工与数据从CSV到Excel的流转2.1 四类文件的分工Python脚本是发动机HTML是仪表盘这20个文件乍一看很杂但归类后只有四类各管一段。先把文件清单捋出来后续脚本分析才不会迷路。类别文件职责原始数据history.csv全球疫情历史序列包含日期、国家/地区、累计确诊等字段中间数据China.xlsx、America.xlsx、China_province.xlsx、world.xlsx按国家或省份拆好的结构化数据Python脚本World.py、China——province.py、Compare.py、China_xz_predict.py、America_xz_predict.py、pr.py读取、清洗、聚合、对比、预测、输出HTML展示页面index.html、全球疫情情况.html、国内疫情情况.html、中美疫情对比.html、中国未来14天新增预测.html、美国未来14天新增预测.html、疫情预测分析.html可视化终端直接供阅读辅助文件readme.txt、upload.zip使用说明和打包备份Python脚本是发动机负责从CSV和Excel读入数据、算累计或新增指标、再用模型预测未来14天HTML是仪表盘所有分析结果最终都落到独立的网页文件里。这个项目最值得学习的点在于脚本跑完不是打印几行数字就结束而是生成一份能发给别人的静态报告这也是企业级数据可视化项目的常规思路。2.2 清洗路径pr.py把history.csv拆成China.xlsx和America.xlsxhistory.csv是原始数据源里面通常包含全球所有国家和地区的每日记录。如果直接拿这个文件去画中美对比图每次都要做筛选既慢又容易出错。所以第一步应该把原始CSV处理成每个项目脚本都能直接读的中间Excel文件这个任务正好由pr.py承担。# pr.py 数据清洗核心逻辑 import pandas as pd df pd.read_csv(history.csv) # 先摸清列名防止大小写或空格不一致 print(df.columns.tolist()) # 统一列名去掉首尾空格统一英文小写 df.columns [col.strip().lower() for col in df.columns] # 如果CSV里按省/州拆行需要先按国家聚合 # 假设关键列是 country、date、confirmed daily (df.groupby([date, country], as_indexFalse)[confirmed] .sum() .sort_values(date))这里有两个关键操作columns.tolist()把原CSV的字段名打出来看一眼这一步能节约后面大量的排查时间groupby([date, country])把同一国家多个省份的记录合并成国家维度as_indexFalse保证分组后country仍然是列而不是索引。聚合完之后再用条件筛选拆出中美两国存成单独文件。# 接上面拆出中美数据并落地 cn daily[daily[country] China].copy() us daily[daily[country] US].copy() # 写回Excel显式指定openpyxl引擎 cn.to_excel(China.xlsx, indexFalse, engineopenpyxl) us.to_excel(America.xlsx, indexFalse, engineopenpyxl) # 整体数据也存一份给World.py用 daily.to_excel(world.xlsx, indexFalse, engineopenpyxl)注意to_excel里我用engineopenpyxl显式指定了写xlsx文件的引擎。pandas默认行为在不同版本里不一致不写这个参数有时会静默降级到旧版xlwt引擎生成的文件实际上是损坏的。等你再read_excel读回来报错信息会指向文件格式根本猜不到是写的时候出了问题。China_province.xlsx是省级粒度数据由China——province.py读取。这个脚本的文件名里有个中文全角破折号“——”这是和Windows、Linux文件系统都能兼容的命名但在代码里引用路径时极容易打错后续第5章我会专门讲这个坑。2.3 index.html的入口作用页面之间的导航关系index.html在整个项目里是目录页作用是把其他六个HTML页面串起来。它的内容不复杂核心就是一组超链接。!-- index.html 目录导航示意 -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 title新冠数据可视化分析/title /head body h1新冠疫情数据可视化分析/h1 ul lia href全球疫情情况.html全球疫情情况/a/li lia href国内疫情情况.html国内疫情情况/a/li lia href中美疫情对比.html中美疫情对比/a/li lia href中国未来14天新增预测.html中国未来14天新增预测/a/li lia href美国未来14天新增预测.html美国未来14天新增预测/a/li lia href疫情预测分析.html疫情预测分析/a/li /ul /body /html这些页面文件名都是中文你本地双击index.html用file://协议打开部分浏览器可能会因为编码差异导致链接找不到文件。我的习惯是把整个项目文件夹丢进VS Code然后用Live Server插件起一个本地服务再访问或者用命令行python -m http.server 8080这样中文路径问题基本不会出现。3. 核心脚本拆解14天预测与中美对比的算法逻辑3.1 China_xz_predict.py线性回归预测未来14天新增预测脚本是这堆文件里技术含量最高的。疫情数据本质是时间序列但这里用的并不是LSTM这类复杂模型而是线性回归——把日期转换成连续整数天数再用“天数”去拟合“新增确诊”。为什么选这么简单的模型因为疫情数据波动大、样本量有限复杂模型在短序列上很容易过拟合线性回归反而稳定、可解释出图也自然。# China_xz_predict.py 核心推理逻辑 import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression # 读取已清洗好的中国数据 df pd.read_excel(China.xlsx, engineopenpyxl) df[date] pd.to_datetime(df[date]) df df.sort_values(date) # 日期转成距起始日的整数天数作为回归特征 df[day] (df[date] - df[date].min()).dt.days X df[day].values.reshape(-1, 1) y df[new_cases].values # 训练线性回归模型 model LinearRegression() model.fit(X, y) # 构造未来14天的天数序列 last_day df[day].max() future_days np.arange(last_day 1, last_day 15).reshape(-1, 1) pred model.predict(future_days) # 生成对应日期带格式输出 future_dates pd.date_range( startdf[date].max() pd.Timedelta(days1), periods14 ) result pd.DataFrame({ date: future_dates.strftime(%Y-%m-%d), pred_new_cases: np.round(pred, 1) }) print(result)这里有两个参数要特别说明。第一是X.reshape(-1, 1)sklearn的线性回归要求特征矩阵是二维的哪怕只有一列特征也必须显式变成行向量很多人第一次跑就挂在shape不匹配上。第二是periods14这个数字直接控制预测窗口长度改成7就是未来一周预测改成30就是月度预测。3.2 America_xz_predict.py与数据平滑处理America_xz_predict.py的结构和中国版本几乎一致只是读取America.xlsx、输出美国未来14天新增预测.html。但实际跑的时候美国数据有一个典型问题单日新增波动非常大周末数据偏低、周一反弹是常态。直接用原始序列喂给线性回归拟合出来的直线会被噪声带偏。常见做法是在回归之前先做一次平滑用7天移动平均把周末效应抹掉。# America_xz_predict.py 数据平滑逻辑 df pd.read_excel(America.xlsx, engineopenpyxl) df[date] pd.to_datetime(df[date]) df df.sort_values(date) # 7天移动平均min_periods1保证前6天不出现NaN df[new_cases_smooth] ( df[new_cases] .rolling(window7, min_periods1) .mean() ) df[day] (df[date] - df[date].min()).dt.days X df[day].values.reshape(-1, 1) y df[new_cases_smooth].values # 后续回归流程与China版一致rolling(window7)是移动平均的窗口大小min_periods1表示窗口内最少有1个有效值就算平均否则前6天会全部变成NaN回归就废了。如果你跑出来的预测线前段是断的先检查这里有没有写min_periods。3.3 Compare.py与pr.py中美对比的指标选择Compare.py做的事情是把China.xlsx和America.xlsx按日期合并然后算对比指标。中美两国人口基数差了四倍多直接拿累计确诊绝对值画双线图美国的线会高高在上视觉上完全看不出中国的增长趋势。所以对比脚本里一定要做归一化。# Compare.py 对比指标计算 import pandas as pd cn pd.read_excel(China.xlsx, engineopenpyxl) us pd.read_excel(America.xlsx, engineopenpyxl) cn cn[[date, confirmed]].rename(columns{confirmed: cn_confirmed}) us us[[date, confirmed]].rename(columns{confirmed: us_confirmed}) # 内连接只保留两边都有数据的日期 merged pd.merge(cn, us, ondate, howinner) # 归一化每百万人口确诊数 CN_POP 1400000000 US_POP 331000000 merged[cn_per_million] merged[cn_confirmed] * 1000000 / CN_POP merged[us_per_million] merged[us_confirmed] * 1000000 / US_POPhowinner这个参数决定了合并策略。内连接只保留双方共有的日期如果某天只有中国数据没有美国数据那一行会被丢弃。相比外连接内连接不会产生NaN但代价是样本量可能减少。美国疫情数据公布节奏不稳定用内连接是更稳的选择。人口常量我直接写死在脚本里如果你要改成别的国家对比记得同步替换这两个常量。pr.py在项目里承担的是数据预处理职责除了前面拆CSV的操作它还会对日期格式做统一处理。原始CSV里日期可能有2020-1-5、2020/1/5、20200105三种格式混着统一用pd.to_datetime加format参数解析最省心。4. 可视化输出从DataFrame到ECharts的HTML渲染4.1 全球、国内、中美三个维度页面怎么生成7个HTML页面覆盖三个维度全球、国内、中美对比。生成方式本质上都是同一个套路——脚本里先准备好数据再拼HTML模板最后渲染出图表。World.py读取world.xlsx按国家聚合算Top排行榜输出到全球疫情情况.htmlChina——province.py读取China_province.xlsx渲染国内省级分布输出到国内疫情情况.htmlCompare.py读两国数据输出中美疫情对比.html。模板里用到的图表库是ECharts这也是目前数据可视化项目里最主流的选型。# 通用HTML模板渲染文件 def render_html(title, chart_div_id, option_json, output_file): html_content f !DOCTYPE html html langzh-CN head meta charsetUTF-8 title{title}/title script src./static/echarts.min.js/script /head body div id{chart_div_id} stylewidth: 900px; height: 600px;/div script var chart echarts.init(document.getElementById({chart_div_id})); chart.setOption({option_json}); /script /body /html with open(output_file, w, encodingutf-8) as f: f.write(html_content) print(f已生成 {output_file})这个模板的核心是把ECharts的option配置以JSON字符串形式嵌入script标签。chart.setOption是ECharts的入口方法所有配置都放在这个对象里。注意script的src我写的是./static/echarts.min.js意味着你需要在项目下建一个static目录并放一份ECharts库文件这样才能离线打开页面。4.2 预测结果在HTML里如何区分实际值与预测值中国未来14天新增预测.html和美国未来14天新增预测.html这两个页面图表上同时有两条线历史实际值和未来预测值。前端要区分它们靠的不只是颜色而是数据里带一个标记字段。# 构造带标记的历史预测拼接数据 import pandas as pd df pd.read_excel(China.xlsx, engineopenpyxl) # 取最近30天实际新增 history df.tail(30)[[date, new_cases]].copy() history.rename(columns{new_cases: value}, inplaceTrue) history[is_predict] False # 预测结果构造为DataFrame future pd.DataFrame({ date: future_dates, value: pred, is_predict: True }) # 拼接并转JSON combined pd.concat([history, future], ignore_indexTrue) data_for_chart { dates: combined[date].astype(str).tolist(), values: combined[value].round(0).tolist(), isPredict: combined[is_predict].tolist() }is_predict这个布尔字段是关键前端拿到它之后可以给预测线段设置成虚线、浅色和实际值形成视觉区分。pd.concat按行拼接两个结构相同的DataFrameignore_indexTrue保证行号重新从0开始否则图表数据会被旧索引打乱。4.3 JSON注入ensure_ascii和编码相关的坑按上面的方式把data_for_chart变成JSON字符串时有一个容易被忽视的细节Python的json.dumps默认会把中文转成\uXXXX转义序列HTML源码里看起来全是反斜杠虽然也能运行但可读性很差排查问题的时候非常痛苦。import json # ensure_asciiFalse让中文原样输出HTML里直接可读 option_json json.dumps(data_for_chart, ensure_asciiFalse)这样生成的HTML里日期和中文标题都保留原始字符浏览器解析毫无压力。另一个点是写HTML文件时open(output_file, w, encodingutf-8)必须带上encoding参数否则Windows默认的gbk编码会把中文字符写坏页面打开就是一堆乱码。这两个编码问题我建议养成固定习惯写文件永远指定utf-8。5. 避坑指南跑通这个项目最容易翻车的五个地方5.1 现象FileNotFoundError文件明明就在脚本同目录原因China——province.py这个文件名里是中文全角破折号“——”代码里如果引用的是半角“--”或直接复制路径时混入全角字符Python会把文件路径当成另一个字符串去查找自然找不到。解决进入项目目录执行ls或dir命令先确认文件实际名字再复制粘贴进代码。更彻底的做法是改文件名为ASCII安全的China_province.py然后全局替换所有引用。从那以后我接手的每个项目文件名一律不用中文和全角符号。5.2 现象read_excel报错“Missing optional dependency openpyxl”原因pandas读.xlsx文件需要安装openpyxl库你的虚拟环境里只装了pandas缺了底层依赖。另一个关联场景是写Excel时没指定enginepandas偷偷用旧引擎生成损坏文件。解决在项目根目录执行pip install openpyxl然后代码中统一写成pd.read_excel(China.xlsx, engineopenpyxl)和to_excel(..., engineopenpyxl)。显式声明引擎虽然啰嗦但避免了版本行为差异导致的各种玄学报错。5.3 现象HTML页面打开后图表空白控制台报ECharts加载失败原因页面模板里script标签引用的是远程CDN路径比如https://cdn.jsdelivr.net/npm/echarts别忘了有些内网环境或者本机没有外网加载就会超时失败图表区域自然空白。解决把ECharts库下载后放到项目的static目录script标签改成相对路径./static/echarts.min.js。用离线文件虽然会占用一点项目体积但保证在任何网络环境下打开都秒出图。这也是从“自己机器能跑”到“交付到别人机器也能跑”的一道分水岭。5.4 现象预测曲线在某个点突然断崖式下跌或跳变原因原始CSV里有缺失日期比如某一天中国数据没更新groupby聚合之后那一整天就消失了。回归模型的X是连续整数天数y却是跳跃的中间空缺一天就会让拟合线产生巨大偏移。解决读取后做一次日期补全把缺失日期自动填充为零值。df[date] pd.to_datetime(df[date]) df df.set_index(date).resample(D).sum().reset_index() # resample(D)按天重采样缺失日期的所有列自动补0resample(D)是pandas时间重采样的核心方法sum()决定缺失期怎么聚合。这个操作放在读文件和构造day列之间预测结果会平滑很多。5.5 现象脚本跑完了、HTML文件也更新了浏览器打开还是旧数据原因浏览器缓存了上一次的HTML文件服务器返回的是缓存内容你的新文件根本没被加载。解决开发阶段在浏览器按CtrlF5强制刷新或者在HTML的head区域加入一行禁用缓存的meta标签发布时再删掉。meta http-equivPragma contentno-cache meta http-equivCache-Control contentno-cache这组标签只对浏览器本地缓存生效不会影响服务器端配置适合开发调试阶段快速验证效果。6. 把预测脚本改成常用工具数据源替换与图表演进技巧6.1 替换成实时数据源项目里用的history.csv是静态文件但实际使用中数据每天都会更新。改动最小的方法是用Python直接去公开接口拉数据再落成本地CSV替代原来的读文件逻辑。# 从接口拉数并更新本地缓存 import requests import pandas as pd resp requests.get(https://your-data-api.example.com/covid) data resp.json() df pd.DataFrame(data) df.to_csv(history.csv, indexFalse, encodingutf-8) print(f数据已更新共 {len(df)} 行)requests.get默认会阻塞直到响应返回接口如果比较慢建议加超时参数timeout30避免脚本长期卡住。拿到JSON之后转DataFrame这步是通用的不管接口返回什么结构先用normalize或json_normalize展平嵌套字段再落盘。6.2 改图表类型的技巧如果不想用折线图想换成柱状图或者面积图只需要改ECharts的option配置。ECharts的series类型字段type决定图表形态从line改成bar即可。// 图表配置中的series部分 series: [{ name: 中国新增, type: bar, // line改成bar就是柱状图 data: [120, 200, 150, 80, 70] }]改图表类型时颜色、坐标轴、图例这些配置都不用动ECharts会自动适配。如果你要做大屏展示series里再加一个smooth: true折线会变成平滑曲线视觉上专业很多。6.3 用本地服务器固定访问方式以前我总喜欢双击HTML文件直接看后来发现文件协议下ECharts的字体加载和异步请求经常出问题就改成固定起一个本地服务器。# 在项目根目录起服务端口8080 python -m http.server 8080这个命令会把当前目录作为根目录开启HTTP服务局域网内其他电脑可以访问你的IP加端口直接看到所有HTML页面比U盘拷贝给同事方便得多。从那以后我每次交付可视化项目都会顺带在readme里写一句最简单的启动命令希望帮到你。本文还有配套的精品资源点击获取