3、数据预处理:缺失值处理、异常值检测、数据对齐与重采样、时标校准
各位同行,咱们接着聊。上一章我们把电表数据接进来了,但说实话,那些原始数据就像刚从泥地里挖出来的电缆——脏得很。你直接拿它去驱动EMS逻辑,不出乱子才怪。
我做了十几年电力系统,见过太多因为数据预处理没做好,导致整个控制策略翻车的案例。说白了,这一步做不好,后面全是白搭。今天我就把压箱底的经验掏出来,跟你聊聊数据预处理的四个核心环节。
核心观点:数据预处理不是「洗菜」,而是「炼钢」。它决定了你EMS系统的下限。
3.1 缺失值处理:别让「空」坑了你
电表数据丢包,太常见了。通信闪断、模块死机、甚至老鼠咬断线,都可能导致某段时间的数据变成空白。我遇到过最离谱的一次,一个关键测点连续缺失了3个小时的数据,EMS愣是以为线路跳闸了,差点把备用电源切进来。
处理缺失值,我个人习惯分三步走:
- 先诊断——看看缺失比例。低于5%的,直接插值;超过30%的,建议找原因,别硬补。
- 再选方法——短时间缺失用线性插值,长时间缺失用前向填充或后向填充。
- 最后验证——补完的数据要跟相邻测点做相关性校验。
我的经验:对于电压、电流这类变化平缓的量,线性插值效果很好。但对于功率这种波动大的,我建议用前向填充——说白了就是用上一个有效值顶上去,虽然粗糙,但至少不会引入虚假波动。
# Python示例:缺失值处理
import pandas as pd
import numpy as np
# 模拟电表数据
df = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=100, freq='1min'),
'power': np.random.normal(100, 10, 100)
})
# 人为制造缺失
df.loc[20:25, 'power'] = np.nan
df.loc[50:52, 'power'] = np.nan
# 线性插值
df['power_interpolated'] = df['power'].interpolate(method='linear')
# 前向填充(用于长时间缺失)
df['power_ffill'] = df['power'].fillna(method='ffill')
print(f"缺失值数量: {df['power'].isna().sum()}")
print(f"插值后缺失值: {df['power_interpolated'].isna().sum()}")
3.2 异常值检测:揪出「捣乱分子」
数据里总有些「刺头」。比如某天凌晨3点,一个居民电表突然显示功率500kW——这比一个小型工厂还高。你想想看,这明显是表计故障或者通信误码。
我常用的方法有三种,各有各的适用场景:
| 方法 | 原理 | 适用场景 | 我的评价 |
|---|---|---|---|
| 3σ原则 | 超出均值±3倍标准差 | 正态分布的数据 | 简单粗暴,但容易误判 |
| IQR方法 | 超出Q1-1.5IQR或Q3+1.5IQR | 非正态分布 | 稳健,我常用这个 |
| 业务规则 | 根据设备容量设定阈值 | 已知设备参数 | 最可靠,但需要人工配置 |
⚠️ 避坑指南:我曾经在一个光伏电站项目上,直接用3σ原则检测异常值,结果把中午发电高峰期的数据全给剔除了——因为那段时间功率本身就远高于均值。后来我学乖了,一定要结合业务规则。比如光伏板最大输出功率是已知的,超过这个值才判异常。
# Python示例:IQR异常值检测
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
# 使用示例
outliers = detect_outliers_iqr(df, 'power')
print(f"检测到 {len(outliers)} 个异常值")
3.3 数据对齐与重采样:让所有表「说同一种语言」
实际项目中,不同电表的采集周期可能不一样。有的15秒上报一次,有的5分钟才报一次。你想想看,如果不做对齐,EMS根本没法做比较运算。
我个人的做法是:
- 统一时间基准——比如全部对齐到整分钟或整15分钟。
- 重采样——高频数据降采样(取平均),低频数据升采样(插值)。
- 对齐校验——对齐后检查时间戳是否严格等间隔。
关键点:重采样时要注意「混叠效应」。比如你从15秒重采样到5分钟,如果直接用平均值,可能会丢失短时波动信息。我建议保留最大值和最小值,给EMS提供更多参考。
# Python示例:数据对齐与重采样
# 假设有两个不同频率的数据源
df_15s = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=100, freq='15s'),
'power': np.random.normal(100, 5, 100)
}).set_index('timestamp')
df_5min = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=20, freq='5min'),
'voltage': np.random.normal(220, 2, 20)
}).set_index('timestamp')
# 重采样到1分钟间隔
df_15s_resampled = df_15s.resample('1min').mean()
df_5min_resampled = df_5min.resample('1min').interpolate()
# 对齐
df_aligned = pd.merge(df_15s_resampled, df_5min_resampled,
left_index=True, right_index=True, how='outer')
print(df_aligned.head())
3.4 时标校准:时间不对,一切白费
这是最容易被忽视的一步,但也是最重要的一步。电表的时钟漂移是常态——有些老表一天能差好几秒。你想想看,如果两个相关测点的时间差了几秒钟,计算出来的相位角、功率因数全是错的。
我处理时标问题的经验:
- 源头校准——能接GPS或NTP的就接,这是最根本的解决办法。
- 软件修正——对于无法校时的老表,通过相邻测点的相关性来推算时间偏差。
- 容忍窗口——设定一个时间偏差容忍值(比如±1秒),在窗口内的认为是对齐的。
一个小技巧:我曾经用一个变电站的母线电压作为参考基准,因为母线电压变化是所有馈线共同的。通过计算各馈线电压变化与母线电压变化的互相关函数,就能推算出每条馈线的时间延迟。这个方法帮我解决了好几个老站的时间同步问题。
# Python示例:基于互相关的时标校准
import numpy as np
def estimate_time_delay(signal_ref, signal_test, max_lag=10):
"""
通过互相关估计两个信号的时间延迟
"""
correlation = np.correlate(signal_ref - np.mean(signal_ref),
signal_test - np.mean(signal_test),
mode='same')
lag = np.argmax(correlation) - len(signal_ref) // 2
if abs(lag) <= max_lag:
return lag
else:
return 0 # 超出容忍范围,认为无延迟
# 模拟使用
ref_power = np.sin(np.linspace(0, 10, 100))
test_power = np.roll(ref_power, 3) # 人为延迟3个采样点
delay = estimate_time_delay(ref_power, test_power)
print(f"估计的时间延迟: {delay} 个采样点")
嗯,数据预处理这块,说白了就是「磨刀不误砍柴工」。我见过太多人急着跑算法、建模型,结果被脏数据坑得欲哭无泪。你把这四步走扎实了,后面的EMS控制逻辑才能站得住脚。
记住一句话:垃圾进,垃圾出。数据预处理不是锦上添花,而是生死攸关。