news 2026/10/14 12:55:02

线性回归实现PM2.5预测:从数据清洗到滚动预测全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
线性回归实现PM2.5预测:从数据清洗到滚动预测全流程解析

简介:基于线性回归的PM2.5预测系统Python源码,面向环境数据分析、机器学习入门及课程设计人群,解决从原始监测数据到浓度预测的完整回归建模问题。压缩包共19个文件,以12个csv数据文件为主体,覆盖训练集、测试集及特征拼接、中间变量等处理结果;另有1个py主程序、1个npy模型参数文件和5张过程截图,大小仅2.14MB,轻量便于学习。该代码读取繁体编码的训练数据,按小时维度整理18类污染物指标,通过特征构造、最小二乘正规方程训练,最终输出预测值csv,流程完整。目前已有594人浏览学习。读者可直接调参运行并对照数据文件核验中间结果,借助截图与模型参数理解关键步骤;通过观察arrayx、concatenateX等中间矩阵,还能弄清特征拼接与数据标准化的具体实现,是机器学习课程设计、毕业论文实验或PM2.5回归预测入门的一份完整可运行参考资料。

1. 线性回归做PM2.5预测:先搞定基线,再谈花活

做空气质量预测,十有八九是从线性回归起步的。PM2.5浓度与历史浓度、气象条件之间存在大量可被线性模型捕捉的关联,小时级数据上前几小时的浓度对当前值有极强的自相关。这套基于线性回归的PM2.5预测系统,本质是把“历史浓度+气象特征”映射到未来浓度的回归模型,训练快、可解释、易部署,适合做业务初版和复杂模型的对标基线。它解决的不是“预测最准”,而是“用最小成本换来稳定可用的结果”。

适合两类人:刚接触时序预测的开发者,想看清从数据清洗、滞后特征到训练评估的完整链路;已有深度学习方案的工程师,想拿它当对照基线。这篇从原理讲到代码,再落到高频踩坑点,照着做就能把整套系统搭起来。

2. 线性回归预测PM2.5的原理与数据准备:特征矩阵决定上限

2.1 线性回归为什么能预测PM2.5:机理与适用边界

线性回归假设目标变量 y 是特征 x 的线性组合加噪声,写成公式是 y = β0 + β1x1 + … + βnxn + ε,训练时用最小二乘法求一组系数 β,让预测值与真实值的平方误差和最小。这个过程没有任何黑匣子,每个系数都对应一个特征的影响方向和大小,这也是它至今仍是工业界基线首选的原因。

PM2.5 的短期浓度变化能用线性模型描述,核心支撑有两点。第一是惯性:大气污染物的扩散和清除需要时间,当前小时浓度与过去几小时浓度高度相关,滞后项能捕捉这段自相关,而自相关是时序预测里最值钱的信息。第二是气象驱动:风速决定稀释能力,湿度影响二次生成,气压关系到垂直扩散,这些气象变量在正常波动范围内对浓度的贡献近似线性。把这两类信息编成特征,线性回归就能拟合出一条可用的预测线。

但必须认清边界。二次化学反应、逆温层、沙尘输送这类强非线性过程,线性回归天生预测不了极端峰值。实际项目里我一般把它定位成短期基线:预测未来1到24小时的浓度趋势,评估重点放在正常波动区间的拟合质量,而不是搏极端事件。如果业务需要预测重污染红警,再往上叠加树模型或深度学习,但线性回归这条基线必须存在——它是判断复杂模型是否真有用的尺子。

2.2 数据从哪来:监测字段、时间对齐与样本组织

这套系统最常用的数据源是空气质量监测站的小时级记录,字段一般包括 PM2.5、PM10、SO2、NO2、CO、O3 六项污染物浓度,加同时段的气温、气压、湿度、风速、风向。不同来源的数据粒度可能不同,有的按整点,有的按分钟,先统一再使用。

拿到数据先做三件事:按时间升序排序并去重、处理缺失、校正字段类型。监测设备断网重传经常产生重复记录,时间戳偏移也很常见,表现为整点记录落在 00:01 或 23:59。我一般先把时间戳取整到小时,再按“同一时刻只保留最后一条”去重。

# 数据加载与时间整理 import pandas as pd df = pd.read_csv('air_quality.csv', parse_dates=['datetime']) df['datetime'] = df['datetime'].dt.floor('h') # 取整到小时 df = df.sort_values('datetime').reset_index(drop=True) # 时间升序 df = df.drop_duplicates(subset=['datetime'], keep='last') # 同刻只留一条 print(df.shape, df.dtypes)

