首页 / 资讯中心 / 文章详情

用随机森林预测AQI:pandas数据清洗与机器学习实战

用随机森林预测AQI:pandas数据清洗与机器学习实战 ★ FEATURED ARTICLE
简介面向环境数据分析与机器学习初学者的AQI分析与预测实战资源以空气质量指数为核心完整覆盖污染物浓度及气象因素的数据采集、清洗、缺失值处理、标准化、探索性分析与多模型预测。压缩包共33个文件其中2个ipynb为可运行的完整代码1个csv为原始数据集2个html为交互可视化结果24个png展示分析图表另有md说明文档整体大小4.23MB目录清晰便于对照学习。已有197人学习下载。资源帮助读者掌握pandas、numpy、scikit-learn等库在真实场景中的应用理解线性回归、随机森林、GBDT等算法建模与交叉验证方法并通过MSE、RMSE、R²等指标评估模型可快速获得从数据预处理到结果可视化的完整项目经验。1. 先看懂 AQI 再动手这份代码和数据到底在做什么早上出门前看了一眼手机天气 AppAQI 172中度污染。大多数人只是把它当成一个数字但在接触过这个机器学习项目之后你会发现这个数字背后是一整套分段计算规则、一份能反映污染物之间相互关系的监测数据以及一条从数据清洗到模型预测的完整链路。这份《机器学习-03-AQI分析与预测》资源包含 Python 代码和一份空气质量监测数据。数据文件按照标准的环境监测字段组织日期、SO₂、NO₂、PM10、PM2.5、CO、O₃ 浓度以及对应的 AQI 数值。代码部分完整覆盖了用 pandas 读取数据、数据清洗、AQI 计算复现、探索性分析再到用随机森林预测 AQI 的全过程属于典型的机器学习入门实战项目。它适合正在学机器学习、想找真实数据练手的从业者也适合做期末大作业或数据分析练习的人。它能解决的问题很具体理解 AQI 怎么算、污染物之间怎么互相影响、怎么用历史浓度去预测未来的空气质量指数。下面按实际动手的顺序把它拆开讲。2. 用 pandas 复刻 AQI 计算规则分段线性插值与数据清洗AQI 不是监测仪器直接测出来的而是用六项污染物的浓度算出来的。先把这个底层逻辑立住后面看代码才不会一头雾水。2.1 从污染物浓度到 IAQI分段线性插值AQI 的计算依据是《环境空气质量指数AQI技术规定》HJ 633-2012。六项污染物——SO₂、NO₂、PM10、PM2.5、CO、O₃——各自都有浓度区间与空气质量分指数IAQI的对应关系。当浓度落在某个区间内时IAQI 由分段线性插值公式算出IAQI (IAQI_hi - IAQI_lo) / (C_hi - C_lo) × (C - C_lo) IAQI_lo这里 C 是实测浓度C_lo 和 C_hi 是浓度限值区间的上下界IAQI_lo 和 IAQI_hi 是对应的分指数上下界。最终 AQI 取所有污染物 IAQI 的最大值贡献最大 IAQI 的那个污染物就是首要污染物。以 PM2.5 的 24 小时平均浓度为例限值表如下浓度范围μg/m³IAQI 范围0 ~ 350 ~ 5035 ~ 7550 ~ 10075 ~ 115100 ~ 150115 ~ 150150 ~ 200150 ~ 250200 ~ 300250 ~ 350300 ~ 400350 ~ 500400 ~ 500拿到这份资源里的监测数据后第一步不是建模而是先在本地复现出 AQI 列。这样能验证数据文件里已有的 AQI 字段是否可信也能确认自己对计算规则的理解没有偏。下面这段代码用分段函数实现 PM2.5 的 IAQI 计算def calc_iaqi_pm25(concentration): breakpoints [ (0, 35, 0, 50), (35, 75, 50, 100), (75, 115, 100, 150), (115, 150, 150, 200), (150, 250, 200, 300), (250, 350, 300, 400), (350, 500, 400, 500), ] for c_lo, c_hi, iaqi_lo, iaqi_hi in breakpoints: if c_lo concentration c_hi: return (iaqi_hi - iaqi_lo) / (c_hi - c_lo) * ( concentration - c_lo ) iaqi_lo return 500 # 超过 500 直接封顶这段代码的关键在于区间匹配浓度落在哪个限值区间就用哪个区间的上下界做线性插值。如果浓度超过 500返回 500 封顶这是标准的保守处理。其他五项污染物的计算逻辑完全一样只是限值表不同资源包里的脚本通常把六项都实现了实际使用时直接调用即可。这里有一个容易翻车的细节单位。CO 的浓度用的是 mg/m³其他五项是 μg/m³差了一千倍。第一次跑这个脚本时如果发现 CO 的 IAQI 算出来异常偏大或偏小先检查是不是单位没换算这是典型的血泪经验。2.2 数据清洗决定模型上限的前置步骤数据质量直接决定后面所有分析的准确性。这个项目的数据文件里最常见的三个坑是时间字段不是标准格式、监测缺测值被写成 -999 或空值、同一日期出现重复记录。我的习惯是先用 pandas 把时间字段解析成 datetime 类型再排序和去重最后统一处理缺测标记。下面这段代码覆盖了这几个动作import pandas as pd import numpy as np df pd.read_csv(aqi_data.csv) df[date] pd.to_datetime(df[date]) df df.sort_values(date).drop_duplicates(subset[date]) df df.replace(-999, np.nan) print(df.isnull().sum())这里pd.to_datetime会把各种格式的日期字符串统一成时间戳方便后续按时间排序和聚合。drop_duplicates(subset[date])只保留每个日期的第一条记录replace(-999, np.nan)把监测站的缺测标记转成 pandas 能识别的 NaN这样isnull().sum()才能统计出每个字段到底缺了多少数据。缺测值怎么处理要看缺失比例如果某个字段缺测不超过 5%用前向填充ffill()通常没问题因为空气污染浓度有连续性昨天和今天的浓度不会突然跳变如果某个字段缺测超过 30%就要考虑直接剔除该特征否则填出来的都是噪声会让模型学出一堆假规律。2.3 批量计算 AQI 和首要污染物清洗干净后就可以对每一行数据计算六项 IAQI再取最大值得到 AQI。这里用 pandas 的 apply 加 max 操作几行就能完成pollutants [so2, no2, pm10, pm25, co, o3] for col in pollutants: df[fiaqi_{col}] df[col].apply(calc_iaqi) aqi_cols [fiaqi_{p} for p in pollutants] df[aqi_calc] df[aqi_cols].max(axis1) df[primary] df[aqi_cols].idxmax(axis1).str.replace(iaqi_, )max(axis1)按行取六项 IAQI 的最大值得到的就是 AQI。idxmax(axis1)返回最大值所在的列名再通过str.replace去掉前缀就得到了首要污染物。算出来的aqi_calc可以和原始数据文件里已有的aqi列做对比如果整体误差超过 5说明前面的清洗步骤有遗漏需要回头排查——这一步本身就是一个很好的验证闭环。3. 探索性数据分析相关性矩阵、时间趋势与特征工程起点数据清洗只是准备工作真正决定建模方向的是探索性数据分析EDA。这个阶段的目的只有一个搞清楚污染物之间的大致关系以及 AQI 随时间变化的规律为后面的特征工程提供依据。3.1 相关性矩阵找出 AQI 的主导变量pandas 作为 Python 数据科学库的核心价值在这一步体现得淋漓尽致。一行corr()就能算完整张相关性矩阵直接看 AQI 与各项污染物的相关系数排序corr_matrix df[[aqi, pm25, pm10, so2, no2, co, o3]].corr() print(corr_matrix[aqi].sort_values(ascendingFalse))输出的结果通常会有一个明显规律PM2.5 和 AQI 的相关性最高其次是 PM10 和 CO。PM2.5 排名靠前并不意外因为细颗粒物本身就是大多数城市的首要污染物但 CO 的排名往往比很多人预期的高因为 CO 和燃烧过程强相关而燃烧同时也会产生 PM2.5所以两者背后有共同的来源。看相关性的意义在于特征选择如果某两个特征之间的相关系数超过 0.9比如 PM10 和 PM2.5 在部分地区高度共线那么模型里同时放这两个特征意义不大还会增加冗余。这时候保留与 AQI 相关性更高的一方或者做特征合并是更干净的做法。3.2 时间趋势按月聚合看季节周期空气污染有很强的季节性。把数据按月份聚合能直观看到 AQI 和 PM2.5 的年内变化曲线df[month] df[date].dt.month monthly df.groupby(month)[[aqi, pm25]].mean() print(monthly)如果这份数据来自北方城市大概率能看到冬季月份 AQI 显著升高、夏季月份降低的走势。但这里要留意一个反直觉的可能夏季的 O₃ 浓度往往会形成第二个 AQI 峰值。因为 O₃ 是光化学反应产物高温强日照下浓度走高而 O₃ 的 IAQI 限值又比较严格夏季某些天 AQI 的主导污染物会从 PM2.5 切换成 O₃。在 EDA 阶段发现这种规律后面建模时就要考虑把季节特征或月份编码放进模型。3.3 滞后特征空气是有记忆的时间序列分析和普通回归最大的区别在于历史值对当前值有直接影响。今天的 AQI 和昨天的 AQI 高度相关这是常识也是可以拿来做特征的点。用 pandas 的shift方法可以轻松构造滞后特征df[aqi_lag1] df[aqi].shift(1) df[pm25_lag1] df[pm25].shift(1)shift(1)把整列向下平移一行第 2 行的值就是第 1 天的 AQI。这样构造出来的aqi_lag1在预测第 2 天的 AQI 时就能作为特征使用。注意平移后第一行是 NaN后面建模前要dropna()去掉否则模型会报错。滞后特征是把时间序列信息注入树模型的标准做法。后面章节里的模型主要就用这些滞后特征来做预测这也是整个 AQI 分析项目里最核心的特征工程手段。4. 用随机森林回归预测 AQI模型选型、参数设参与评估指标EDA 做完特征有了接下来进入核心环节预测。这个项目用的是随机森林回归选它而不选线性回归理由有三个树模型不需要做特征归一化省掉一步预处理能捕捉浓度和 AQI 之间的非线性关系自带特征重要性输出方便解释哪些预测变量作用最大。对机器学习入门项目来说这三个特性都很友好。4.1 按时间切分而不是随机切分训练集和测试集的划分方式是这个项目里最容易出问题的地方。很多人在入门时不自觉地用train_test_split默认的随机切分这在普通回归任务里没问题但在时间序列任务里是致命的随机切分会把未来的数据混进训练集模型相当于考试时偷看了答案。正确的做法是按时间顺序切分让训练集全部在测试集之前from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score features [ aqi_lag1, pm25_lag1, pm10_lag1, so2_lag1, no2_lag1, co_lag1, o3_lag1 ] X df[features].dropna() y df[aqi].loc[X.index] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, shuffleFalse )这里的shuffleFalse是关键参数它让train_test_split不洗牌直接取前 80% 作为训练集、后 20% 作为测试集。这样模型的预测对象是完全没见过的未来时间段评估结果才具有真实参考价值。如果数据文件的时间跨度覆盖了完整的两年以上用前一年半训练、后半年验证是比较合理的比例。顺带一提dropna()在这里不是可选项。因为第一行的aqi_lag1一定是 NaN如果不删掉模型训练时会出现特征全部为空的行。4.2 随机森林参数怎么设从默认值出发小步调整随机森林的超参数不少但对这个项目来说真正需要关注的只有三个n_estimators、min_samples_leaf、max_features。我的常用起点是这样model RandomForestRegressor( n_estimators200, min_samples_leaf2, max_featuressqrt, random_state42 ) model.fit(X_train, y_train)n_estimators200代表训练 200 棵决策树。对 AQI 这种规模的数据集200 棵树的精度已经接近上限再增加到 500 棵只会增加训练时间精度提升非常有限。min_samples_leaf2限制叶子节点的最小样本数防止树对个别异常 AQI 数值过拟合如果想进一步正则化可以调到 3 或 5。max_featuressqrt是回归任务的默认值即每次分裂只随机选取部分特征参与计算增加树之间的差异性。random_state42固定随机种子保证下次跑出来的结果完全一致否则每次运行评估指标都会轻微浮动不方便对比实验。调参这块多少带点玄学但原则很清楚先用默认参数跑通流程再逐个调整超参数观察验证集指标变化不要一上来就网格搜索数据量小的时候网格搜索很容易过拟合验证集。4.3 评估指标怎么读R² 高不代表一切模型训练完用测试集做预测然后算三个最常用的回归指标y_pred model.predict(X_test) print(MAE:, mean_absolute_error(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(R2:, r2_score(y_test, y_pred))MAE 是平均绝对误差它的含义最直观比如 MAE12就代表预测的 AQI 平均偏了 12 个单位。RMSE 对更大误差更敏感如果某个异常天预测差了 80RMSE 会被这个样本拉高很多。R² 代表模型解释了测试集方差的多少在这个场景里 R² 在 0.7 到 0.85 之间都是合理水平。但要特别提醒AQI 序列自相关很强即使拿「昨天的 AQI 当今天的预测值」这种无脑 baselineR² 也可能达到 0.8 左右。所以不要单独看 R²一定要同时对比 MAE 和 RMSE。如果模型 R² 到了 0.95 以上先怀疑数据泄露而不是高兴。5. 避坑指南AQI 预测项目最容易翻车的六个细节这个项目看起来简单但跑过一遍之后你会发现坑都藏在细节里。下面六条是实际项目中反复出现的踩坑记录按「现象 → 原因 → 解决」的方式列出。5.1 R² 接近 1 的「完美模型」现象模型在测试集上 R² 高达 0.98MAE 只有 3 左右堪称完美。但换一段新数据立刻崩盘预测值几乎不变。原因数据泄露。最常见的泄露是把当天的aqi本身放进了特征矩阵或者把当天某个污染物的实测浓度当成了预测特征。AQI 由这些浓度直接计算而来模型等于直接拿到了答案。解决严格使用滞后特征所有特征的时间戳必须早于预测目标。如果预测当天的 AQI特征只能来自昨天及之前的数据。5.2 shuffleTrue 导致的虚假高精度现象用默认的train_test_split测试集 MAE 非常好看模型上线后误差却大得离谱。原因随机切分把时间序列打乱了训练集里混入了测试集时间段的数据。模型实际上见过了未来的空气状况测试集变成了开卷考试。解决时间序列数据一律用shuffleFalse按时间切分或者用TimeSeriesSplit做交叉验证。这是机器学习模型中时间序列任务和普通任务最重要的差别之一。5.3 缺测值填 0冬季 AQI 系统性偏低现象重算出来的aqi_calc整体低于数据文件里的aqi字段尤其在冬季月份偏差最大。原因监测站在冬季设备故障率高缺测值被读成 NaN 后很多人顺手fillna(0)把「没测到」当成了「浓度为 0」。AQI 计算里浓度越低IAQI 越低算出来的 AQI 自然整体偏低。解决缺测值不能填 0。先用前向填充ffill()或线性插值interpolate()如果某个污染物缺测比例超过 30%直接放弃这个特征而不是硬填。5.4 用了「当天」的气象特征做预测现象训练时特征里有当天的温度、湿度测试集精度不错。但部署到实际环境时发现当天还没结束当天的气象数据根本拿不到。原因训练时用了实时数据预测时却只有预报数据特征分布不一致模型自然失效。解决预测当天 AQI只用截至昨天为止能拿到的数据。比如当天温度的预测值不是不能用但要在特征工程时明确区分「已观测」和「待预测」两类特征模型只能吃前者。5.5 只盯着 R²忽略了 MAE 的真实含义现象模型 R² 有 0.85看起来不错但 MAE 有 25。对 AQI 来说25 的误差意味着中度污染和轻度污染的分界敏感人群的出行建议会完全不同。原因R² 是相对指标受序列波动幅度影响大。AQI 序列如果年内波动大R² 天然偏高MAE 才是用户实际感受的误差水平。解决评估时同时打印 R²、MAE、RMSE并画残差图。如果残差在 AQI 高值区间系统性偏大说明模型对污染严重的时段预测能力不足这类样本恰恰是最重要的。5.6 忽略了首要污染物的业务含义现象两个日期的 AQI 都是 150模型预测结果也一样但一个是 PM2.5 主导一个是 O₃ 主导。原因AQI 是一个聚合指标它只告诉你污染有多重不告诉你什么污染物在作怪。模型如果只预测 AQI 数值就丢失了这层信息。解决在模型之后加一个简单的分类步骤用同一套滞后特征预测首要污染物类别或者至少在结果输出时附上近几天各污染物浓度的趋势方便人工判断。6. 进阶验证用滚动预测让模型在真实时间线上走一遍固定的一次性训练测试划分只能反映某个时间段的表现不能证明模型在更长时间里稳定可用。AQI 预测模型最有效的验证方式是 walk-forward 验证也叫滚动预测沿着时间轴不断用过去的数据训练预测接下来的几天再前进一个窗口重复这个过程。下面这段代码实现了一个基础的滚动验证函数def rolling_validate(df, features, targetaqi, train_size365, horizon7): preds, trues [], [] for start in range(0, len(df) - train_size - horizon, horizon): train df.iloc[start:start train_size] test df.iloc[start train_size:start train_size horizon] model RandomForestRegressor(n_estimators100, random_state42) model.fit(train[features], train[target]) pred model.predict(test[features]) preds.extend(pred) trues.extend(test[target].values) return preds, trues preds, trues rolling_validate(df, features) print(Rolling MAE:, mean_absolute_error(trues, preds))train_size365表示每次用过去一年的数据训练horizon7表示每次预测未来一周。循环每走一次训练窗口就向后推进 7 天。这样得到的误差反映的是模型在「完全没见过这段未来」情况下的真实表现比单次切分可信得多。跑完以后把预测值和真实值按时间画在同一张图上还能直观看到一段关键问题预测曲线是不是总比真实曲线慢半拍。AQI 序列的自相关性很强如果用 lag1 特征做主力模型最容易出现的毛病就是把昨天的 AQI 几乎原样搬到今天表现就是预测曲线滞后真实值一天。如果滚动验证时发现这种情况考虑把aqi_lag1换成aqi_lag2甚至aqi_lag3或者把滞后特征和周均值特征一起用滞后现象通常会明显缓解。从那以后我每次做完 AQI 预测模型不管数据多干净都强制走一遍滚动验证再对比扫一眼预测曲线有没有滞后。这个习惯帮我避掉了至少三次「看着指标不错、一上线就翻车」的局面。希望这个检查步骤也能帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站