4. 数据预处理与清洗:打好数字孪生的地基
各位工程师朋友,咱们接着聊。上一章我们把风场的数据源梳理清楚了,但说实话,从现场拿回来的数据,那叫一个「脏」。你想想看,传感器在野外风吹日晒,通讯偶尔断个线,采集系统时不时抽个风——这些数据直接拿来建模?那数字孪生出来的结果,你敢信吗?
我个人习惯,拿到原始数据后,第一件事不是急着分析,而是先做预处理与清洗。这一步花的时间,往往占整个项目的一半以上。别嫌烦,地基打不牢,楼盖得再高也得塌。
4.1 缺失值处理:别让数据「断片」
风场数据里,缺失值太常见了。通讯故障、传感器维护、甚至鸟撞了设备,都会导致数据记录中断。我遇到过最夸张的一次,某台风机连续3天的风速数据全是NaN,后来发现是网线被老鼠咬断了。
处理缺失值,主要有两招:删除和插值。
4.1.1 直接删除
简单粗暴,但有效。如果缺失比例很小(比如低于5%),而且缺失是随机发生的,直接删掉整行数据,对整体统计影响不大。
# Python示例:删除含有NaN的行
import pandas as pd
df = pd.read_csv('wind_turbine_data.csv')
df_clean = df.dropna() # 默认删除任何包含NaN的行
print(f"删除前:{len(df)}行,删除后:{len(df_clean)}行")
4.1.2 插值填充
更优雅的做法是插值。说白了,就是用已知数据「猜」出缺失值。常用的方法有:
- 线性插值: 假设缺失点前后数据呈直线变化。适合风速、功率这类变化平缓的参数。
- 多项式插值: 用曲线拟合,适合非线性变化。但小心过拟合,我吃过这个亏。
- 前向/后向填充: 用上一个或下一个有效值填充。适合短时间缺失,比如通讯中断几秒钟。
# 线性插值示例
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
# 前向填充
df['wind_speed'] = df['wind_speed'].fillna(method='ffill')
4.2 异常值检测:揪出「捣乱分子」
数据里总有些「不速之客」。比如风速突然飙到100m/s,或者功率为负值——这明显不合理。异常值不处理,模型会被带偏。
我常用的检测方法有三种,各有各的适用场景。
4.2.1 3σ 法则(拉依达准则)
假设数据服从正态分布,那么99.7%的数据会落在均值±3个标准差范围内。超出这个范围的,视为异常。
import numpy as np
mean = df['wind_speed'].mean()
std = df['wind_speed'].std()
df_clean = df[(df['wind_speed'] >= mean - 3*std) &
(df['wind_speed'] <= mean + 3*std)]
4.2.2 IQR(四分位距法)
不依赖正态分布假设,更稳健。IQR = Q3 - Q1,通常把小于Q1-1.5×IQR或大于Q3+1.5×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
df_clean = df[(df['wind_speed'] >= lower_bound) &
(df['wind_speed'] <= upper_bound)]
我个人更偏爱IQR,因为它对数据分布不敏感。风场数据往往不是完美的正态分布,用IQR更靠谱。
4.2.3 DBSCAN(基于密度的聚类)
这是进阶玩法。DBSCAN不依赖统计假设,而是根据数据点的密度来识别异常。密度低的点,就是异常点。
from sklearn.cluster import DBSCAN
# 假设我们有两维特征:风速和功率
X = df[['wind_speed', 'power']].values
clustering = DBSCAN(eps=3, min_samples=10).fit(X)
# 标签为-1的点就是异常点
df_clean = df[clustering.labels_ != -1]
4.3 时间戳对齐与重采样:让数据「步调一致」
风场里不同传感器的采样频率可能不一样。风速仪可能每秒采一次,功率表每5秒采一次,温度传感器每10秒采一次。时间戳不对齐,后续分析根本没法做。
我的做法是:统一时间基准,然后重采样。
4.3.1 时间戳对齐
先把所有数据的时间戳转换成统一的时区(比如UTC),然后按相同的时间间隔排列。
# 确保时间戳是datetime类型
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True)
# 设置为索引
df = df.set_index('timestamp')
4.3.2 重采样
重采样就是把数据从一种频率转换到另一种频率。比如把1秒的数据降采样到10秒,或者把10秒的数据升采样到1秒。
# 降采样:从1秒到10秒,取平均值
df_resampled = df.resample('10S').mean()
# 升采样:从10秒到1秒,用插值填充
df_resampled = df.resample('1S').interpolate()
4.4 数据标准化与归一化:让数据「平起平坐」
风速、功率、温度、振动……这些参数的量纲和数值范围完全不同。风速可能是0-30m/s,功率是0-2000kW,温度是-20-50℃。如果不做标准化,数值大的特征会主导模型,数值小的特征就被淹没了。
常用的方法有两种:
4.4.1 归一化(Min-Max Scaling)
把数据缩放到[0,1]区间。公式很简单:X' = (X - X_min) / (X_max - X_min)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
4.4.2 标准化(Z-score Scaling)
把数据转换成均值为0、标准差为1的分布。公式:X' = (X - μ) / σ
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以把它当作数据预处理的「作战地图」。
数据预处理没有银弹。每个风场、每台风机都有自己的「脾气」。我建议你建立一套标准流程,但也要保留灵活调整的空间。比如,海上风机的数据噪声通常比陆上大,异常检测的阈值就要适当放宽。
好了,数据洗干净了,下一章我们就可以开始真正的建模了。记住,预处理花的时间,会在后续建模和仿真中加倍回报给你。
公众号:蓝海资料掘金营,微信deep3321