dt.floor('h')会把 00:01 归到 00:00,避免滞后特征串位。sort_values加drop_duplicates是最低保障,后面所有特征构造都依赖这一步的正确性。字段类型也要顺手检查,湿度、风速这类列如果被读成字符串,后面所有运算都会报错。

提示:时间序列数据严禁在排序前做滚动或滞后操作,否则滞后值会串到别的时刻上,训练完的模型全是错觉。

2.3 特征矩阵构建:滞后项、滚动统计量与环形气象编码

特征工程是这套系统里性价比最高的环节。我常用的特征分三类:目标变量滞后项,取 lag1、lag3、lag6、lag24,覆盖短期惯性和日周期;滚动统计量,例如过去6小时 PM2.5 均值,相当于简易低通滤波,抑制单小时尖刺噪声;气象特征,其中风向是环形变量,0度和360度含义完全相同,直接拿数值塞进模型会让回归系数莫名其妙,推荐拆成 sin 和 cos 两个分量。

特征类别特征名构造方式作用
滞后项pm25_lag1 / lag3 / lag6 / lag24shift(n)捕捉短期惯性与日周期
滚动统计pm25_roll6_meanrolling(6).mean()平滑单小时尖刺
气象temp / pressure / humidity原始数值气象驱动
气象wind_speed原始数值扩散能力
风向wind_sin / wind_cos角度三角函数环形变量编码
# 构造滞后、滚动与风向特征 import numpy as np def build_features(data, target='pm25', lags=(1, 3, 6, 24)): df = data.copy() for lag in lags: df[f'{target}_lag{lag}'] = df[target].shift(lag) df[f'{target}_roll6_mean'] = df[target].rolling(6).mean() df['wind_sin'] = np.sin(np.deg2rad(df['wind_direction'])) df['wind_cos'] = np.cos(np.deg2rad(df['wind_direction'])) return df.dropna().reset_index(drop=True) df = build_features(df) print(df.head())

shift和rolling都会在序列头部产生 NaN,dropna会顺带丢掉最前面几行,这是正常现象,不是数据错误。滞后档位的选择跟预测步长强相关:预测下一小时,lag1 的权重通常最大;预测未来24小时,lag24 和滚动均值的作用更突出。这个函数默认给四档滞后,覆盖小时级预测最常用的配置,想预测更长周期就自己加 lag48、lag72。

3. 用Python源码搭训练流程:数据清洗、特征构造到预测一行不落

3.1 数据清洗:缺失值插值、异常值截断与脏数据兜底

监测设备断网、标定维护都会造成整段缺失。线性回归对缺失值零容忍,scikit-learn 的 LinearRegression 遇到 NaN 直接抛错。常见做法是线性插值加阈值截断组合:短缺失用前后值插值补上,长缺失整段丢弃,因为连续缺失超过6小时时插出来的值基本是猜,带进模型只会污染系数。

# 缺失值插值与异常截断 df = df.interpolate(method='linear', limit=6) # 最多连续插值6小时 df = df.dropna() # 超长缺失直接丢弃 cap = df['pm25'].quantile(0.995) # 99.5分位作为上限 df['pm25'] = df['pm25'].clip(upper=cap)

interpolate的limit参数是关键,它限制连续插值长度,超过的部分保留为 NaN 再dropna丢掉。这个组合比一刀切fillna(0)或fillna(mean)靠谱得多,也避免把设备故障期间的假数据当成真实波动。异常处理上用分位数截断而非三倍标准差,原因是 PM2.5 分布右偏严重,正常高污染日的浓度就可能超过均值加三倍标准差,直接删会误伤样本,截断则保留样本量,同时压制极端值对回归线的拉扯。

3.2 特征与标签切分:时间顺序交叉验证,不能随机打乱

分类任务可以随机打乱数据,时间序列不行。随机划分会让模型在训练时看到测试时刻之前的未来信息,验证指标虚高,部署上线后立刻现原形。这是整套系统里最隐蔽的坑,后面 5.1 会细讲。这里先用 TimeSeriesSplit 按时间顺序做交叉验证。

# 时间顺序交叉验证 from sklearn.model_selection import TimeSeriesSplit features = ['pm25_lag1', 'pm25_lag3', 'pm25_lag6', 'pm25_lag24', 'pm25_roll6_mean', 'temp', 'pressure', 'humidity', 'wind_speed', 'wind_sin', 'wind_cos'] X = df[features].values y = df['pm25'].values tscv = TimeSeriesSplit(n_splits=5) for fold, (tr_idx, te_idx) in enumerate(tscv.split(X)): X_tr, X_te = X[tr_idx], X[te_idx] y_tr, y_te = y[tr_idx], y[te_idx] # 每一折训练集都严格早于测试集

