ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python疫情数据分析课设全流程:从数据清洗到可视化大屏

Python疫情数据分析课设全流程:从数据清洗到可视化大屏 简介新冠肺炎疫情数据分析与可视化Python课程设计项目面向计算机、电子信息工程与数学等专业学生可作为课程设计、期末大作业或毕业设计参考内容完整且质量较高曾获导师认可。压缩包共57个文件总大小3.94MB包含17个py脚本、2个ipynb笔记本、5份csv数据集、多张png图表与HTML可视化页面以及Word版设计报告Python脚本覆盖微博数据爬取、中文分词、情感分析、逻辑斯蒂模型预测、地图绘制等环节ipynb呈现分析过程HTML用于展示交互图表。目前已有424人学习下载。整个项目以真实疫情数据为对象演示了从数据抓取、清洗建模到可视化展示的完整链路可直接对照源码和报告学习适合想快速搭建同类数据分析课设或毕业设计框架的读者。1. 疫情数据分析课设卡死人的不是画图是数据源新冠肺炎疫情数据分析这几年成了 Python 课设里曝光度最高的题目数据维度多、话题熟悉、可视化效果好导师也认可选题价值。但以我带课设和参与评审的经验能一次通过的项目不到三成多数人卡在同一处——数据源口径不对导致后边所有图表和分析都立不住。这篇文章按数据准备、指标体系、可视化、报告答辩四个环节给出一条可以直接照做的完整路径每一步都有能运行、能改参数的完整代码。适合刚开始做数据分析课设、或者代码写到一半中途翻车的人。目标只有一个让你把代码跑通报告写得像专业分析拿到一个应得的成绩。2. 疫情数据准备从原始 CSV 到可分析的 SQLite 库数据准备在课设里容易被低估。疫情数据量真的不大——全球一两万条记录、国内三千条上下完全用不着数据集群但它脏。日期不统一、省份名混用、新增值对不上不同数据源的口径差别大得能让同一天的分析结果差出几百例。这一章的核心是先把数据变成一份“谁都能复核”的中间产物后续分析和报告都在这份产物上展开不让矛盾的数据同时出现在代码里。2.1 选数据源先看三个口径字段、粒度、更新频率选数据源我只看三个东西。字段上课设至少要覆盖确诊、死亡、治愈、新增这四项缺了治愈或新增治愈率、增幅分析全都补不回来。粒度上有国家、省/州、城市三级课设推荐做到省级城市级数据太多天不全空值经常超过三成清洗时间成本翻倍。更新频率上能固定住一份本地快照比什么都重要。常见做法是用公开的数据仓库GitHub 上有不少每日更新的疫情数据仓库下载即用。但我建议先确认它是否保留了历史回溯数据而不是只有当天值。如果你处于 Python 入门阶段我更建议先找一份结构化 CSV把 pandas 清洗流程跑通再去考虑爬虫或接口。全网找一个可下载的直链 CSV 永远比写爬虫稳。数据下载到本地之后第一步不是分析是确认形态import pandas as pd df pd.read_csv(daily.csv, encodingutf-8-sig) print(df.shape) # 行数、列数 print(df.info()) # 字段类型、缺失情况 print(df.head(3)) # 前 3 行肉眼确认表头含义这一步干三件事shape 告诉你数据量级info 暴露列类型和空值分布head(3) 是让你人眼核对“日期、省份、数值”是不是按你想的格式进来的。很多人在这一步跳过去直接 groupby等到画图才发现日期是字符串、省份带后缀返工成本反而更高。选源时有个原则宁可字段少一点也要口径稳定。有些数据源把全球和国内混在一个表里有些分开选后者。全球数据里地区写法复杂国内数据清洗规则干净对课设来说更好把握。2.2 清洗就是要对齐国家、省份、日期三个主键清洗是代码量最大的地方。疫情数据最常见的脏问题是三类日期有时是字符串有时是时间戳省份有的带“省/市/自治区”后缀有的不带某些天数值缺失被 pandas 读成 NaN。清洗目标只有一个把数据统一成“一行代表某天某个地区”的记录。下面的函数一次处理四类问题def clean_covid(df): # 1. 日期统一为 datetime 类型便于后续做时间序列聚合 df[date] pd.to_datetime(df[date]) # 2. 省份名去后缀保证“湖北”和“湖北省”落到同一组 df[province] df[province].astype(str).str.replace( 省|市|自治区|特别行政区, , regexTrue ) # 3. 缺失值处理确诊为空按 0 补不要整行 drop df df.fillna({confirmed: 0, deaths: 0, recovered: 0}) # 4. 按国家省份日期排序保证同一地区记录在时间上连续 df df.sort_values([country, province, date]).reset_index(dropTrue) return df df clean_covid(df)日期用pd.to_datetime统一以后pandas 才能识别“2020-03-01”和“2020/3/1”是同一天。省份去后缀用正则替换只去掉结尾出现的后缀不会误伤本来就短名称的省份。fillna 填 0 而不是 dropna是因为某天某省没有上报就等于 0 例删掉这一行会让时间轴出现空洞折线图直接断掉。清洗后马上要生成一个关键衍生列——新增确诊。数据源一般只给累计值新增要靠自己差分df df.sort_values([province, date]) df[new_confirmed] df.groupby(province)[confirmed].diff().fillna(0) df df[df[new_confirmed] 0]groupby(province)[confirmed].diff()的含义是同一个省内后一天累计减前一天累计得到当天新增。fillna(0) 把每个省第一条记录的空差分变成 0。最后过滤负值很重要有些数据源会把前一天漏报的补进今天导致 diff 算出负数画折线图时会出现一根向下的尖刺。2.3 用 SQLite 落库为分析和报告省下反复读文件的功夫清洗完的数据如果每次从 CSV 重新算写到第三周你就会发现自己在重复劳动。我的习惯是清洗完直接写进 SQLite后续分析统一从库里取数保证数据只有一份。import sqlite3 conn sqlite3.connect(covid.db) df.to_sql(covid_daily, conn, if_existsreplace, indexFalse) conn.commit() conn.close()if_existsreplace表示每次执行都重建表适合清洗逻辑还没定稿的阶段indexFalse避免把行号写进数据库否则读出来会多一列无意义的索引。写入之后每次新开 notebook 只需要一句读库命令def load_from_db(): conn sqlite3.connect(covid.db) return pd.read_sql(SELECT * FROM covid_daily, conn)把数据落到 SQLite 不是炫技是为了交付。课设包里要求附数据说明时你可以在报告里写“清洗后的数据存入 covid.db共 X 行覆盖 Y 个地区、Z 天时间跨度”。这几个 X、Y、Z 一查即得比在报告里写“数据经过了预处理”有说服力得多。3. 疫情分析指标体系先定指标再写代码报告才有话可说很多课设报告写不长不是文笔问题是指标太少。翻开正文就是一句“病例总体呈上升趋势”然后贴三张图没了。这一章先把疫情分析能用的指标全部列出来再给出可直接复用的指标计算函数。指标跑出来报告的文字素材自然就有了。3.1 五个核心指标与两个衍生指标做疫情数据分析不能只盯着绝对数。“累计确诊”会一路涨涨到后面对比的实际是“增长速度”。课设阶段覆盖下面几类指标就够用了| 指标 | 公式/口径 | 说明什么 | | 累计确诊 | 数据源直接给出 | 疫情整体规模 | | 新增确诊 | 当日累计 - 前一日累计 | 疫情实时进展最重要 | | 累计治愈 | 数据源直接给出 | 医疗救治效果 | | 累计死亡 | 数据源直接给出 | 疫情严重程度 | | 病死率 | 累计死亡 / 累计确诊 | 病毒危险性 | | 治愈率 | 累计治愈 / 累计确诊 | 收治与康复进展 | | 环比增幅 | (当日新增 - 前日新增) / 前日新增 | 判断疫情是否迎来拐点 |病死率在不同国家的口径差异很大有的只统计确诊后死亡有的把疑似也计入。课设不需要统一全球口径但必须在报告里写清楚自己用的是哪一套、数据源怎么定义。写清了这一点本身就是分析的体现。指标计算建议封装成独立函数避免在分析与可视化代码里反复重写公式def add_indicators(df): result df.sort_values([province, date]).copy() result[new_recovered] result.groupby(province)[recovered].diff().fillna(0) result[new_deaths] result.groupby(province)[deaths].diff().fillna(0) result[mortality_rate] result[deaths] / result[confirmed] result[recovery_rate] result[recovered] / result[confirmed] return result这里第一行的.copy()非常重要。pandas 里 sort_values 返回的是视图而非副本后面改了数据可能污染原 DataFrame排查起来极其痛苦。mortality_rate 用的是累计值不会瞬间被很小的分母放大但数据量少时依然可能出现 inf建议计算后统一替换import numpy as np result[mortality_rate] result[mortality_rate].replace([np.inf, -np.inf], np.nan)3.2 时间维度分析7 日移动平均怎么给日增数据去噪日新增这一列噪音非常大。双休日通报少、周一周二集中补报表现在折线图上就是锯齿。直接拿锯齿说“今天反弹了”很容易得出错误结论。标准解法是移动平均疫情分析惯例用 7 日窗口正好能消掉以周为周期的上报波动。ts ( df[df[country] China] .groupby(date)[new_confirmed] .sum() .reset_index() ) ts[ma7] ts[new_confirmed].rolling(window7, min_periods3).mean() ts[ma14] ts[new_confirmed].rolling(window14, min_periods7).mean()rolling(window7)表示向前取 7 天窗口做均值。min_periods3是说开头不足 7 条时有 3 条就算曲线前段不至于被画成空白。ma7 看短期趋势ma14 看中期趋势两条线加原始新增放一起本身就是报告里最有说服力的一段描述。时间序列分析还有一个高频翻车点groupby(date) 之前必须确认 date 是 datetime 类型否则“2021-02-01”会被排在“2021-02-10”后面。字符串排序和日期排序在跨月时必定不一致。这就是第二章清洗时统一 to_datetime 的原因在这里直接规避掉一个潜在错误。3.3 地域维度分析TopN 排名和省份对比的写法除了全国趋势地域对比是疫情课设第二个大头。标准做法是取某个时间点的数据做省份 Top10再叠加“新增量突出”的省份体现热点转移。代码结构如下last_date df[date].max() today df[df[date] last_date] # 累计确诊 Top10 top10 ( today.groupby(province)[confirmed] .sum() .sort_values(ascendingFalse) .head(10) ) # 当日新增 Top10 new_top10 ( today.groupby(province)[new_confirmed] .sum() .sort_values(ascendingFalse) .head(10) )groupby(province)[confirmed].sum()在已经按省份分好的数据里等价于直接取列值但写成 sum 能兼容“省内有多条城市记录”的粒度通用性更稳。sort_values 降序head(10) 截断。Top10 我一般输出成表格放进报告图表留给时间趋势地域维度用图展示时适合配地图热力。提醒一个语义问题新增 Top10 的时间点如果撞上数据源补报某省可能突然出现几千“新增”那是历史补登而非当日真实疫情。做任何地域对比之前先看这个省前一周的趋势图确认没有异常脉冲再下结论。4. 可视化实现从 matplotlib 静态图到 pyecharts 可视化大屏可视化是课设里占比重最大、也是容易“金玉其外”的部分。评阅老师看过的大屏比你多得多与其堆效果不如把每张图讲清楚为什么选它它揭示了什么。这一章从 matplotlib 的静态图起步再叠一套 pyecharts 大屏两层都给了能跑的代码。4.1 图表选型时间序列、排名、分布各用哪一类图选图表的第一原则不是好看是数据关系匹配。疫情课设里的关系无非四种| 想表达的关系 | 适合的图表 | 课设里的例子 | | 时间趋势 | 折线图 | 全国新增确诊 7 日均线走势 | | 大小排名 | 横向条形图 | 各省累计确诊 Top10 | | 构成占比 | 饼图/环形图 | 各大洲确诊占比 | | 空间分布 | 地图着色 | 各省累计确诊热力 |两张以上的图要围绕一条叙事线展开“趋势看时间、分布看地域”。饼图能少用就少用分类超过 5 个时肉眼很难分辨 26% 和 31% 的差别。比例类分析优先用条形图替代饼图信息传达更准确。4.2 matplotlib 三张静态图中文字体和图例提前处理matplotlib 是课设最稳的底牌不用装服务器、单张 PNG 直接插报告、社区资料最多。唯一必须提前处理的是中文字体和负号。先做全局配置import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [ SimHei, Microsoft YaHei, PingFang SC, WenQuanYi Zen Hei ] plt.rcParams[axes.unicode_minus] False # 负号不显示成方块 plt.rcParams[figure.dpi] 120 # 导出清晰度字体列表是回退关系Windows 优先黑体macOS 用苹方Linux 用文泉驿正黑。axes.unicode_minus必须单独设否则坐标轴负号变成方块。dpi 设 120插到报告里足够清晰。核心图“全国新增 7 日移动平均”这样画fig, ax plt.subplots(figsize(12, 5)) ax.plot(ts[date], ts[new_confirmed], color#cccccc, linewidth1, label每日新增) ax.plot(ts[date], ts[ma7], color#d62728, linewidth2, label7日移动平均) ax.set_title(全国新冠疫情每日新增确诊与7日移动平均) ax.set_xlabel(日期) ax.set_ylabel(新增确诊人数) ax.legend() fig.autofmt_xdate() # 日期标签自动旋转避免重叠 fig.savefig(images/trend_new_confirmed.png, bbox_inchestight)fig.autofmt_xdate()自动旋转日期轴标签不写这一行40 多个日期挤在一行时必然重叠。bbox_inchestight裁掉图四周多余白边报告排版更干净。4.3 pyecharts 大屏多图同屏与数据刷新方案如果课设要求有可视化大屏pyecharts 是最省事的方案直接生成 HTML、浏览器打开、自带悬浮 tooltip不需要部署服务器。用 Grid 组件把“全国趋势 省份排名 地图分布”拼成一张大屏是疫情课设的标准布局。from pyecharts.charts import Bar, Grid, Line, Map from pyecharts import options as opts line ( Line() .add_xaxis([d.strftime(%m-%d) for d in ts[date]]) .add_yaxis(新增确诊, ts[new_confirmed].tolist(), is_smoothTrue) .set_global_opts(title_optsopts.TitleOpts(title全国新增确诊趋势)) ) bar ( Bar() .add_xaxis(top10.index.tolist()) .add_yaxis(累计确诊, top10.values.tolist()) .set_global_opts(title_optsopts.TitleOpts(title省份累计Top10)) ) chart_map ( Map() .add(累计确诊, [list(z) for z in zip(today[province], today[confirmed])], china) .set_global_opts(visualmap_optsopts.VisualMapOpts(max_50000)) ) grid Grid() grid.add(line, grid_optsopts.GridOpts(pos_left55%, pos_top10%)) grid.add(bar, grid_optsopts.GridOpts(pos_left55%, pos_bottom10%)) grid.add(chart_map, grid_optsopts.GridOpts(pos_left10%, pos_right45%)) grid.render(output/dashboard.html)三个关键点。第一pyecharts 的 x 轴要求字符串列表日期必须用strftime(%m-%d)转成短格式否则会显示“2020-03-01 00:00:00”。第二Map 的“china”地图由 pyecharts 自带地图包提供如果运行报“未找到地图”把 pyecharts 和相关地图扩展重装一遍即可。第三VisualMapOpts(max_50000)要根据当前数据的最大值来定设小了全国一片深色设大了看不出区分。先看 top10 第一名的量级再定值。用 Grid 布局时各图边框会互相挤压。我的做法是先把三张图分别单独 render 确认不出错再组合成 dashboard组合后只查“有没有图被挤出可视区”不再动数据源缩小排错范围。5. 课设避坑最容易让项目翻车的五处细节数据课设翻车概率最高的点往往不是分析逻辑而是环境、路径、编码这类小地方。以下踩坑记录几乎每学期都会遇到写成“现象-原因-解决”直接对着排。5.1 现象read_csv 报错或读出来多一列最典型的是FileNotFoundError和第一列出现Unnamed: 0。前者十有八九是路径带中文或反斜杠——Windows 的“\”在 Python 字符串里是转义符路径读一半就断了。解决方式统一用正斜杠和 Path 对象from pathlib import Path data_path Path(D:/course_design/covid/daily.csv) df pd.read_csv(data_path, encodingutf-8-sig)Path 对象隔离了跨平台路径拼接问题正斜杠省去所有转义烦恼。Unnamed: 0的出现是因为数据源第一列本身是行号存储时加indexFalse读取时用index_col0把它指定为索引即可。5.2 现象图里的中文全变方块负号消失中文字体问题在 Windows 上不太明显一换到 Linux 服务器或 Docker 环境就集体翻车。原因是系统里没有中文字体或者 matplotlib 字体列表里没有可回退项。解决链路不只是改 rcParams还要确认系统真装了字体。Windows 一般自带 SimHeimacOS 有苹方Linux 需要手动装文泉驿sudo apt install fonts-wqy-zenhei fc-cache -f装完重开 jupyter 内核乱码基本消失。如果换电脑又乱码不要改代码先查字体环境——rcParams 已经配置到位问题大概率在系统字体缺失。5.3 现象答辩前数据源更新了所有数字对不上报告在线数据是流动的报告是静止的。启动课设时下载的数据三周后和最新数据相差很大评阅老师拿当前数据对照你的报告数字必然对不上。解法是课设第一天就固定数据快照分析脚本只读快照cp daily.csv data_snapshot_20240301.csv快照文件放进课设包报告里注明“数据来源为 2024 年 3 月 1 日快照”。整个课设内部自洽老师认可“固定数据”这个意识反而比“用最新数据”更像专业做法。5.4 现象代码能跑但报告里的图和现场输出不一致这是“改了代码没重新跑图”导致的。报告贴的是上周的旧图代码是这周又调整过的版本数字对不上是必然。解决方式报告里每一张图必须在定稿日重新生成不允许拿旧图凑数。输出图片统一放到 images 目录文件名带版本号答辩提交时把脚本和输出图一起打包保证可追溯。5.5 现象想爬官网数据结果被反爬拦截有人把疫情课设理解成“爬虫课设”上来就抓网页被抓后转去网上找现成压缩包。这里明确一点课设评分核心是分析不是爬虫。与其破解网页不如先找结构化 CSV 或公开接口。如果确实要用接口按文档规范请求带好请求头、控制频率import requests headers {User-Agent: Mozilla/5.0 (course-design-study)} resp requests.get(api_url, headersheaders, timeout10) data resp.json() # 接口返回 JSON 时直接解析不要手撸字符串带 User-Agent、设置超时、频率压到 1 次/秒以下是对公开数据源的基本尊重程序也能稳定跑完。凡是返回 JSON 的接口直接.json()接住不要自己去解析字符串嵌套层数多时极易写错下标。6. 报告与答辩课设成绩的得分点不在代码本身代码能跑只是及格线真正拉开分数的是报告能不能把“我做了什么”讲清楚。6.1 报告结构从背景到结论的七段式报告按七个部分组织选题背景与意义数据说明数据源、口径、清洗规则分析指标体系时间维度分析地域维度分析可视化系统说明结论与不足。核心原则是一条每张图都必须有归属。报告中每贴一张图紧跟两段话——这张图说明了什么规律为什么会呈现这个规律。只贴图不解释等于白做。6.2 答辩前自检把“跑通代码”变成“能回答为什么”答辩提问集中在三类为什么这样清洗、为什么选这些指标、图表结论是否稳健。自检三问数据快照日期能一眼找到吗新增确诊是数据源直接给的还是自己 diff 算的Top10 排名如果把指标换成治愈率结论会不会反过来第三问最常被追问提前想好答案等于给答辩上了一道保险。6.3 版本对齐报告里的图和代码输出保持一致最后一个习惯答辩前一天把代码重跑一遍生成所有图表覆盖到报告目录再打包整个课设。报告引用的图、数据库里的数据、脚本生成的图三者对照一致。被问“这张图的数据是哪天的”当场能打开数据快照翻到同一行记录这个动作比任何口头解释都有说服力。血泪经验早期总把时间花在美化界面、堆动态组件上最后评审只问了一句“这个趋势为什么这么走”就答不上来。把数据清洗讲清楚、指标算明白、每张图背后都有结论比十个大屏组件都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表