3. 数据采集与预处理:数据来源、清洗、缺失值处理与标准化
好,咱们进入第三章。这一章聊的是数据预处理,说白了就是“把脏活累活干漂亮”。很多同学觉得建模、调参才是技术活,预处理就是体力活。我刚开始也这么想,直到有一次在项目里,因为一个传感器数据漂移没处理好,模型直接跑偏了20%的精度。嗯,从那以后,我再也不敢小看这一步了。
3.1 数据来源:监测站与遥感
空气质量数据从哪来?主要就两条路:地面监测站和卫星遥感。
地面监测站是最直接的。每个城市都有国控点、省控点,甚至有些企业自己建了微型站。这些站点实时采集PM2.5、PM10、SO₂、NO₂、CO、O₃这些常规指标。我个人习惯,拿到数据第一件事就是看时间戳——很多站点的数据上报有延迟,或者干脆跳过了某个小时,这坑我踩过。
卫星遥感呢,覆盖广,但精度差点。比如MODIS、VIIRS这些卫星,能反演气溶胶光学厚度(AOD),再换算成PM2.5浓度。说白了,遥感数据适合做大范围趋势分析,但具体到某个街道,还是得靠地面站。
我建议,做项目时尽量把两者结合起来。地面站数据做“真值”,遥感数据做“空间插值”。你想想看,监测站再密,也不可能每个小区都建一个,这时候遥感就能补上空白。
3.2 数据清洗:把垃圾数据筛出去
数据清洗,说白了就是“去伪存真”。我见过最离谱的情况——某个监测站连续三天报PM2.5为0,后来发现是传感器被鸟粪糊住了。这种数据不清理,模型学出来的全是错的。
常见的脏数据有这几类:
- 重复数据:同一时间戳出现多条记录。一般是采集程序bug导致的。
- 异常值:比如PM2.5突然飙到1000,但周围站点都正常。这大概率是传感器故障。
- 格式错误:时间戳格式不统一,有的用“2024-01-01 08:00”,有的用“2024/01/01 8:00”。
怎么处理?我一般用三步走:
- 去重:按时间戳和站点ID去重,保留第一条。
- 范围过滤:比如PM2.5的正常范围是0-500,超出这个区间的直接标记为异常。
- 一致性检查:比如温度不能是负数(除非在极地),风速不能是负值。
3.3 缺失值处理:别让空值毁了你的模型
缺失值在空气质量数据里太常见了。设备断电、网络中断、维护检修,都会导致数据缺失。你想想看,一个时间序列模型,中间缺了几个点,怎么算?
处理缺失值,常用的方法有这几种:
| 方法 | 适用场景 | 优缺点 |
|---|---|---|
| 直接删除 | 缺失比例很小(<5%) | 简单粗暴,但会损失样本 |
| 均值/中位数填充 | 数据分布稳定,缺失随机 | 计算快,但会降低方差 |
| 前向/后向填充 | 时间序列数据,短时间缺失 | 保留趋势,但长缺失会失真 |
| 插值法(线性/样条) | 缺失段较短,数据平滑 | 效果较好,但计算量稍大 |
| 模型预测填充 | 缺失比例高,数据复杂 | 精度高,但容易过拟合 |
我个人习惯,对于短时间缺失(比如连续缺1-2小时),用线性插值就够了。如果缺失超过6小时,我会用前向填充,或者干脆把这段数据标记为“不可用”。
代码示例,用pandas处理缺失值:
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('air_quality.csv', parse_dates=['timestamp'])
# 检查缺失值
print(df.isnull().sum())
# 线性插值(短时间缺失)
df['pm25'] = df['pm25'].interpolate(method='linear', limit=6)
# 前向填充(长时间缺失)
df['pm25'] = df['pm25'].fillna(method='ffill', limit=12)
# 标记剩余缺失值
df['pm25_missing'] = df['pm25'].isnull().astype(int)
3.4 数据标准化:让不同量纲的数据能一起玩
标准化,说白了就是把不同尺度的数据拉到同一个量级。PM2.5的单位是μg/m³,范围0-500;温度是℃,范围-10到40;风速是m/s,范围0-20。如果不做标准化,模型会天然偏向数值大的特征。
常用的标准化方法:
- Z-score标准化:减去均值,除以标准差。适合数据近似正态分布的情况。
- Min-Max归一化:缩放到[0,1]区间。适合数据有明确边界的情况。
- Robust标准化:用中位数和四分位距。适合数据有较多异常值的情况。
我建议,对于空气质量数据,优先用Robust标准化。为什么?因为空气质量数据经常有异常峰值(比如沙尘暴、烟花爆竹),这些异常值会严重拉偏均值和标准差。Robust标准化用中位数,抗干扰能力强得多。
代码示例:
from sklearn.preprocessing import RobustScaler
# 初始化标准化器
scaler = RobustScaler()
# 从训练集拟合参数
train_scaled = scaler.fit_transform(train_data)
# 应用到验证集和测试集
val_scaled = scaler.transform(val_data)
test_scaled = scaler.transform(test_data)
好了,这一章的内容就这些。数据预处理虽然琐碎,但绝对是决定模型上限的关键一步。下一章我们会聊特征工程,到时候你会看到,预处理做得好,特征工程事半功倍。