ARTICLE DETAIL

资讯详情

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

疫情数据分析课设实战:从Pandas清洗到可视化呈现的完整指南

疫情数据分析课设实战:从Pandas清洗到可视化呈现的完整指南 简介面向新冠肺炎疫情数据分析与可视化的Python完整课设资料包主要适用于计算机、电子信息工程、数学等专业大学生可作为课程设计、期末大作业或毕业设计的参考资料。项目基于真实疫情数据完整覆盖数据采集、清洗、时序预测、微博评论情感分析与多种可视化方案代码和报告经导师指导后获得98分参考价值较高。压缩包共57个文件总大小3.94MB包含17个Python脚本、2个Jupyter Notebook、5个CSV数据集、5个HTML页面、16张PNG图片以及docx报告、词云JS、Markdown说明等目录结构清晰便于按模块查阅复用。当前已有424人学习。借助完整源码和报告读者可快速掌握爬虫、jieba分词、TF-IDF、Logistic回归、地图与折线图绘制等核心技术直接将工程范式迁移到自己的课设或毕业设计场景中。1. 一张能扛住答辩的疫情数据分析课设看完这篇你就知道工作量在哪“新冠肺炎疫情数据分析与可视化”这个题目几乎是 Python 课设里的常青树。每年都有大量学生选它以为自己会 Pandas 读个 CSV、Matplotlib 画个折线图就完事结果答辩时被老师一句“你这个图说明什么问题”问得哑口无言。这门课设真正考察的从来不是你会不会调用plt.plot()而是你能否完成一条完整的数据分析链路——从数据获取、清洗、聚合到可视化呈现和结论提炼。本文要拆解的这份“完整课设和代码报告”方案本质上是给你一套可以直接复现的落地流程数据源选型、Pandas 核心操作、可视化图表规划、报告写作框架以及最容易被忽视的踩坑点。适合正在做 Python 数据分析与可视化课设、但不太确定从哪里下手的同学。读完你不需要再到处找示例代码照着做就能搭出你自己的版本。2. 数据从哪来、怎么装疫情数据源选型与 Pandas 读取的完整流程2.1 疫情数据集的三种常见来源不是所有数据都适合直接拿来做课设做疫情数据分析第一步不是写代码而是找数据。数据源选错了后面所有分析都是空中楼阁。我做过不少数据分析项目也帮人看过课设代码发现大家最常犯的错是用别人二次处理过的 Excel 表字段没有统一说明日期格式乱七八糟分析到一半才发现数据根本对不上。常见做法是这样。第一类WHO 和 Johns Hopkins 的开源仓库。它们提供 CSV 或 JSON 格式的全球疫情日更数据字段命名规范比如Country/Region、Confirmed、Deaths、Recovered不同国家地区的数据都有。这类数据的优点是覆盖时间长、格式稳定适合做全球维度的趋势分析。缺点是字段名是英文国内课设通常需要翻译或重命名而且数据量大如果不做聚合Pandas 读取后运行会明显卡顿。第二类国内主要做的是国家卫健委和各省卫健委的公开数据通常以 JSON 接口的形式提供但接口经常变动需要爬虫不断调试。课设时间有限不建议把精力耗在写爬虫上——除非你的题目明确要求必须爬取实时数据。第三类GitHub 上有不少“疫情可视化”项目的数据集比如国内某个城市的确诊病例明细或者某段时间的境外输入数据。这些数据常用于数据分析工程实践拿来练手可以但如果报告里要写“数据来源于某某仓库”一定要确认数据的更新日期和采集方式别拿一个 2020 年就停更的数据集硬撑到 2023 年答辩时经不起追问。我一般建议课设选第一类或第三类因为 CSV 和 JSON 读取稳定不依赖网络。你甚至可以只挑一个国家比如中国或一个省份的数据做深入分析避免全球数据量过大导致可视化交互卡死。2.2 读取 CSV 和 JSON 的最小可运行代码解析日期列和字段重命名确定数据源后先写一个最小读取脚本。假设你下载了 Johns Hopkins 的time_series_covid19_confirmed_global.csv这是全球累计确诊数的时序数据。import pandas as pd # 读取全球累计确诊数据 file_path time_series_covid19_confirmed_global.csv df pd.read_csv(file_path) # 查看前5行确认字段结构 print(df.head()) # 查看列名和缺失值 print(df.columns.tolist()) print(df.isnull().sum())这段代码只做三件事读文件、看结构、看缺失情况。很多课设翻车就在这一步——你没有先看数据长什么样就急着画图结果画出来的折线图 x 轴全是日期字符串y 轴是字符串拼接图出来全是乱码。df.head()和df.columns.tolist()是最廉价的调试手段。接下来是字段重命名和日期标准化。Johns Hopkins 的数据里前几列是Province/State、Country/Region、Lat、Long后面每一列是一个日期字符串如1/22/20。这种宽表格式方便人阅读但 Pandas 处理起来非常别扭——你没法直接按日期分组。所以要把宽表转换成长表# 将宽表转为长表id_vars是保留的标识列var_name是新列名value_name是数值列名 df_melted df.melt( id_vars[Province/State, Country/Region, Lat, Long], var_namedate_str, value_nameconfirmed ) # 将日期字符串转换为真正的日期类型 df_melted[date] pd.to_datetime(df_melted[date_str], format%m/%d/%y) # 重命名列方便后续代码阅读 df_melted.rename(columns{ Province/State: province, Country/Region: country }, inplaceTrue) print(df_melted.head())参数说明要记清楚melt里的id_vars是你不希望被折叠的列var_name和value_name是自己起的列名一般叫date和valuepd.to_datetime的format参数必须和源数据里的1/22/20完全对应否则 Pandas 只能用默认解析器去猜速度慢而且容易出错。这里改成%m/%d/%y是因为美国日期格式是月/日/年。2.3 数据目录规划一个不容易被老师翻出问题的项目结构数据读取只是开始更实际的问题是项目管理。课设交付时老师会看你的目录结构一个乱七八糟的目录会拉低印象分。我一般这样组织covid19_analysis/ ├── data/ │ ├── raw/ # 原始数据只读不写 │ └── processed/ # 清洗后的数据 ├── notebooks/ # Jupyter Notebook 分析过程 ├── src/ │ ├── data_loader.py # 数据读取 │ ├── data_clean.py # 清洗逻辑 │ └── visualization.py # 画图函数 ├── figures/ # 输出的图表 ├── report/ │ ├── 报告.md │ └── 答辩PPT.md └── requirements.txt这个结构不是拍脑袋定的。data/raw放原始文件确保你随时能回溯src把代码按职责拆开别把 500 行代码全塞进一个main.py老师看代码时看到你按函数拆分至少能说明你有模块化意识。requirements.txt里写清楚依赖版本比如pandas2.0.3、matplotlib3.7.2、pyecharts2.0.5这样换一台电脑也能复现。3. 数据清洗与聚合从混乱的原始表到可分析的结构化数据3.1 处理缺失值和重复行疫情数据里最常见的“隐形地雷”疫情数据不是完美样本缺失值和重复行是最常遇到的问题。Johns Hopkins 的仓库里很多省份的Province/State列是 NaN比如中国的数据就统一放在“China”下。如果你不去管这些缺失后面按国家分组时会得到一堆奇怪的键。# 处理缺失值如果省份为空直接用国家名填充 df_melted[province] df_melted[province].fillna(df_melted[country]) # 删除完全重复的行 df_clean df_melted.drop_duplicates() # 检查每个国家的数据条数确认没有明显异常 country_counts df_clean.groupby(country).size() print(country_counts.head(20))逻辑说明fillna用国家名填充省份保证每条记录都有地区标识drop_duplicates清理原始文件里可能存在的重复行。这里有个细节——drop_duplicates默认是整行完全重复才删如果你只想去重某几列需要传subset参数比如drop_duplicates(subset[country, date])。疫情数据是时间序列同一国家同一日期不应该出现两行。参数说明fillna的method参数也有讲究比如methodffill向前填充适合补时序数据的缺失值但不适合这种需要按列填充的场景。所以这里直接传值是最直观的。3.2 计算每日新增、死亡率与增长率别把累计值当增量用这是整个课设里最关键的一步也是很多人的认知盲区。原始数据里通常只有累计值比如累计确诊但你想分析“每日新增”不能用累计值直接画趋势线需要自己算差分。# 先按国家日期排序排序是差分计算的前提 df_sorted df_clean.sort_values([country, date]) # 计算每日新增确诊groupby之后用diff是当前行减去上一行 df_sorted[daily_confirmed] df_sorted.groupby(country)[confirmed].diff() # 计算死亡率累计死亡 / 累计确诊 df_death pd.read_csv(time_series_covid19_deaths_global.csv) # 转换和merge过程略只展示计算部分 df_sorted[death_rate] df_death[deaths] / df_sorted[confirmed] * 100 # 计算环比增长率 (今天累计 - 昨天累计) / 昨天累计 df_sorted[growth_rate] df_sorted.groupby(country)[confirmed].pct_change() * 100 # 将第一个值diff结果NaT填充为0 df_sorted[[daily_confirmed, growth_rate]] df_sorted[ [daily_confirmed, growth_rate] ].fillna(0) print(df_sorted.head(10))逻辑说明groupby(country)[confirmed].diff()的意思是对每个国家的累计确诊列做一阶差分得到的就是每日新增。pct_change()得到的是相对上一个日期的变化百分比这个指标用来判断疫情是处于扩散期还是控制期非常有用。死亡率必须用deaths / confirmed顺序别搞反。避坑提醒diff()的结果在第一行一定是 NaN因为第一行没有“上一天”。如果你不fillna(0)后续画图时这些空值会被 Matplotlib 默认跳过但如果在 Excel 里打开处理后的 CSV你会看到一堆空白——老师看到这种数据会认为你连基本的数据处理都没做。3.3 按国家/地区聚合全球对比分析的 pivot_table 操作当你需要对比几个国家的疫情趋势时光有长表还不够要把它转成适合对比的宽表格式。pivot_table是这里最常用的工具。# 选取几个重点国家做对比 countries_of_interest [China, US, India, Brazil, United Kingdom] # 生成以行为日期、列为国家的累计确诊矩阵 pivot_confirmed df_sorted[df_sorted[country].isin(countries_of_interest)].pivot_table( indexdate, columnscountry, valuesconfirmed, aggfuncmax ) # 对缺失值做向前填充然后删除全空的行 pivot_confirmed pivot_confirmed.ffill().dropna(howall) print(pivot_confirmed.tail(5))参数说明pivot_table的index是你希望作为行索引的列columns是希望变成列标题的列values是要聚合的数值列aggfunc取决于你的数据粒度。这里用了max因为同一日期同一个国家可能有多条记录取最大累计值避免重复。ffill()是向前填充如果某天没有数据就用前一天的值补上保证画折线图时不会断线。这里特别提醒你的课设报告里一定要写清楚每个字段的计算口径比如“每日新增 今日累计 - 昨日累计”不要只放一张图。答辩老师很可能就问“你这个每日新增是怎么算的”答不上来就尴尬了。4. 可视化方案设计疫情分析到底该画哪几张图、怎么画4.1 Matplotlib 画折线趋势图中文乱码与日期轴格式化的完整解法拿到结构化数据后开始画图。第一张图通常是“重点国家累计确诊趋势对比”用折线图最直观。import matplotlib.pyplot as plt import matplotlib.dates as mdates # 解决中文乱码指定中文字体 plt.rcParams[font.sans-serif] [SimHei] # Windows 用 SimHeiMac 用 PingFang SC plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题 # 创建画布设置尺寸让x轴刻度有足够的空间 fig, ax plt.subplots(figsize(12, 6)) # 逐个国家画折线 for country in countries_of_interest: if country in pivot_confirmed.columns: ax.plot( pivot_confirmed.index, pivot_confirmed[country], labelcountry, linewidth1.8 ) # 设置x轴为日期格式每隔30天显示一个刻度 ax.xaxis.set_major_locator(mdates.MonthLocator(interval1)) ax.xaxis.set_major_formatter(mdates.DateFormatter(%Y-%m)) ax.set_title(主要国家新冠疫情累计确诊数趋势对比, fontsize14) ax.set_xlabel(日期) ax.set_ylabel(累计确诊人数) ax.legend(locupper left) ax.grid(alpha0.3) # 旋转x轴刻度避免重叠 plt.xticks(rotation45) plt.tight_layout() plt.savefig(figures/top_countries_trend.png, dpi300, bbox_inchestight) plt.show()这段代码是课设里比较核心的部分。中文乱码是几乎所有做 Python 数据可视化课设时都会遇到的问题——plt.rcParams[font.sans-serif] [SimHei]这行代码就是治它的但前提是你的系统里装了 SimHei 字体。如果你在 Linux 服务器上跑这个字体可能不存在可以用fc-list :langzh查看系统可用中文字体或者改用Noto Sans CJK SC。MonthLocator(interval1)让 x 轴每 1 个月显示一个刻度一方面避免刻度太密另一方面也符合疫情的长期趋势分析节奏。dpi300保证保存的图片清晰不要用默认 100答辩 PPT 投影出来会糊。如果你觉得累计确诊对比太普通可以换一张“每日新增确诊对比”。只需把valuesconfirmed换成valuesdaily_confirmed语义完全不一样——累计量看趋势定调新增量看拐点和波动两张图配合使用才有说服力。4.2 Pyecharts 画中国地图和全球地图省市数据如何匹配到地理坐标折线图是基础但课设要有亮点建议加一张地图。Pyecharts 是做交互可视化比较顺手的库它可以在 Notebook 里直接渲染出支持鼠标悬停的 HTML 页面生成的是独立的 HTML 文件不用依赖 Python 环境就能打开。from pyecharts.charts import Map from pyecharts import options as opts # 假设你已经有一份各省累计确诊的数据province_list 和 confirmed_list province_list [广东, 河南, 湖北, 浙江, 湖南] confirmed_list [2500, 1800, 68000, 1500, 1200] # 创建地图实例 map_chart Map() map_chart.add( series_name累计确诊, data_pairlist(zip(province_list, confirmed_list)), maptypechina, is_map_symbol_showFalse ) map_chart.set_global_opts( title_optsopts.TitleOpts(title中国部分地区疫情分布), visualmap_optsopts.VisualMapOpts(max_5000, is_piecewiseTrue) ) map_chart.set_series_opts( label_optsopts.LabelOpts(is_showFalse) ) # 输出成独立的HTML文件 map_chart.render(figures/china_map.html)这里有一个非常容易踩的坑Pyecharts 的省份名称映射。它识别的是“广东”这样的标准名称如果你数据里写的是“广东省”地图上就显示不出来如果写“湖北”它会映射到“湖北”而不是“湖北省”的图形区域。1.0 版本之后 Pyecharts 对名称匹配的容错比前面的版本好一些但仍然建议先把省份列清洗成标准名称比如去掉“省/市/自治区”后缀再把“内蒙古”、“广西”、“新疆”等特殊名称单独核对一遍。验证方法很简单渲染完 HTML 后打开看一眼哪个区域没上色就是名称没匹配上。地图并非必须每张都做。如果你的题目重点是国家趋势分析一张全球地图或中国地图做点缀就够把精力花在解释数据上更有价值。4.3 热力图与堆叠柱状图关注哪些场景适合用哪些是误导疫情分析里还有一个常见需求按“月份 × 国家”看新增确诊的分布。这种二维关系用热力图比较合适用颜色深浅表达数值大小。import seaborn as sns # 提取年-月字段用于聚合 df_sorted[year_month] df_sorted[date].dt.to_period(M) # 按月和国家聚合新增确诊 pivot_monthly df_sorted[df_sorted[country].isin(countries_of_interest)].pivot_table( indexyear_month, columnscountry, valuesdaily_confirmed, aggfuncsum ) # 画热力图 fig, ax plt.subplots(figsize(10, 8)) sns.heatmap(pivot_monthly, annotTrue, fmt.0f, cmapYlOrRd, linewidths0.5, axax) ax.set_title(重点国家月度新增确诊热力图) plt.xticks(rotation45) plt.tight_layout() plt.savefig(figures/monthly_heatmap.png, dpi300) plt.show()逻辑说明先按月份切分数据再按国家和月份分组求和。热力图的行是月份列是国家格子里的数字是当月总新增。这张图能快速看出某个国家疫情的“高峰期”在什么时间段横向对比也很直观。cmapYlOrRd是黄色到红色的渐变数值越大颜色越深符合直觉。但要注意热力图不适合展示量级差异过大的数据。如果某个国家单月新增 500 万另一个国家只有 1000颜色会被最大值拉满小数值全是白色没有区分度。这种情况可以考虑对数据做对数变换或者改用分位数映射。我一般会在课上跟学生强调可视化项目的一个原则选图前先看数据分布而不是反过来。5. 疫情课设最容易翻车的 5 个坑从数据结构到图表呈现的常见问题排查5.1 坑一日期字符串排序出错折线图在时间轴上乱跳现象画图时 x 轴刻度的顺序不是按时间先后排的而是按字符串首字母排的比如 “10/1/20” 会排在 “2/1/20” 前面——因为字符串排序把 “1” 和 “2” 当成字符比较结果先排 “1” 开头的所有日期再排 “2” 开头的。原因读取 CSV 后日期列是字符串类型没有通过pd.to_datetime转换成datetime64类型。Pandas 不会自动识别日期列。解决在处理原始数据时立刻执行pd.to_datetime(df[date])转换后你可以用df.dtypes验证date列应该是datetime64[ns]而不是object。转完之后再排序才可靠。5.2 坑二累计值和新增值混用一张图里画了两个口径现象图例标题写着“每日新增”但折线的数据实际是累计值或者反过来。如果你同时画累计确诊和每日新增两条线两者的量级差了好几倍新增数据会被压在 x 轴上成为一条线。原因数据处理的中间变量被覆盖。常见场景是在清洗时执行了df[confirmed] df[confirmed].diff()或类似的重赋值操作之后想再画累计曲线发现数据已经不可逆了。解决不要用df[confirmed] ...这种方式覆盖原始列。新增一列df[daily] df.groupby(country)[confirmed].diff()保留原始列。另外画出图之后检查一眼纵轴最大值是否和已知数据吻合比如中国累计确诊应该在几十万量级比对着代码猜快得多。5.3 坑三中国地图显示不出来或者省份颜色全是一个色现象HTML 渲染后地图只显示灰色背景没有任何省份有颜色或者在 Notebook 里报错mapchinanot found。原因有两种可能性。第一种是 Pyecharts 没有注册中国地图的数据文件老版本的 Pyecharts0.5.x需要额外安装echarts-countries-js等地图扩展包新版本1.0虽然内置了部分地图但如果你的环境里缺少pyecharts-snapshot或网络无法加载地图脚本也可能显示不了。第二种是数据名称匹配不到省份名是全称、缩写或非标准写法地图组件的 geo 数据源里找不到对应用户输入。解决第一步检查 Pyecharts 版本import pyecharts; print(pyecharts.__version__)如果是 0.5.x去升级到 2.x。第二步固定地图脚本的加载方式直接把render(figures/china_map.html)生成的 HTML 文件用浏览器打开如果地图底图是白色但省份有颜色说明是 JS 加载问题检查网络或换成离线包。5.4 坑四Melt 之后数据条数翻倍内存爆炸现象原本几十兆的 CSV经过melt后数据量暴增到几百万行Pandas 处理明显变慢甚至 Jupyter Notebook 直接卡死。原因宽表转长表会让行数等于“原本的列数 - 标识列数”乘以“原本的行数”这是结构性正常现象。但如果你做了一次melt之后又对结果再做一次melt或者对多个宽表反复拼接就会指数级放大数据量。解决melt之前先做一次列过滤比如只保留日期列里某几个月的数据或者直接读取时指定usecols参数只在内存中加载需要的列。如果是多个 CSV 文件需要合并把它们统一 melt 后再concat而不是反着来。5.5 坑五报告里的结论和图表对不上答辩被问到数据口径就卡壳现象报告里写“某月疫情达到峰值”但图表上的峰值月份其实是另一边图表用的是全球数据但报告正文分析的是中国情况。原因报告和代码不是同一个人写的或者写报告的时间距离做分析的时间太长自己对数据都没印象了。这是很多数据课设的痛处项目做完就忘了细节。解决做分析的时候养成一个好习惯——每做完一张图直接在 Notebook 里以 Markdown 单元格写下这张图的 2-3 个关键结论。比如“图 3.1 显示中国在 2020 年 2 月达到第一个新增峰值之后逐步下降”。这样写报告时直接组装不容易出现数据和结论脱离的情况。6. 让课设多走一步从静态图到交互式看板的十分钟迁移静态图只能证明你会用 Matplotlib但 Python 数据分析的历史课设千篇一律。如果想给自己增加一点竞争力学会把静态图替换成交互式看板是一个性价比很高的方向——不增加工作量但成品效果提升明显。Pyecharts 已经能满足大部分交互需求但它的图是会生成 HTML 文件不能直接嵌入到 Jupyter Notebook 中实时联动的。如果你想在 Notebook 里拖动滑块看不同时间段的数据或者悬浮鼠标查看具体某一天某个国家的数值可以考虑用 Plotly Express。import plotly.express as px # 使用前面处理好的长表数据df_sorted 包含 country, date, confirmed 列 # 选择几个国家避免图例过多 df_plot df_sorted[df_sorted[country].isin(countries_of_interest)] # 绘制交互式折线图 fig px.line( df_plot, xdate, yconfirmed, colorcountry, title主要国家累计确诊交互式趋势图, labels{confirmed: 累计确诊人数, date: 日期} ) # 设置悬停信息 fig.update_traces(hovertemplate%{y} 人br%{x}) # 输出为独立HTML也可以直接在Notebook中展示 fig.write_html(figures/interactive_trend.html)这段代码的核心价值在于colorcountry自动分组上色不需要手动循环ax.plot鼠标悬停能看到精确数值这份可交互的成果比一张静态 PNG 在答辩展示时能留下更深的印象。hovertemplate自定义了悬浮提示的文字格式%{y}和%{x}分别是纵轴和横轴变量。但有一条血泪经验要告诉你交互式图表适合展示不适合作为唯一的结果输出。你仍然要在报告里放静态图因为老师打印你的报告时HTML 文件没法直接打印。所以正确做法是报告里放静态图答辩 PPT 里放交互式图两者内容一致。最后无论是代码还是报告记得把注释写清楚。我见过太多人的代码变量名是a、b、c图表标题还是默认的 “Figure 1”这种代码即使画出来的图再漂亮印象分也会被拉低。用这些技巧把课设收尾希望帮到你。本文还有配套的精品资源点击获取
返回列表