数据清洗与预处理:缺失值处理、异常值检测、时间戳对齐、数据标准化
各位同学,欢迎来到数据清洗与预处理这一节。
说实话,风场数据是我见过最「脏」的数据之一。传感器在户外风吹日晒,信号干扰、设备漂移、通讯丢包……你拿到的原始数据,基本不可能直接用来做分析。我当年第一次做风场项目时,拿到数据就急着建模,结果模型一跑,效果惨不忍睹。后来才发现,数据里全是坑。
所以这节课,咱们就把这些坑一个一个填上。
缺失值处理:别让空值毁了你的模型
风场数据里,缺失值太常见了。传感器偶尔掉线、网络传输丢包,都会导致某个时间点的数据为空。你想想看,如果一条时间序列里突然缺了几个点,后续的FFT分析、趋势预测全都会出问题。
常见的缺失值处理方法有三种:
- 删除法:直接丢掉含缺失值的行或列。简单粗暴,但数据量少的时候别用,会心疼。
- 填充法:用均值、中位数、众数或前后值填充。我个人习惯用前向填充(ffill),因为风场数据变化相对平缓,前一个值往往比均值更靠谱。
- 插值法:线性插值、多项式插值、样条插值。精度高,但计算量大。我在项目中遇到过传感器连续掉线10分钟的情况,这时候用线性插值效果就很好。
核心原则:缺失率低于5%时,删除法可以接受;高于5%时,建议用插值或填充。千万别一股脑全删了,数据宝贵啊。
import pandas as pd
import numpy as np
# 模拟风场数据
df = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=100, freq='1min'),
'wind_speed': np.random.randn(100) * 5 + 10,
'wind_direction': np.random.randn(100) * 30 + 180
})
# 人为制造缺失值
df.loc[10:15, 'wind_speed'] = np.nan
df.loc[30:32, 'wind_direction'] = np.nan
# 前向填充
df['wind_speed'].fillna(method='ffill', inplace=True)
# 线性插值
df['wind_direction'].interpolate(method='linear', inplace=True)
print(df.isnull().sum()) # 检查是否还有缺失值
异常值检测:揪出那些「离谱」的数据点
什么叫异常值?说白了,就是那些明显不符合物理规律的数据。比如风速突然飙到200m/s,或者风向在1秒内转了180度。嗯,这里要注意,异常值不一定是错误,也可能是真实事件(比如阵风),但大多数情况下是传感器故障。
我常用的检测方法:
- 3σ原则:数据服从正态分布时,超出均值±3倍标准差的值视为异常。简单,但风场数据往往不服从正态分布。
- IQR(四分位距)法:超出Q1-1.5IQR或Q3+1.5IQR的值视为异常。鲁棒性更好,我推荐这个。
- 领域知识法:比如风速不可能超过当地历史极值,风向变化率有物理上限。我曾经用这个方法,发现一个传感器连续三天输出固定值,原来是卡住了。
小技巧:异常值检测后,别急着删除。先标记出来,看看是不是有规律。比如某个时间段频繁出现异常,那可能是那个时段的传感器有问题。
# IQR法检测异常值
Q1 = df['wind_speed'].quantile(0.25)
Q3 = df['wind_speed'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['wind_speed'] < lower_bound) | (df['wind_speed'] > upper_bound)]
print(f"检测到 {len(outliers)} 个异常值")
时间戳对齐:让所有数据「步调一致」
风场里传感器很多,风速计、风向标、温度计、气压计……它们的数据采集频率可能不一样。有的每秒采集一次,有的每分钟一次。如果不做时间戳对齐,后续分析根本没法做。
为什么会这样?因为不同传感器的时钟可能不同步,或者采集程序有延迟。我记得有一次,两个传感器的数据差了整整3秒,导致风速和风向的关联分析完全错了。
对齐策略:
- 降采样:把高频数据降到低频。比如把1秒数据降为1分钟均值。适合做长期趋势分析。
- 升采样:把低频数据升到高频。比如把1分钟数据插值为1秒数据。适合做精细分析。
- 最近邻对齐:找到时间戳最接近的数据点进行匹配。简单,但精度一般。
注意:升采样时,插值方法要谨慎选择。线性插值假设数据变化是线性的,但风场数据往往不是。我建议用前向填充或后向填充,至少不会引入虚假的波动。
# 时间戳对齐示例
# 假设有两个传感器,频率不同
sensor1 = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=10, freq='1min'),
'value': np.random.randn(10)
})
sensor2 = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=20, freq='30s'),
'value': np.random.randn(20)
})
# 统一为1分钟频率,使用前向填充
sensor1.set_index('timestamp', inplace=True)
sensor2.set_index('timestamp', inplace=True)
aligned = sensor1.join(sensor2, how='outer', lsuffix='_s1', rsuffix='_s2')
aligned = aligned.resample('1min').ffill()
print(aligned.head())
数据标准化:让不同量纲的数据「平起平坐」
风速的单位是m/s,风向是度,温度是摄氏度,气压是hPa。这些数据的数值范围天差地别。如果你直接拿原始数据做机器学习,风速的权重会被放大,风向的权重会被忽略。说白了,就是量纲在作怪。
标准化的两种主流方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - μ) / σ | 数据近似正态分布,或需要保留异常值信息 |
| Min-Max归一化 | (x - min) / (max - min) | 数据有明确边界,或需要将数据映射到[0,1]区间 |
我个人习惯用Z-score。为什么?因为风场数据经常有极端天气事件,Min-Max会把正常数据和极端数据压缩在一起,反而丢失了信息。而Z-score保留了数据的相对关系。
避坑指南:标准化时,一定要先拆分训练集和测试集,用训练集的参数去标准化测试集。我曾经犯过这个错——把整个数据集一起标准化,结果模型在测试集上表现虚高,上线后直接翻车。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化
scaler_z = StandardScaler()
df['wind_speed_z'] = scaler_z.fit_transform(df[['wind_speed']])
# Min-Max归一化
scaler_mm = MinMaxScaler()
df['wind_speed_mm'] = scaler_mm.fit_transform(df[['wind_speed']])
print(df[['wind_speed', 'wind_speed_z', 'wind_speed_mm']].head())
本章知识体系
下面这张图,把数据清洗与预处理的四个核心步骤串起来了。你可以把它当作一个检查清单,每次处理数据时对照着来。
好了,数据清洗与预处理就讲到这里。这四个步骤,每一步都踩过坑,也积累了不少经验。你按照这个流程走一遍,基本能保证数据质量过关。记住,数据清洗花的时间,会在后续建模和分析中加倍还给你。