环境数据特征工程:数据清洗、缺失值处理、异常值检测与标准化
各位同学,咱们今天聊点实在的。做环境数据分析,最怕什么?不是算法不会,是数据太脏。我入行那会儿,接过一个空气质量监测项目,打开数据一看,好家伙,PM2.5 值有负数,温度记录出现 9999,还有一整片一整片的空白。那一刻我明白了——数据清洗不是预处理,是救命。
说白了,特征工程就是给数据「洗澡、整容、穿衣服」。洗掉脏东西,补上缺的,揪出离谱的,最后让所有特征站在同一个起跑线上。咱们一步步来。
核心观点: 环境数据天生带「病」。传感器漂移、传输丢包、人为录入错误,这些都是常态。特征工程做不好,后面建模就是垃圾进垃圾出。
1. 数据清洗:先把「垃圾」筛出去
数据清洗听着简单,做起来全是坑。我个人的习惯是,拿到数据先干三件事:看格式、查重复、找乱码。
格式统一——环境数据经常来自不同监测站,有的用「2024-01-15」,有的用「2024/01/15」,还有的用「15-Jan-2024」。不统一?后面时间序列分析直接崩。我一般上来就转成 pandas 的 datetime 类型,一步到位。
去重——传感器有时候会重复上报数据。你想想看,同一分钟同一个站点出现两条一模一样的记录,这合理吗?不合理。直接 drop_duplicates() 干掉。
乱码与异常字符——这个我踩过坑。有一次数据里混了不可见字符,肉眼根本看不出来,但模型训练就是报错。后来用正则把非数字、非字母、非标准符号的字符全筛了一遍才搞定。
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('air_quality.csv')
# 1. 统一时间格式
df['datetime'] = pd.to_datetime(df['datetime'], errors='coerce')
# 2. 去除完全重复的行
df = df.drop_duplicates()
# 3. 去除列名和数值中的空白字符
df.columns = df.columns.str.strip()
for col in df.select_dtypes(include='object').columns:
df[col] = df[col].str.strip()
print(f"清洗后数据量: {len(df)} 条")
小技巧: 清洗完数据后,我习惯用 df.info() 和 df.describe() 快速扫一眼。数据类型对不对?数值范围合不合理?一眼就能看出问题。
2. 缺失值处理:别让「空」毁了你的模型
环境数据缺失太常见了。传感器断电、通信中断、维护期间不采集……原因五花八门。怎么处理?没有万能答案,得看情况。
先搞清楚缺失比例。 我一般会算一下每列的缺失率。如果某个特征缺失超过 70%,我建议直接扔掉——补出来的也是猜的,还不如不用。
缺失少的,怎么补?
- 均值/中位数填充——最简单,适合数值型特征。但要注意,如果数据有偏态分布,用中位数比均值靠谱。我记得有一次处理 SO₂ 浓度数据,分布严重右偏,用均值填充直接把异常值的影响带进去了,后来换成中位数才正常。
- 前向/后向填充——时间序列数据的好朋友。比如温度传感器每 5 分钟采集一次,中间缺了一两个点,用前一个值填充完全合理。
- 插值法——线性插值、样条插值,适合变化平滑的环境指标。比如 PM2.5 浓度在短时间内不会突变,插值效果很好。
- 模型预测填充——用其他特征预测缺失值。这个方法精度高,但计算量大。我一般只在关键特征缺失时才用。
# 检查缺失率
missing_rate = df.isnull().mean() * 100
print("各特征缺失率(%):")
print(missing_rate[missing_rate > 0])
# 策略1:删除缺失过多的列
df = df.drop(columns=[col for col in df.columns if missing_rate[col] > 70])
# 策略2:中位数填充(数值型)
num_cols = df.select_dtypes(include=[np.number]).columns
for col in num_cols:
if df[col].isnull().sum() > 0:
df[col].fillna(df[col].median(), inplace=True)
# 策略3:前向填充(时间序列)
df['temperature'].fillna(method='ffill', inplace=True)
注意: 千万不要在填充前不做任何分析就全用均值填充。我曾经见过一个项目,把所有缺失的 PM10 值都填了均值,结果聚类分析时这些「假数据」自己聚成了一类,完全误导了分析方向。
3. 异常值检测:揪出那些「离谱」的数据
异常值是什么?就是那些明显不符合物理规律或统计规律的数据点。比如温度 -50°C 出现在夏天,或者 PM2.5 浓度突然飙到 5000(正常也就几十到几百)。
我个人常用的方法有三种:
方法一:IQR(四分位距法)
这个最经典。把数据从小到大排,Q1 是 25% 分位数,Q3 是 75% 分位数。IQR = Q3 - Q1。一般认为小于 Q1 - 1.5×IQR 或大于 Q3 + 1.5×IQR 的就是异常值。简单粗暴,但有效。
方法二:Z-score 法
计算每个数据点距离均值多少个标准差。一般 |Z| > 3 就认为是异常值。这个方法适合数据近似正态分布的情况。环境数据里很多指标其实偏态严重,用 Z-score 要小心。
方法三:领域规则法
这个我最推荐。结合环境科学的专业知识来定阈值。比如 PM2.5 浓度不可能超过 1000 μg/m³(仪器量程上限),温度不可能超过 -50°C 到 60°C 这个范围。这些规则比任何统计方法都可靠。
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers
# 检测 PM2.5 的异常值
pm25_outliers = detect_outliers_iqr(df, 'PM2.5')
print(f"PM2.5 异常值数量: {len(pm25_outliers)}")
# 结合领域规则:PM2.5 合理范围 0-500
df = df[(df['PM2.5'] >= 0) & (df['PM2.5'] <= 500)]
避坑指南: 我曾经处理一个水质监测数据,用 IQR 方法把「总磷」指标的异常值全删了。后来发现,那些「异常值」其实是雨季农田径流导致的真实高值。所以——异常值不等于错误值,先搞清楚原因再动手。
4. 数据标准化:让所有特征「平起平坐」
这一步很多人容易忽略。你想想看,温度范围是 -10°C 到 40°C,而 PM2.5 浓度是 0 到 300。如果直接拿去做聚类或 PCA,温度那点变化根本不够看,模型会天然偏向数值大的特征。这不公平。
标准化就是解决这个问题的。
常用的方法有三种:
| 方法 | 公式 | 适用场景 | 我的经验 |
|---|---|---|---|
| Z-score 标准化 | z = (x - μ) / σ | 数据近似正态分布 | 环境指标大多偏态,慎用 |
| Min-Max 归一化 | x' = (x - min) / (max - min) | 数据有明确边界 | 适合浓度类指标,如 PM2.5 |
| Robust 标准化 | x' = (x - median) / IQR | 数据含异常值 | 我最常用,抗干扰能力强 |
我个人最推荐 Robust 标准化。为什么?环境数据里异常值太常见了,Min-Max 会被异常值拉偏,Z-score 会被异常值拉偏均值。Robust 用中位数和 IQR,不受极端值影响,稳得很。
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# 选择要标准化的数值列
feature_cols = ['PM2.5', 'PM10', 'NO2', 'SO2', 'CO', 'O3', 'temperature', 'humidity']
# 方法1:Z-score 标准化
scaler_std = StandardScaler()
df_std = scaler_std.fit_transform(df[feature_cols])
# 方法2:Min-Max 归一化
scaler_mm = MinMaxScaler()
df_mm = scaler_mm.fit_transform(df[feature_cols])
# 方法3:Robust 标准化(推荐)
scaler_robust = RobustScaler()
df_robust = scaler_robust.fit_transform(df[feature_cols])
# 替换原数据
df[feature_cols] = df_robust
重要提醒: 标准化参数(均值、标准差、最小值、最大值等)一定要用训练集计算,然后应用到测试集。千万别把测试集的数据混进来一起算——这叫数据泄露,是数据挖掘的大忌。
好了,特征工程的四个步骤讲完了。数据清洗把脏东西筛掉,缺失值处理把坑填平,异常值检测把离谱的揪出来,标准化让所有特征站在同一起跑线。做完这些,你的数据才算「能用」。下一件事?嗯,那就是真正的聚类分析了。