TimeSeriesSplit与KFold的本质区别就在这里:它不洗牌,且每一折的训练集时间上严格早于测试集。用它的前提是原始数据已经按时间升序排好,这一点忘了检查,后面所有评估指标都是白算。n_splits 设5到10都行,小时级数据样本量大,5折足够;折数太多会缩短每折训练长度,折数太少评估方差大。

3.3 训练与预测:LinearRegression 最小实现与结果解读

# 线性回归训练与评估 from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score import numpy as np model = LinearRegression() model.fit(X_tr, y_tr) y_pred = model.predict(X_te) rmse = np.sqrt(mean_squared_error(y_te, y_pred)) r2 = r2_score(y_te, y_pred) print(f'RMSE={rmse:.2f} ug/m3, R2={r2:.3f}')

这段代码不需要标准化。普通线性回归对特征量纲不敏感,系数会自动适应不同尺度,滞后项浓度是几十到几百,气压是上千百帕,模型照样收敛。如果换成 Ridge 或 Lasso 正则化,才必须提前做标准化,否则正则项会对大数值特征惩罚过重,这一点很多人搞反。

跑完之后先看 RMSE 的量级:如果 RMSE 和测试集浓度的标准差差不多,说明模型几乎没学到信息,优先回头查特征和清洗流程,别急着换模型。加了 lag1 之后 R² 通常能做到 0.7 以上,达不到这个水平,数据链路大概率有 bug。每折都打印这两个指标,观察折与折之间的稳定性,比单看某一折的结果有意义得多。

4. 模型评估与调参:R²、RMSE、正则化与残差诊断

4.1 评估指标怎么选:R²看拟合度,RMSE看业务误差

评估回归模型,最常用的三个指标各有侧重。R² 衡量模型解释了多少方差,越高越好,但容易被极端值拉高;RMSE 是误差的均方根,单位和原始数据一致,直接对应业务上的“平均偏差多少微克”;MAE 是绝对误差均值,比 RMSE 更抗异常值干扰。小时级 PM2.5 预测里,我一般同时报 RMSE 和 R²,RMSE 给业务看,R² 给技术评审看。

指标含义适用判断注意点
R²1 - 残差平方和/总平方和模型整体拟合度极端值会虚高
RMSE均方误差开根号业务实际偏差对异常值敏感
MAE绝对误差均值稳健偏差不反映大误差

这三个指标都不能只看训练集。训练集 R² 很高、测试集掉一半,几乎可以断定过拟合,先用 4.3 的残差分析定位,再考虑加正则化或砍特征。还有一个常用技巧:把测试集按季度切片分别算 RMSE,能直观看到模型在冬夏两季的表现差距,这比一个全局指标更有业务参考价值。

4.2 正则化选型:Ridge还是Lasso,取决于共线性结构

特征多了之后,共线性会让最小二乘解的系数方差变大,训练集上拟合得很好,测试集上飘。两个常用解法:Ridge 加 L2 惩罚,把系数往零压但不真正归零,适合特征间存在相关性的场景;Lasso 加 L1 惩罚,会把不重要的系数直接压成零,自带特征选择。

# Ridge与Lasso对比 from sklearn.linear_model import Ridge, Lasso from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline scaler = StandardScaler() ridge = make_pipeline(scaler, Ridge(alpha=1.0)) lasso = make_pipeline(scaler, Lasso(alpha=0.01)) for name, pipe in [('ridge', ridge), ('lasso', lasso)]: pipe.fit(X_tr, y_tr) y_p = pipe.predict(X_te) print(name, f'R2={r2_score(y_te, y_p):.3f}', f'RMSE={np.sqrt(mean_squared_error(y_te, y_p)):.2f}')

注意这里用了 StandardScaler,配合make_pipeline保证标准化和训练在同一条流水线里,避免在测试集上泄漏统计量。alpha 的取值要靠小网格搜索调,我一般从 0.01、0.1、1、10 几档里选交叉验证 RMSE 最低的。PM2.5 预测这个场景里,Ridge 通常比 Lasso 稳,因为气象特征之间多少都相关,L1 硬切容易把有用信息一起删掉。

4.3 残差分析:模型失效的信号灯

