ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于Python的陕西省AQI空气质量分析与可视化实战

基于Python的陕西省AQI空气质量分析与可视化实战 看到“基于Python的陕西省空气质量AQI指数分析与可视化”这个毕设题目不少同学的第一反应是这不就是爬点数据画几张折线图和地图然后写个论文吗真把一个完整项目从数据采集、指标计算到可视化系统搭完、再走一遍答辩你会发现任何一个环节都能让你折腾两三天。这篇文章我把做这套体系时踩过的坑、验证过的方法、以及答辩时被老师重点追问的问题完整梳理一遍给正在选这个方向的人一份直接能落地的路线图。这个项目到底适合谁如果你正在准备计算机类毕业设计方向是数据分析、Python应用、可视化前端又不想碰那种纯算法推导到头疼的题目这个选题性价比很高。它覆盖了数据采集、数据清洗、指标体系计算、统计分析、可视化呈现一整套流程工作量好展示技术难度适中而且可扩展空间很大。如果你只是想用Python做环境数据分析的爱好者这篇文章里的清洗思路和可视化坑点也值得看看。1. 选题背景与需求拆解为什么选空气质量AQI分析1.1 一个“数据—算法—展示”三步齐全的题目毕设选题最怕什么怕题目太空、没有抓手也怕题目太偏、找不到参考资料。空气质量AQI分析属于典型的“数据密集型应用”题目它可以被拆成一条非常清晰的技术链路数据采集层获取空气质量监测站点数据、气象辅助数据数据清洗层处理缺失值、重复值、异常值统一时间格式指标计算层根据六项污染物浓度计算IAQI分指数再合成AQI分析评估层做时间趋势、空间对比、污染特征与气象相关性分析可视化展示层用图表、地图、交互面板呈现结果。这一条链正好对应了计算机专业里“数据的获取、处理、计算、呈现”四大基本功。老师在开题时最关心的问题——“你的毕设工作量体现在哪”——用这条链路能回答得非常充分因为每一层都有可量化的成果。这个题目还有一个隐形优势数据是客观真实的。环境监测数据不像用户行为数据或网络爬虫数据那样涉及大量隐私和反爬风险数据源公开、结构清晰、字段语义明确。对一个毕设来说“数据能不能稳定拿到”往往是最大的风险点而这恰恰是AQI项目最不担心的部分。1.2 陕西省数据的“先天优势”一个省就是一个小型分析样本库选陕西省作为研究区域不是随手一拍而是因为它的地理结构和污染特征非常适合做对比分析。陕西从北到南可以粗略分成三大块陕北是黄土高原边缘地带靠近毛乌素沙地边缘春季容易出现沙尘天气颗粒物浓度波动大关中地区是平原城市密集、工业集中冬季供暖期和静稳天气叠加时容易出现污染累积陕南地处秦岭以南山脉对污染物有天然阻隔作用整体空气质量明显好于关中。这种“一个省内有明显区域差异”的特点让分析内容变得非常丰富。你可以做城市间的横向对比做陕北沙尘事件与关中冬季污染的成因对比做PM2.5和PM10比值来区分“燃烧型污染”和“沙尘型污染”。如果选一个污染特征高度一致的小省份反而很难写出有深度的分析章节。从工作量角度看陕西省下辖的城市数量适中数据量既不小到没东西可分析也不大到需要分布式计算。做全省近几年的日尺度数据单机Python处理起来毫无压力这在毕设的时间预算里非常重要。1.3 技术栈选型与整体架构设计我的选型思路是“能解决问题就够用但不回避主流工具”最终使用的技术栈如下模块工具选择理由数据采集requests BeautifulSoup / 官方数据接口轻量、可控适合单机跑数据处理pandas numpy表格处理事实标准向量化计算效率高统计分析scipy statsmodels相关性检验、描述性统计足够基础可视化matplotlib seaborn高度可控适合论文配图地图可视化pyecharts国内地图JSON适配好交互性强交互面板Streamlit 或 Flask ECharts答辩演示效果好开发成本低整体架构上我建议把项目拆成四个独立模块data_crawler、data_processor、analysis_engine、visualizer。每个模块之间只通过标准化后的CSV文件或DataFrame接口通信。这样做的最大好处是哪怕某一步出了问题时不用从头跑一遍哪个模块报错就修哪个模块后期想换成别的省份数据也只需要修改配置。架构上还有一个容易被忽略的点数据文件管理和结果输出要规范化。我建议所有原始数据放raw_data/目录清洗后的数据放clean_data/目录生成的图表按“图类型_区域_时间粒度”的命名规则存放。别小看这个习惯做分析时你会反复重跑图表规范的命名能让你少走很多弯路。2. 数据从哪来AQI原始数据的获取与清洗方案2.1 三条可行的数据获取路径AQI数据获取方式我前后试过三种各有优劣这里直接说结论。第一种是直接调用全国城市空气质量实时发布平台的历史数据接口。这个路径最正规数据质量最高但接口需要申请、可能有调用频率限制而且部分历史数据接口现在改成了登录授权模式。如果时间紧张不建议把宝全押在这上面。第二种是从公开的空气质量历史数据网站上抓取。很多环境数据网站提供历史逐日和逐小时数据页面结构相对规律。用requests加BeautifulSoup就能解析。缺陷是部分站点有反爬机制比如限制频率、需要处理接口签名参数。第三种是下载公开数据集。网上有网友整理好的历史空气质量压缩包通常覆盖多个省份多年数据字段完整。这种数据拿来练手最省事但你要做一次“来源可信度说明”——在毕设论文里如实写清楚数据是二手整理、原始来源是全球或国家层面公开数据并交叉验证一部分数值。我的建议是组合方案以官方接口和公开网站为主抓取2019年至今的全省逐日数据再用公开数据集做交叉验证。一个合理的数据规模是陕西省全部地级市最近三到四年的日尺度数据外加关中某一个重点城市的逐小时数据用于展示短时污染过程。逐小时数据不是必须的但对论文的“突发污染事件分析”章节非常有帮助。另外别忘了气象数据。做AQI与气象因素相关性分析需要温度、相对湿度、风速、降水量等要素。这部分可以从气象历史数据接口获取通常是逐日数据字段语义比空气质量数据还要规范。2.2 用pandas做清洗的完整流程拿到原始数据后直接进分析是新手最常犯的错误。实测中原始数据至少存在四类问题时间格式不统一、日期缺失、字段重复、存在异常极值。下面这段代码是清洗流程的核心逻辑import pandas as pd df pd.read_csv(raw_data/shaanxi_aqi_2019_2023.csv, encodingutf-8) # 1. 统一列名保留核心字段 df df.rename(columns{ 城市: city, 日期: date, AQI: aqi, PM2.5: pm25, PM10: pm10, SO2: so2, NO2: no2, CO: co, O3: o3 }) df[date] pd.to_datetime(df[date], format%Y-%m-%d, errorscoerce) # 2. 去重同一城市同一天的记录只保留一条 df df.drop_duplicates(subset[city, date], keepfirst) # 3. 剔除日期无效和浓度异常的记录 df df[df[date].notna()] df df[(df[aqi] 0) (df[aqi] 500)] # 4. 按城市分组填充缺失值前向填充 线性插值互补 df df.sort_values([city, date]) for col in [pm25, pm10, so2, no2, co, o3, aqi]: df[col] df.groupby(city)[col].transform(lambda s: s.ffill().rolling(7, min_periods1).mean()) df df.dropna() df.to_csv(clean_data/shaanxi_aqi_clean.csv, indexFalse, encodingutf-8-sig)这段代码里有两个设计思路值得说明。第一去重必须是“城市日期”组合维度而不是只看日期。因为原始数据中可能出现同一城市在同一天有两条记录比如接口分页重复返回如果只按日期去重会把一个城市的数据误删成一条而另一个城市的数据还在导致后续分析出现严重偏差。第二缺失值填充我用了“前向填充 7日滚动均值”的组合。前向填充的思路很简单空气质量的短时连续性很强今天缺失就用昨天的数值顶上。但连续多天全部缺失时比如站点维护了一周滚动均值能把长期缺测段的噪声压平。这个组合在实测中对AQI序列的重建效果比单纯用均值插值更贴近真实变化。关于异常值特别提醒一句不要把所有“看起来不合理”的高值都当异常删掉。AQI超过300的重度污染日是真实存在的删除这类记录等于人为抹掉了你论文里最有分析价值的事件。真正要删的是物理上不可能的值比如PM2.5浓度为负数、AQI为800以上且连续出现多数天的机械重复值等。2.3 数据质量验证拿到数据先别急着分析清洗完成后做一次系统的质量验证能省掉后面无数排查的时间。我常用的验证手段是三类检查。第一类是范围检查AQI理论范围是0到500六项污染物浓度也都有物理上下限。写一个describe()输出各字段的最大最小和中位数肉眼扫一遍就能发现明显异常。PM2.5超过1000、O3小时值超过400的都要单独盯一眼。第二类是完整性检查统计每个城市的日期数量。正常应该等于“采集年份天数×年份数”。如果某个城市的条目数明显偏少大概率是抓取时漏页或者站点上线晚。可以用下面这段代码检查date_range pd.date_range(2019-01-01, 2023-12-31, freqD) for city in df[city].unique(): actual_dates set(df[df[city] city][date]) missing len(set(date_range) - actual_dates) print(f{city}: 缺失 {missing} 天)第三类是交叉验证随机抽取10到15天把清洗后的数据和国家公开发布的数据做比对。如果个别日期偏差超过2个AQI指数单位就得回去检查是不是时间对齐出了问题。我在实测中就遇到过日期格式带时区偏移导致所有时间往后挪了8小时的问题。数据质量验证这件事在毕设答辩时几乎一定会被问到“你怎么保证你分析的这些数据是可靠的”能把验证流程讲清楚“范围检查、完整性检查、交叉验证”三步一摆这个问题基本就过关了。3. AQI计算原理与Python实现从污染物浓度到发布指数3.1 为什么不能直接用官网AQI理解IAQI分指数的意义AQI数据可以直接从数据源拿到但一个合格的毕设必须要能自己从污染物浓度算出AQI。这既是论文的一个重要章节也是区分“会调用数据”和“懂指标体系”的关键点。AQI全称是空气质量指数它是一个无量纲数值。计算它要先对六项污染物分别计算分指数IAQI再取最大值。六项污染物是PM2.5、PM10、SO2、NO2、CO、O3。其中CO的单位是毫克每立方米其他五项都是微克每立方米计算时一定要先统一单位。单个污染物的分指数公式是IAQI (IAQI_hi - IAQI_lo) / (BP_hi - BP_lo) × (C - BP_lo) IAQI_lo其中C是污染物浓度实测值BP_lo和BP_hi是对应浓度区间断点IAQI_lo和IAQI_hi是对应的指数区间。这个公式的本质是线性插值把污染物浓度映射到0到500的指数空间中。AQI的最终值就是六项IAQI中的最大值而“首要污染物”则是那个让IAQI达到最高的污染物。如果所有分指数都小于50首要污染物这一栏通常填“—”。为什么国家不直接给出AQI原始浓度而要用这样一个指数直观理解是不同污染物的浓度量纲差别很大SO2浓度10微克每立方米和PM10浓度10微克每立方米对健康的影响完全不同。AQI把这些不同量纲的浓度统一映射到同一把“尺子”上公众只看一个数就能判断空气品质。理解了这个背景你就明白为什么IAQI的断点表设计得如此“千奇百怪”——它是依据健康风险研究和流行病学证据制定的不是单纯按浓度均匀切分。3.2 六项污染物的浓度断点表国标《环境空气质量指数AQI技术规定》给出了各项污染物的分指数与浓度限值。这里列出核心断点完整版以最新标准为准IAQIPM2.5 24h均值PM10 24h均值SO2 24h均值NO2 24h均值CO 24h均值O3 8h滑动值000000050355050402100100751501508041601501152504751801421520015035080028024265300250420160056536800400350500210075048—500500600262094060—这张表里藏着几个常见掉分点。第一个是O3用的是“日最大8小时滑动平均”不是24小时平均值也不是某整点的小时值。第二个是CO和SO2在IAQI超过150后断点变化非常剧烈这意味着它们的分指数在高浓度下增长很快往往成为污染日的首要污染物。第三个是PM10的500分指数浓度只有600微克每立方米而PM2.5要500这种差异直接反映了不同颗粒物在健康风险权重上的区别。3.3 向量化实现用pandas而不是逐行for循环很多人拿到这个公式后的第一反应是写一个for循环逐行计算。数据量小倒无所谓但更专业、也更能体现工程素养的做法是利用pandas的向量化操作。代码逻辑是每种污染物单独做一个分度区间查找函数然后在DataFrame上逐列计算IAQI最后取最大值。import numpy as np import pandas as pd def calc_iaqi(series, bp_lo, bp_hi, iaqi_lo, iaqi_hi): iaqi pd.Series(indexseries.index, dtypefloat) for i in range(len(bp_lo)): mask (series bp_lo[i]) (series bp_hi[i]) iaqi[mask] ( (iaqi_hi[i] - iaqi_lo[i]) / (bp_hi[i] - bp_lo[i]) * (series[mask] - bp_lo[i]) iaqi_lo[i] ) return iaqi # 以PM2.5为例 pm25_bp_lo [0, 35, 75, 115, 150, 250, 350, 500] pm25_bp_hi [35, 75, 115, 150, 250, 350, 500, 700] pm25_iaqi [0, 50, 100, 150, 200, 300, 400, 500] df[iaqi_pm25] calc_iaqi(df[pm25], pm25_bp_lo, pm25_bp_hi, pm25_iaqi, pm25_iaqi) # 其他五项同理最后合成 df[iaqi_pm10] calc_iaqi(df[pm10], pm10_bp_lo, pm10_bp_hi, pm10_iaqi, pm10_iaqi) # ... 省略其余四项 iaqi_cols [iaqi_pm25, iaqi_pm10, iaqi_so2, iaqi_no2, iaqi_co, iaqi_o3] df[aqi_calc] df[iaqi_cols].max(axis1).round()这里有个细节我为什么用了round()国标规定AQI保留整数而计算过程中的IAQI可以保留小数。不同数据源在最后一步的舍入策略有细微差异——有的是四舍五入有的直接用astype(int)向下取整。实测中这两种策略的差最多就1个指数单位但如果拿自算值跟官方发布值比你会发现那一两个单位的偏差往往就来自这里。所以校验时只要偏差在±2以内基本可以认定计算逻辑正确。另外一个常见错误是漏掉对O3的特殊处理。前面说了O3要用8小时滑动平均的最大值你在清洗阶段不能直接拿日平均O3浓度去算必须单独准备一列“O3_8h_max”数据。如果数据源里只有小时值需要先按小时序列做8小时窗口滑动平均再按天取最大。这一步是计算章节里最有“技术含量”的部分之一写进论文里非常加分。3.4 用官方发布值做双重校验自己算完AQI后一定要和官方发布值做一次系统性对比。方法很简单把官方AQI字段和自己算出来的aqi_calc做差值统计df[diff] df[aqi] - df[aqi_calc] print(df[diff].describe()) print((df[diff].abs() 2).mean())如果两列的一致性在95%以上你的计算逻辑基本没问题。剩下的那5%差异可能来自O3的8小时滑动窗口边界对齐方式不同也可能来自原始数据中小数位数截断。把这部分差异样本列出来逐个观察每个案例都能写进论文的“数据验证”小节。我还做过一个更有说服力的校验按月份统计官方AQI和自算AQI的均值差。如果整体差异在±1以内说明计算逻辑没有系统性偏差。这一步做好了答辩时老师问“你算的AQI靠谱吗”你就可以直接给出这个统计结果。4. 分析维度的设计时间趋势、空间分布与气象关联4.1 时间维度月度、季节、年度三个尺度同时看AQI分析的核心输出之一是“污染在时间上的规律”。我在项目里分了三个尺度来看。月度尺度是最常用的。按“城市-月份”分组求平均AQI再看多年同期的月度均值。陕西的整体规律通常是冬季12月、1月明显偏高夏季6到8月偏低春季会有一个或大或小的波动。冬季偏高的原因很直接——供暖季排放增加加上静稳天气多、逆温层容易出现污染物扩散条件差。春季的波动则多半来自沙尘过程这时候首要污染物往往从PM2.5切换成PM10。年度尺度用来判断整体趋势。把每年的年均AQI画成柱状图通常可以看到近几年整体在缓慢改善。但要注意的是单看年均值会掩盖季节性差异所以年度部分最好配一张“月均AQI热力图”横轴是月份、纵轴是年份、颜色代表AQI值。这张图能一眼看出“改善主要发生在哪个季节”比单讲一个年均值的信息量大得多。月度分析的代码核心是分组聚合monthly df.groupby([df[date].dt.year, df[date].dt.month])[aqi].mean().unstack()如果你想做全省整体情况就把所有城市的AQI先按天求全省均值再按月聚合。如果做城市个体分析按“城市月份”分组即可。两张图结合展示“全省整体月变化”和“典型城市的月变化对比”。4.2 空间维度城市间的横向对比空间分析的目的是回答“哪些地方空气差、差在哪”。最直接的操作是把每个城市的年均AQI排序画条形图。陕西的典型格局是关中城市群整体排在前面陕北居中陕南城市排在最后。这个结果和地理直观感受高度一致但论文里不能只摆结论得把原因拆出来。拆解原因的一个好方法是计算PM2.5/PM10比值。这个比值很能说明问题比值接近0.6到0.7说明污染以细颗粒物为主大概率来自燃烧排放工业、机动车、供暖比值偏低比如0.3到0.4说明粗颗粒物占主导多半受到沙尘、扬尘影响。把每个城市的年均比值列出来你会发现一个有意思的现象陕南部分城市的AQI虽然低但PM2.5/PM10比值反而偏高说明细颗粒物占比不小而陕北某些城市在沙尘季会有明显的低比值波动。再来一层就是“污染天频次”的对比。定义AQI大于100的天数为超标天统计每个城市每年的超标天数。这个指标比年均值更直观也更有民生意义。实测中关中城市超标天通常在60到90天区间浮动陕南基本能控制在30天以下这个差异写进分析章节非常有说服力。空间对比还可以和地图结合做“年均AQI城市分布图”。地图部分我会在第5节专门讲pyecharts的正确打开方式这里先提一个关键点城市名称必须在数据里和地图注册名完全一致否则地图上会显示空白。常见的坑是数据里写“西安”地图注册名是“西安市”或者县级市名称不在地图JSON里。4.3 与气象因素的相关分析别把相关当因果AQI与气象要素的相关分析是论文里很出彩、但也容易被挑毛病的一部分。我用的方法是先做Pearson相关矩阵再按季节拆开来看。实际操作是把两部分数据按城市和日期对齐合并AQI、PM2.5、PM10作为空气质量变量温度、相对湿度、风速、降水量作为气象变量。合并后直接调用pandas的corr()方法merged df.merge(weather, on[city, date], howinner) corr merged[[aqi, pm25, pm10, temp, humidity, wind, precip]].corr()典型结论一般是AQI与风速呈负相关风速越大污染物扩散越快AQI与降水量呈负相关雨雪天气对颗粒物有清除作用AQI与相对湿度的关系比较复杂有时正相关高湿环境下颗粒物吸湿增长、能见度下降有时因为降水伴随高湿而表现为负相关。这里必须提醒一句相关性不是因果关系。高湿度既可能促进颗粒物二次生成也可能只是静稳天气和逆温共同作用下的伴生现象。如果你想在论文里做更深一层建议把数据按季节拆开再分别算相关——冬季的湿度效应和夏季的湿度效应很可能正负相反混在一起算会把真实关系掩盖掉。气象分析还有一个小技巧做“静稳天气”的简单识别。定义风速低于某个阈值比如日平均风速小于1.5米每秒且连续持续两天以上作为一次静稳过程然后对比静稳过程期间和非静稳期间的平均AQI差。这个操作不复杂但结论表述非常直观答辩现场也很好讲。4.4 污染事件识别从“描述”走向“结论”最后一个分析维度是识别重污染事件。这个维度很多二手教程没有讲但它最能体现工作量。我的做法是先筛选AQI大于200的重污染日按城市统计全年出现次数和月份分布然后把“重污染事件”定义成“连续至少两天AQI大于150”的过程统计每次事件的起始日期、持续天数、平均AQI和过程中的首要污染物变化。以关中地区为例典型的冬季重污染事件往往持续3到5天污染物以PM2.5为主过程通常从AQI缓慢爬升开始、持续累积、在强冷空气过境后迅速回落。把这个过程逐日画出来配合气象要素的逐日变化就是一个完整的“个案分析”章节。这种案例分析在论文里占一页但含金量远比几个统计图表高。选择哪些城市做案例也有讲究。选一个污染最重的城市能突出问题的严重性选一个污染改善幅度最大的城市能展示治理成效两者搭配能让分析章节更有层次。5. 可视化呈现地图、时序图与交互面板的实现思路5.1 基础图表matplotlib环境配置与三张必备图可视化的第一步其实是环境配置因为中文乱码问题几乎每个人都会遇到。matplotlib默认字体不包含中文字符直接画图全是方块。正确的处理方式有两种一是全局设置字体二是临时指定。实测最稳的做法是先检查系统里有没有中文字体再注册进matplotlibimport matplotlib.pyplot as plt from matplotlib import font_manager # 以黑体为例字体路径按实际系统调整 font_manager.fontManager.addfont(C:/Windows/Fonts/simhei.ttf) plt.rcParams[font.family] SimHei plt.rcParams[axes.unicode_minus] Falseaxes.unicode_minus这一行很容易漏掉。不设的话坐标轴上的负号会显示成方块整个图的档次瞬间下降。基础图里我推荐至少做三张全省月均AQI折线图、各城市年均AQI排序条形图、逐日AQI热力图。前两张用matplotlib就能搞定第三张是整篇论文里最出彩的一张图——横轴是月份、纵轴是年份、方格颜色代表AQI值一眼就能看出冬季污染带。实现方法是用pivot构造矩阵再交给imshow或seaborn.heatmap。画图时有个容易被忽视的细节图例、坐标轴标签、数据标注一个都不能少。论文里的图评阅老师不会像读者那样仔细看他们扫一眼图的标题、坐标轴、图例就能判断这张图是否规范。坐标轴标签建议写全称比如“月均AQI指数”而不是“AQI”图例要标明单位比如“空气质量指数AQI”。5.2 地图可视化pyecharts的正确打开方式城市间空间分布用地图展示最直观。pyecharts是主流选择但我必须把几个坑先摆出来。第一个坑是版本。pyecharts在早期版本和当前版本之间的API变化非常大网上搜到的大多是旧版写法。新版本创建地图的推荐方式是from pyecharts.charts import Map from pyecharts import options as opts city_data [(西安市, 98), (宝鸡市, 82), (咸阳市, 105)] map_chart ( Map(init_optsopts.InitOpts(width800px, height600px)) .add(年均AQI, city_data, maptype陕西) .set_global_opts( title_optsopts.TitleOpts(title陕西省各城市年均AQI分布), visualmap_optsopts.VisualMapOpts(min_0, max_200, range_color[#00E400, #FF0000]) ) ) map_chart.render(output/map_aqi.html)第二个坑是地图JSON文件的加载。pyecharts的在线地图依赖网络资源毕设现场万一没有外网地图可能渲染不出来。稳妥做法是把需要的JS文件下载到本地在InitOpts里通过js_host参数指向本地路径。第三个坑是地市名称必须匹配地图注册名。你用“西安”作为数据key时可能图上一片空白改成“西安市”就好了。解决方法是先查看地图对象的注册名列表再写一个映射函数统一数据里的城市名。这个坑我在第6节还会详细展开。地图配色也要符合常识。AQI的颜色体系是由国家技术规范规定的绿色代表优0到50、黄色代表良51到100、橙色代表轻度污染101到150、红色代表中度污染151到200、紫色代表重度污染201到300、褐红色代表严重污染大于300。做地图可视化时直接用这套标准色会在视觉上一眼看出区域差异也让图表的专业性更强。VisualMap的区间颜色按这个顺序设置即可。5.3 进阶把静态图升级成在线交互面板如果做到这一步时间还够强烈建议加一个交互面板。理由很简单答辩演示的时候一张会动的、能切换城市的交互页面效果远好于十张静态图。实现方案首选Streamlit。它比Flask轻量太多不需要自己写HTML模板和路由逻辑几行代码就能把图表组织到页面上import streamlit as st import pandas as pd st.title(陕西省空气质量AQI分析) city st.selectbox(选择城市, df[city].unique()) metric st.selectbox(选择指标, [AQI, PM2.5, PM10, O3]) filtered df[df[city] city] st.line_chart(filtered.set_index(date)[metric])Streamlit的line_chart和bar_chart虽然简单但样式偏基础。如果追求更好的视觉效果可以用st.components.v1.html嵌入pyecharts生成的HTML文件这样地图和交互图表都能整合进来。交互面板的设计有个原则不要把所有支持的功能都堆在页面上。毕设面板通常做三个区域就够了——“总体概览”放全省月均趋势和城市排名“城市对比”放两三个城市的并行折线“事件分析”放重污染个案的逐日序列。功能太多反而暴露出逻辑松散的问题。5.4 配色与排版规范让论文配图达到发表级最后再说说美观度。很多同学画完图不调样式就直接贴进论文其实只要做三件事图就能立刻提升一个档次。第一件事是统一画布尺寸。论文里常用的图宽是14厘米左右折线图长宽比大概3比2热力图可以方一点。设置figsize(10, 6)或者论文模板要求的尺寸比默认大小要好看很多。第二件事是配色不踩雷。除了AQI标准色之外普通统计图建议选一套低饱和度的渐变色不要用matplotlib默认的蓝色和橙色混搭。seaborn自带的配色方案deep或muted就很好用。第三件事是去除不必要的信息干扰。默认的坐标轴边框、刻度线、多余的网格全部可以关掉。只保留必要的坐标轴标签和水印图会显得干净得多。细节决定论文的观感这一部分花半小时调整很值。6. 踩坑记录与答辩经验让毕设更具竞争力的关键细节6.1 六个真实踩过的坑与排查过程这部分是我最想分享的内容。我实际排查过的坑至少有六个每一个都浪费过半天以上的时间。第一个坑是pyecharts版本导致的API写法错误。我先后试过在线文档里的新写法和老教程里的旧写法报错信息完全不一样。排查过程是先定位到报错的类名去查当前安装版本的源码发现是新版把Geo的部分方法改成了add_coordinate和add_geo两种模式。解决方法是固定版本或者严格按当前版本的官方示例改写。第二个坑是地图上的城市名不显示。排查过程很典型图表成功渲染了但陕西省地图上只有轮廓没有数据块。我先检查了数据key是不是“西安市”而不是“西安”改成全称后地图正常。如果你也遇到地图空白第一反应就是去查地名词典。第三个坑是matplotlib中文乱码。前面讲过解决方案这里补充一个排查技巧先执行matplotlib.font_manager查看当前可用字体如果没有中文字体需要手动下载一个SimHei.ttf或者用系统自带的其他中文字体文件注册。第四个坑是数据接口的编码问题。部分第三方站点返回的页面编码是GBK直接用utf-8解析会导致乱码甚至报错。排查方法是先用requests拿到原始字节流用chardet检测编码再决定解码方式。这个坑对于爬虫类的毕设非常普遍。第五个坑是官方AQI与自算AQI的差值。我一开始计算差异时发现很多样本差了5到8个指数值排查了很久最后定位到O3用的是8小时滑动平均而我用的数据源里只有O3的24小时均值。换成O3_8h字段后差值立刻收敛到±1以内。这个细节特别适合写进论文的“数据说明”部分。第六个坑是合并数据集时的NaN膨胀。空气质量数据和气象数据在日期覆盖范围上不一致用merge(howleft)会导致大量NaN进而让相关系数的计算样本量骤减。解决办法是把howinner作为主要合并方式只保留两边都有数据的日期。这个操作的逻辑是宁可样本量稍微小一点也不要引入不完整的伪记录。6.2 答辩追问清单提前准备好这六类问题根据我参加答辩的经验评委对AQI项目的提问高度集中在六个方向提前准备起来并不难。第一类是数据来源问题“你的数据从哪里来的怎么保证可信”回答框架是如实说明数据源 交叉验证结果 清洗流程。如果你用的是公开数据集诚实说明二手整理和原始出处评委不会为难你。第二类是计算逻辑问题“AQI为什么是取最大分指数而不是平均”这是最核心的原理问题。你要能解释清楚AQI反映的是“最差一项”暴露的健康风险而不是整体污染平均水平。同时要能说出IAQI分指数的含义和断点表的制定依据。第三类是结果争议问题“你分析的结论靠谱吗和官方发布信息一致吗”用你自己的校验数据说话比如自算AQI和官方AQI的一致性在95%以上。承认局限也不丢人——“本研究基于公开数据未纳入临时管控措施等未公开信息”这种表述反而显严谨。第四类是选型问题“为什么用pyecharts而不用ECharts原生或Leaflet”回答方向是开发效率与项目复杂度匹配pyecharts封装了地理JSON加载、地图配色、Tooltip等细节能把精力留给数据处理和分析本身。第五类是扩展问题“如果给你更多时间你怎么改进”建议准备两到三个方向引入逐小时数据做污染过程分析、加入机器学习模型做AQI预测、接入更多省份数据做区域对比。第六类是工作量问题“这个项目里哪些是你自己写的哪些是调包”诚实回答并强调“调用库不等于没有工作”——数据清洗逻辑、污染事件识别规则、指标体系计算、业务分析都是自己设计的工程量。6.3 个人体会做完这个项目我学到的几件事最后聊一点我个人体会。这个项目做完之后我最大的感受是一个看似“简单”的数据分析毕设做好和做完之间差得非常远。“做完”是把数据爬下来、画几张图、写一篇论文“做好”是数据经过严格的清洗和交叉验证、分析有清晰的逻辑链、每张图都在回答一个明确的问题。后者需要你在每个环节都多问一句“为什么”——为什么这样清洗、为什么这样分组、为什么这样配色而这些“为什么”恰恰是答辩老师最爱问的地方。还有一个很实际的经验数据备份要趁早。我中途重装过一次环境差点丢掉已经清洗完的中间数据。从那以后我养成习惯每天的原始数据、清洗数据、图表输出都按日期归档重要内容上传到网盘。这件事看似和“技术含量”无关但关键时刻救过我一命。再分享一个小技巧把清洗好的数据、计算脚本、分析图表按“章节名”整理成目录论文写到哪一节就对应到哪个目录。整个项目做完之后你会发现素材已经自动变成了一篇结构完整的论文初稿。这种“代码与论文同步推进”的方式比最后几周熬夜赶工要舒服得多也更不容易出错。如果你也想在这个题目上加点差异化的东西我建议考虑两条路一条是往“预测”方向走用历史AQI和气象数据做一个时间序列预测模型哪怕只是简单的线性回归或随机森林也会让项目的技术栈多出一层另一条是往“交互”方向走把可视化面板做成一个完整的应用加了预测的话面板里的“明天空气质量会怎样”就是一个很自然的展示功能。这个方向接得住、也出得了成果。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表