ARTICLE DETAIL

资讯详情

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

2015年五一数学建模B题复盘:空气污染数据分析与预测建模全流程

2015年五一数学建模B题复盘:空气污染数据分析与预测建模全流程 说起2015年的五一数学建模联赛B题“空气污染问题研究”我到现在还记得当年假期第一天三个人窝在宿舍里对着赛题发呆的场景。那年的题目看起来不算绕考的就是空气质量监测数据分析但真正动手之后才发现从数据清洗到相关性分析再到回归归因和时间序列预测几乎每一步都有坑等着你。数模圈里有一句老话数据类题目不是比谁模型多而是比谁能把数据用扎实。这篇文章就以我们当年的实操路径为线索完整梳理一遍这类污染监测数据题的破题思路、模型选型、代码要点和论文包装经验给以后要做同类环境数据题的同学一个可以直接抄作业的参考。1. 2015年五一赛B题拆解这不是一道评价题而是一条完整链条1.1 当年的赛题到底要我们干什么从公开的赛题框架来看那年的B题给的是某区域一段连续时间内的空气质量监测数据污染物指标基本围绕SO2、NO2、PM10、PM2.5、CO和O3这六项展开同时附带了相应的气象观测信息。题目要回答的核心问题包括区域内空气污染的整体现状如何六种污染物之间是否存在联动关系影响污染浓度高低的主要因素是什么以及未来一段时间内污染水平会怎么变化。很多队伍拿到题之后第一反应是“做个综合评价”然后排个序画几张图交差。这个思路不是完全错但它只覆盖了题目的一部分。仔细拆解会发现这道题其实藏了四个层次的任务现状评价、结构分析、归因解释、趋势预测。每个层次对应的分析方法完全不一样单纯做评价后面的十几小问就没有支撑了。所以拿到题目后的第一件事不是找模型而是把问题重新整理成一条分析链条。我们当时在白板上写下了这样一句话先描述它是什么再解释为什么是这样最后预测它会变成什么样。这句话定了整个论文的骨架。1.2 四个任务层级对应的模型矩阵为了不让后面写论文时东一榔头西一棒子我们在第一天就用表格把任务层级和初步模型选型列清楚了。这个表格看起来很基础但后面所有的分工、时间分配、代码模块都是从这里来的。任务层级核心问题推荐方法论文呈现形式现状评价污染严重吗哪个时段最重描述统计、超标率、污染指数折算箱线图、时间序列折线图、月度热力图结构分析污染物之间有没有内在联系相关矩阵、主成分分析、聚类分析热力图、碎石图、载荷矩阵表、聚类树状图归因解释什么因素在驱动污染变化多元回归、逐步回归、气象因子相关性回归系数表、标准化系数对比图、残差图趋势预测未来污染水平怎么走ARIMA、灰色GM(1,1)、指数平滑、组合预测预测曲线、置信区间、误差对比表这张表最大的作用是把“空气污染问题研究”这样一个偏宏观的题目变成了几个可以被分别攻克的子问题。后面的模型不需要多炫技每一个任务层选一到两个合适的模型把它们之间的衔接做顺一篇高质量参赛论文的框架就有了。1.3 队伍分工与第一天的任务安排数模是三个人的活儿赛前一定要把角色分清楚。我们当时的分工是一个人负责数据清洗和描述统计一个人负责多元统计和回归一个人专门盯论文框架和图表模板。第一天晚上之前所有人都必须对自己的部分有一个初步结论不允许任何人抱着“等数据准备好了再开始”的心态。这种分工在环境数据类题目里特别重要因为这类题目数据量通常不小预处理工作至少占据前期三分之二的时间。如果三个人都挤在数据清洗上后面建模和写作一定会手忙脚乱。让论文手提前熟悉图表规范能避免最后一晚还在调整字体统一度这种性价比极低的工作。2. 数据预处理污染数据分析最容易被低估的一关2.1 数据来源和格式里的隐形差异做这类题目数据质量几乎决定了论文的下限。我们当时的数据主要来自公开的空气质量监测历史数据库和天气历史数据平台。听起来简单但把两份数据拼到一起时问题就来了空气质量数据的污染物浓度单位不统一CO常用mg/m³SO2、NO2、PM10、PM2.5用μg/m³O3还有“日最大8小时滑动平均”和“24小时平均”两套统计口径直接用日均值会和别的研究文献对不上。建议所有人第一步先把数据字典建出来明确每个字段的单位、统计时段和来源。如果题目本身给的是半小时或小时级数据还要决定是聚合成日均值还是保留小时值做日内规律分析。我们最后选择的是“日均值为主、小时值为辅”整体趋势和建模用日均讨论早晚高峰污染差异时再用小时级数据单独做一节。2.2 缺失值、异常值、时间对齐的处理经验现实中的监测数据就没有“干净”的节假日设备检修、传感器故障、极端天气下的数据异常都很常见。我们的处理规则定得很简单也很实用缺失值单日缺失用前后两天线性插值连续缺失超过3天的该段数据不插值直接剔除避免人为造出一段平滑的假数据。异常值先用箱线图看分布再用3σ原则筛像PM10在沙尘天气时可能冲到上千这种异常值存在物理意义不能一刀切删掉要单独标记。时间对齐气象数据里风速、湿度通常是小时级空气质量数据也是小时级合到一起做日均值时必须保证两套数据的时间戳一致否则会出现“今天的风速”配“昨天的PM2.5”这种低级错误。这步我多提醒一句不要等到建模阶段才发现数据对不齐。我们当时的做法是跑完清洗流程后顺手出一张每一位变量的折线图人眼扫一遍基本就能看出有没有时间错位或者单位数量级的问题。2.3 一份可以直接改着用的Python预处理代码现在回头看当年用Excel和SPSS手工清洗数据确实太慢了。如果重做这道题我会直接用Python的pandas一步到位。下面这段代码是核心预处理流程的简化版覆盖了缺失值插补、日均值聚合和异常值筛选三个基本操作供参考import pandas as pd import numpy as np df pd.read_csv(air_quality.csv, parse_dates[time]) df.set_index(time, inplaceTrue) # 日均值聚合注意O3先取8小时滑动平均的日最大值 daily_mean df.resample(D).mean() daily_ozone_8h df[O3].rolling(8).mean().resample(D).max() # 缺失值线性插值 daily_mean daily_mean.interpolate(methodlinear, limit3) daily_mean[O3_8h_max] daily_ozone_8h # 简单3σ异常值替换 def replace_outlier(series, n3): mean, std series.mean(), series.std() return series.where((series - mean).abs() n * std, np.nan) daily_mean daily_mean.apply(replace_outlier) daily_mean daily_mean.interpolate(methodlinear, limit3) daily_mean.to_csv(daily_clean.csv)这段代码不复杂但它把最容易出错的几个点都照顾到了O3的统计口径单独处理、插值上限限制、异常值替换而不是直接删除。处理完之后务必打印一下数据集的时间范围和每天的监测站点数量确认没有某一天全部站点数据为空。3. 污染物之间的秘密相关矩阵、主成分与聚类给出的第一层结论3.1 Pearson相关矩阵先看谁和谁联动数据清洗完之后我先跑了一张六种污染物之间的Pearson相关系数矩阵。这一步看起来简单却是整个分析中最出结论的环节。我们从结果里很明显地看到两簇相关性SO2、PM10和CO两两之间相关性强相关系数在0.6到0.8之间O3和NO2之间则是弱的负相关。换成别的领域可能觉得负相关是坏事但对空气污染来说这个现象背后是明确的光化学反应机制——NO2在光照下参与O3的生成高浓度NO又会通过滴定效应消耗O3所以两者日变化往往此消彼长。这个解释后来成了论文中非常出彩的一段。因为评委看的很多论文只会把相关系数表一摆根本不解释相关系数背后的物理过程。你只要写上一句“污染物之间的相关性并不只是统计现象它反映了排放源的相似性与大气化学过程的耦合”整篇论文的档次就上去了。3.2 主成分分析把六个指标压成三个能讲故事的因子相关性矩阵只能告诉我们“谁和谁关系近”但没办法回答“这几个关系近的污染物背后代表什么”。这一步就需要主成分分析上场。我们当时用的是R里的prcompPython里对应的是sklearn.decomposition.PCA。先对六项污染物做标准化再跑PCA。结果大概是这样前三个主成分累计方差贡献率超过85%第一主成分上PM10、SO2、CO载荷都比较大可以解释为燃煤和工业源排放因子第二主成分上NO2和O3载荷突出对应机动车和光化学污染因子第三主成分则更多反映了区域性的二次颗粒物特征。这里有一个很实用的经验不要只把载荷矩阵扔进论文要结合主成分得分画出时间序列图。比如第一主成分得分在冬季明显偏高第二主成分得分在夏季午后出现峰值这样空间就变成了“不同季节受不同污染源主导”的结论。后来很多优秀论文都把这个结论作为第二部分的核心发现来写。3.3 层次聚类给月份和城市重新分组主成分是找变量之间的结构聚类则是找样本之间的结构。我们拿每个月的污染物浓度均值做样本用层次聚类把12个月分成了三类冬季燃煤叠加型11月到次年2月、春秋过渡型3月到5月、9月到10月、夏季光化学型6月到8月。这个结果其实在环境领域算常识但在建模题里能用聚类图把常识“数据化”并画出来是非常加分的。如果数据是多个站点的也可以对站点做聚类看看哪些区域的污染特征更接近能直接回答题目里关于空间分布的问题。聚类数量建议用轮廓系数来定不要拍脑袋选3类我们的做法是跑K-Means从2类到6类画误差平方和曲线选肘部点对应的K值。4. 归因建模气象与社会经济因素如何进入回归方程4.1 确定进入方程的自变量组合做归因分析前先想清楚“因变量”是谁。如果目标是解释PM2.5日均浓度那么自变量可以分为两类气象因子风速、温度、相对湿度、气压、降水量和活动水平因子如果有区域尺度数据可以是工业用电量、机动车流量、燃煤消耗量的代理指标。不同的变量必须提前做好共线性筛查。气象数据里的温度和气压往往高度负相关风速和湿度也可能互相关联。我们先用相关系数矩阵筛了一遍凡是两两相关系数高于0.7的变量只保留一个。然后用VIF方差膨胀因子做复核一般来说VIF小于5可以接受大于10就必须处理。4.2 逐步回归和标准化回归系数的正确解读自变量选好了之后我们用的是双向逐步回归按AIC最小原则筛选变量。最终进入回归方程的核心变量包括风速、湿度、温度和上一日污染物浓度滞后项。这里要特别提醒污染物浓度数据通常呈右偏分布直接回归会让极端值过度影响系数估计所以我们先对因变量做了对数变换也就是ln(PM2.5)回归效果明显变好。标准化回归系数是论文里要重点展示的因为不同自变量单位不同不能用原始系数比较相对重要性。我们算出来的结果和大气物理机制高度一致风速的标准化系数为负风速越大扩散条件越好污染物浓度越低相对湿度为正湿度大有利于颗粒物吸湿增长和二次转化温度对PM2.5浓度的影响不明显但如果换成O3做因变量温度的贡献立刻变得显著。这段结果为什么重要因为评委判断一个模型好不好除了看统计指标还会看结论是否符合常识。如果回归结果显示“风速越大污染越重”哪怕R²再高评委也会觉得你的模型有问题。所以跑完回归之后一定要基于专业背景先审查一遍系数的正负号。4.3 残差诊断和模型修正回归做完不能直接写进论文要检查残差。我们当时把预测值和实际值画在一起发现雨季前后的残差明显呈团状聚集说明残差存在自相关。普通线性回归对自相关数据很不友好于是我们在模型中加入因变量的一阶滞后项变成动态回归结构D-W统计量从接近0.9改善到了1.9附近整个模型才算站得住。残差这一段很多队伍都不写但恰恰是这种细节决定了论文能不能从“做了模型”跨越到“模型可靠”。哪怕是简单的一句话“残差散点图随机分布在零线两侧无自相关趋势”也比完全不提强得多。5. 短中期预测ARIMA、灰色GM(1,1)与组合模型的实战对比5.1 为什么没直接上神经网络很多队伍看到“预测”两个字就想到神经网络、随机森林恨不得把所有机器学习的名字都堆上去。但我们当时认真想了想还是决定先用经典时间序列模型把问题解决。原因有三第一样本量只有几百天的日均数据训练深度模型很容易过拟合第二比赛时间只有三天调模型超参数的时间成本太高第三也是最重要的一点评委更看重模型的可解释性ARIMA和灰色模型的参数都有明确的经济或物理含义这在论文答辩中更容易讲清楚。5.2 ARIMA建模的全过程ARIMA是时间序列预测的标准武器。我们以PM2.5的月均序列和日均序列分别建模。日均序列非平稳先做一阶差分差分后做ADF检验p值小于0.05确认平稳然后看ACF和PACF图定阶。整个过程可以浓缩成下面几行statsmodels代码import statsmodels.api as sm from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 假设 daily_mean 中的 PM2.5 列 x daily_mean[PM2.5].dropna() x_diff x.diff().dropna() plot_acf(x_diff) plot_pacf(x_diff) # 根据图形初定 p、q再用AIC做小范围搜索 model sm.tsa.ARIMA(x, order(1, 1, 2)).fit() print(model.summary())我们最终用的日均PM2.5模型是ARIMA(1,1,2)AIC值在小范围搜索里最低。残差的Ljung-Box检验p值大于0.05说明残差已经是白噪声模型没有漏掉明显的自相关结构。预测未来30天时我记得RMSE折算成原始浓度大约是20μg/m³左右MAPE在20%以内。对于日均浓度预测来说这个精度在当时的数据条件下已经够用。5.3 灰色GM(1,1)的适用边界灰色GM(1,1)是数学建模比赛里的常客适合小样本、短序列、近似指数增长趋势的数据。它的做法是先对原始序列做一次累加生成弱化随机波动然后用一阶线性微分方程去拟合累加序列最后累减还原。整个计算过程在教科书里有很多我在这里不多抄公式只讲应用边界。我拿PM2.5月均值试过GM(1,1)效果很差因为空气质量数据有明显的季节性周期振荡不是单调指数趋势。GM(1,1)在这种数据上会把周期性当成噪声硬吃进去预测出来基本是一条平滑斜线完全失去季节特征。后来我们只对“剔除季节项后的趋势分量”用GM(1,1)效果才勉强合格。所以如果你打算用灰色模型请先检查数据是否光滑或者像我们一样对数据先做季节分解只对趋势成分做灰色预测。5.4 组合预测把不同模型的优点叠起来最终我们提交的组合预测模型是ARIMA和指数平滑的加权平均。组合权重的设定不加任何玄学就用简单等权或根据验证集误差最小化来定。我们留出最后30天做验证集算出两种模型的验证集误差然后按误差倒数确定权重。最后组合模型的RMSE比性能较好的单个模型还低了一点虽然幅度不算大但论文里可以理直气壮地说“组合预测降低了单一模型的系统性风险”。预测部分的表格要展示两个层次一是验证集里三种方法的RMSE、MAE、MAPE对比二是未来30天预测曲线的置信区间图。尤其是置信区间能让评委直观看到预测的不确定性比只给一条预测直线可信得多。6. 论文包装、灵敏度分析与三天赛程的时间管理6.1 灵敏度分析是容易被忽视的加分项很多队伍把模型建完就收工完全不做灵敏度分析太可惜了。评委看一篇论文除了看模型对不对还会看模型靠不靠谱。灵敏度分析就是用来回答这个问题的。最简单的做法包括三类改变回归模型的样本范围比如用前80%的数据重新拟合看系数是否稳定改变ARIMA的训练集长度用前24个月训练和用前18个月训练比较预测精度变化对关键参数做±10%的扰动看主导结论是否改变。我们当时做的是把训练集从24个月缩减到18个月重新跑了一遍ARIMA和组合预测发现MAPE只上升了不到3个百分点回归系数的正负号和显著性没有发生翻转于是在模型评价一节里放心地写了一句“模型对训练样本范围变化不敏感结论具有稳健性”。这一句话评委就能看出你认真做过检验。6.2 论文图表与结果呈现的几条硬经验我见过太多队伍模型做得不错但论文不忍直视最后只拿了成功参赛奖。论文呈现至少要在下面几个方面花心思摘要第一屏就要给出具体数据结论比如“冬季PM2.5均值比夏季高58%风速是影响PM2.5浓度的最主要气象因子”不要通篇都是“本文建立了某某模型”这种空话。所有图的坐标轴必须标单位时间序列图必须标清楚起点和终点多个序列对比时要用同一种配色和线型规范。主成分载荷矩阵、回归系数表、预测误差对比表这类核心表格一定要放在正文里不能只在附录出现。代码不要全放正文附录塞完整代码即可正文最多放一小段核心伪代码或算法步骤。还有一条关于写作顺序的提醒不要等所有模型跑完再写论文。正确做法是Day1晚上就开始写数据描述和现状分析部分Day2一边跑回归一边写方法论段落Day3只需要把预测结果和对策建议填进去。这样最后一天晚上你只改摘要和排版而不是从零开始憋论文。6.3 三天赛程的具体时间分配与现场经验五一赛的赛程名义上三天实际有效时间掐头去尾只有两天半。我们当时的排期可以作为参考时间段任务Day1上午读题、拆解任务、确定论文框架Day1下午到晚上数据清洗、描述统计、相关矩阵和聚类图Day2上午主成分分析、逐步回归、VIF检验Day2下午预测模型ARIMA GM 组合模型Day2晚上到Day3凌晨灵敏度分析、残差校验、补齐所有图表Day3全天论文写作、摘要打磨、排版检查这个排期里最关键的是Day1晚上必须出第一张能放进论文的图否则后面所有环节都会顺延。如果你们队伍经验不太足宁可砍掉一个复杂模型也要保住论文的完整性和思路连贯性。6.4 我们当年踩过的坑最后分享几个实实在在踩过的坑希望后来人不重蹈覆辙第一个坑是过度解读负相关。当时我们一度把NO2和O3的负相关解释成“两者存在相互转化”这本身不算错但论文里没写清楚原因被评委追问后我们才发现引用的大气化学反应机制表述不够准确。写解释性段落之前务必查一下基础理论别让统计结果和科学背景脱节。第二个坑是预测模型的还原单位。我们在做对数变换回归后忘了把预测结果从对数尺度还原回原始浓度导致预测值整体偏小。比赛提交前最后一小时才用逆变换修正。这里一定要在代码里留下清晰的还原逻辑。第三个坑是图表编号混乱。论文手在最后一晚调整图片顺序时有几张图编号和引用不一致后来只能一张一张手工核对。建议一开始就固定图表编号规则改图不挪号挪号必全文搜索引用位置。现在再看2015年这道B题它其实一点也不难难的是把一条完整的分析链条走完并写出说服力。环境数据分析类题目的套路就是这样拿到真实数据后先别急着上高级模型把数据看明白把相关结构和主控因素找出来再谈预测和对策。只要每一步都有清晰的目的和可验证的结论哪怕用的都是最经典的模型也能交出一份让评委觉得扎实的作品。
返回列表