训练完别急着收工,画残差图是排查模型问题最直接的手段。把 y_te - y_pred 按时间顺序画出来,如果残差围绕零线随机波动,模型基本正常;如果出现明显的上升或下降趋势,说明有未建模的时间效应,比如季节漂移;如果残差方差随时间变大,说明模型在浓度高的时候系统性失灵。

# 残差趋势检查 import matplotlib.pyplot as plt residual = y_te - y_pred plt.figure(figsize=(12, 4)) plt.plot(residual, alpha=0.6) plt.axhline(0, color='red', linestyle='--') plt.title('Residual over time') plt.show()

残差还有一个高频特征:重污染时段残差为正且很大,说明线性模型系统性低估峰值,这符合 2.1 里说的非线性边界。如果业务确实需要峰值预测,就得在特征里加入风速湿度等气象交互项,或者换树模型。残差分析不需要每折都做,但至少要在最后一折上做一次,作为上线的放行条件之一。

5. 避坑指南:PM2.5线性回归预测的五个真实踩坑记录

5.1 数据泄露:随机划分让指标虚高,上线就翻车

现象:训练集 R² 0.91,测试集 R² 0.89,漂亮得不像话,结果部署到线上实时预测,误差比测试时大了将近一倍。

原因:代码里用了train_test_split默认的随机划分。PM2.5 序列自相关极强,随机切分会让训练集里混入测试时刻前后的样本,模型等于提前看到了答案,测试指标纯属自欺欺人。

解决:按 3.2 的方式改用 TimeSeriesSplit,或直接按时间比例手动切分,保证训练集永远在测试集之前。所有涉及时间的特征构造(shift、rolling)也必须在切分之前完成,且不能引入未来信息。这是时序预测的头号血泪教训,没有之一。

5.2 预测值出现负数:浓度不可能是负的

现象:预测结果里出现 -10、-25 这类负数,业务方直接质疑数据有问题。

原因:线性回归没有非负约束。当特征组合落入训练数据稀疏的区域,回归平面外推,预测值就可能越过零轴。PM2.5 浓度分布左端紧贴零,外推时最容易出现负值。

解决:最简单的是np.clip(y_pred, 0, None)在输出端兜底。更讲究一点,把目标变量取对数再回归,预测完再指数还原,这样预测值天然为正,同时压缩右偏分布的影响。注意取对数后 RMSE 要在对数域计算,别拿还原后的值直接算。

5.3 多重共线性:PM10和PM2.5打架,系数符号反转

现象:PM2.5 和 PM10 高度相关,单独只放一个时 R² 差不多,两个都放时系数符号突然反转,PM10 系数变负。

原因:共线性特征之间可以相互替代,最小二乘解在相关方向上不稳定,系数估计方差被放大,符号翻转是典型表现。回归系数的“黑匣子”效应在这里体现得最明显。

解决:先看相关系数矩阵,把相关性超过 0.8 的特征合并,或保留其中业务意义更明确的一个。PM10 和 PM2.5 同源,实际项目里我通常保留目标滞后项,把 PM10 作为辅助特征或直接去掉。也可以用 Ridge 缩小系数方差,但不解决解释性问题。

5.4 异常值把回归线带偏:一次标定故障毁掉一折训练

现象:模型整体 RMSE 正常,但某天重污染时误差特别大,且系数明显偏向那个方向。

原因:监测设备标定异常会产出离谱的高值,比如某个小时 PM2.5 记录到 3000。线性回归的损失函数是平方误差,一个 3000 的样本对回归线的拉扯力度相当于几百个正常样本。

解决:用 3.1 的 clip 截断,或把极端值缩到分位数边界。更稳的做法是用 HuberRegressor 代替 LinearRegression,它对异常值的鲁棒性天生更强,损失函数在残差大于阈值时从平方退化为线性,高污染日的数据不会单点带偏整条回归线。

5.5 标准化后忘记还原预测值:数字对不上领导

现象:评估时 RMSE 看着挺小,但预测值和真实浓度画在同一张图上差了一个量级,全是零点几的小数。

原因:训练前对特征或者目标做了 StandardScaler,预测结果自然落在标准化后的尺度上,没做 inverse_transform 还原。

解决:目标变量不要标准化,保持原始浓度单位;如果真的标准化了,预测后必须用scaler.inverse_transform(y_pred)还原。如果用的是make_pipeline包标准化,预测输出会自动反标准化。凡是训练和预测走了两条路的,都要检查是否做了可逆变换,这条经验同样适用于其他回归项目。

6. 从静态模型到滚动预测小系统:重训练、可视化与输出

6.1 滚动预测与重训练:让模型跟上季节漂移

静态模型一次性训练完就上线,PM2.5 的浓度分布会随季节漂移,冬夏差异巨大,模型用久了精度必然下滑。常见做法是滚动预测加定期重训练:每天用最新数据预测未来24小时,每7天用最近三个月的数据重新训练一次,既保证样本量,又跟上漂移。

# 滚动预测示例:用当前特征递推未来24小时 def rolling_predict(model, df, steps=24): preds = [] temp = df.copy() for _ in range(steps): x = temp[features].iloc[-1:].values pred = model.predict(x)[0] preds.append(max(0, pred)) # 负数兜底 new_row = temp.iloc[-1:].copy() new_row['pm25'] = pred # 预测值接回序列 temp = pd.concat([temp, new_row], ignore_index=True) return preds

滚动预测的关键是“预测即数据”:每预测出一步,就把结果接回序列尾部,供下一步的滞后特征使用,否则多步预测会退化成重复同一个值。这种递推方式误差会累积,预测步数越远越不可信,所以24小时以上的预测建议直接用 lag24 特征一次到位,而不是滚动24次。重训练我放在一个定时任务里,每次拉最新数据、重建特征、重新拟合,用模型文件覆盖旧的。

6.2 可视化与结果输出:把预测变成业务能用的东西

模型跑通只是第一步,落地要解决“预测结果给谁看、怎么用”。可视化我一般画两样:一是近48小时真实值与预测值的对比曲线,直接反映模型状态;二是未来24小时的预测曲线加置信带,置信带用历史残差的标准差近似,业务看到的是一个区间而不是一个单点。

# 预测结果导出 result = pd.DataFrame({'datetime': future_time, 'pm25_pred': preds}) result.to_csv('pm25_forecast.csv', index=False)

导出格式保持与监测数据同构,带上时间戳和浓度单位,这样下游的告警、报表、大屏可以直接消费。我这套系统跑完给我的最大教训是:评估指标再漂亮,也不如一个能稳定输出的 CSV 文件有价值。所有后处理逻辑——负数截断、置信带、单位标注——必须在导出前统一处理好,别把原始预测丢给下游让人家二次加工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 12:54:49

电力系统同步相量计算算法对比与工程实践

1. 电力系统同步相量计算的核心价值 电力系统同步相量测量是智能电网运行控制的基础技术。当我在某省级调度中心第一次见到相量测量单元(PMU)的实时数据刷新时,瞬间理解了这项技术的革命性意义——它让我们首次获得了电网动态过程的"显微镜"。 传统SCADA…

作者头像 李华
网站建设 2026/10/14 12:53:37

机器人空间感知进阶:地图中间件与自然语言查询实战

接到 lingbot-map 这个标题,我的第一反应是:这不只是一个起得挺随意的内部代号,而是一个很典型的“给智能助手补上空间感知能力”的项目。说白了,就是在机器人对话、任务调度的逻辑里,插入一层地图服务中间件&#xff…

作者头像 李华
网站建设 2026/10/14 12:53:36

运行时错误分析工具REA:日志归因与告警收敛实战

在线上服务兜兜转转做了几年,最怕的其实不是需求改来改去,而是深更半夜手机突然震动:线上某个服务报错率飙升,日志翻了几页也没定位到根因。这种问题多了以后,我决定写一个小工具,代号就叫 REA(…

作者头像 李华
网站建设 2026/10/14 12:53:35

Oracle EBS 物料清单管理:数据模型、API 建单与避坑指南

简介:这份PPT资料面向Oracle EBS初学者、实施顾问及企业信息化人员,系统讲解物料清单管理模块的标准功能与操作逻辑,帮助读者快速建立对生产数据管理与工程数据管理的整体认知。资源包内含1个pptx文件,约955KB,以图文幻…

作者头像 李华
网站建设 2026/10/14 12:53:35

公益网站毕业设计:从功能实现到学术价值的提升

1. 选题背景与项目概述 去年参与指导本科生毕业设计时,遇到一个典型案例:计算机专业的小张同学在开题答辩环节,其《志愿者公益网站的设计与实现》选题连续两次被答辩组要求修改方案。这个现象引发了我的思考——看似简单的公益类网站项目&…

作者头像 李华
网站建设 2026/10/14 12:53:35

Python字符串方法全解析:从基础到高级应用

1. Python字符串方法概述 字符串是Python中最基础也是最常用的数据类型之一。作为动态类型语言,Python为字符串对象内置了丰富的方法,这些方法极大简化了日常开发中对文本数据的处理工作。不同于其他语言需要导入额外库才能进行字符串操作,Py…

作者头像 李华