3、异常数据分类:缺失值、异常值(离群点)、重复数据、时间戳错乱、死值(恒定值)
好,咱们进入正题。数据清洗的第一步,不是急着写代码,而是先搞清楚——到底哪些数据算“异常”?
我做了这么多年风电数据分析,说实话,见过最离谱的数据集,里面居然有连续三天风速都是 12.5 m/s 纹丝不动。你想想看,这怎么可能?风是活的,数据却是死的,这种数据你敢拿去训练模型?
所以今天,我把异常数据分成五类来讲。每一类我都结合项目经验,给你讲透。
3.1 缺失值
缺失值是最常见的异常。说白了,就是某个时间点的数据没采集到,或者采集了但没存下来。
表现形式:
- 单个字段为空(比如风速字段是 NaN)
- 整行数据全部缺失(时间戳还在,但所有测点都是空)
- 连续一段时间的数据全部丢失(比如凌晨 2:00-3:00 的数据全没了)
我在项目中遇到过:某风场 SCADA 系统因为硬盘写满,导致凌晨 1:00-4:00 的数据全部丢失。整整三个小时,功率曲线像被刀切了一样。后来我补数据补到怀疑人生。
处理原则:
- 缺失率 < 5%:可以用插值(线性插值、样条插值)
- 缺失率 5%-20%:建议用前后均值或同类机组数据填充
- 缺失率 > 20%:直接丢弃该时间段,别硬补
我的小技巧:别一上来就用复杂插值。先看看缺失是不是随机的。如果是连续缺失,线性插值反而会引入假趋势。我习惯先用 pandas.isnull().sum() 快速扫一眼缺失分布。
3.2 异常值(离群点)
异常值,就是那些明显偏离正常范围的数据点。比如风速 0 m/s 但功率 1500 kW,这明显是传感器抽风了。
常见来源:
- 传感器故障(比如风速计卡住、温度探头漂移)
- 通信干扰(电磁噪声、信号衰减)
- 极端天气(但要注意:极端天气下的数据不一定是异常,可能是真实工况)
检测方法:
| 方法 | 适用场景 | 缺点 |
|---|---|---|
| 3σ 原则 | 正态分布的数据 | 风电数据往往不是正态分布 |
| 箱线图(IQR) | 偏态分布 | 对尾部数据不敏感 |
| DBSCAN 聚类 | 高维数据、非线性边界 | 参数调起来麻烦 |
| 孤立森林 | 高维、大规模数据 | 解释性差 |
注意:千万不要一刀切!我曾经在某个项目里,用 3σ 把台风天的数据全删了,结果模型在极端风速下完全失效。后来我学乖了——异常值要分“物理不可达”和“统计离群”两种处理。
3.3 重复数据
重复数据,就是完全相同的记录出现了多次。这玩意儿比缺失值还坑——它不会报错,但会悄悄拉偏你的统计结果。
典型场景:
- SCADA 系统重复上报(比如网络重传导致同一秒的数据存了两次)
- 数据合并时未去重(多个数据源拼接,忘了 drop_duplicates)
- 人为误操作(比如手动补录时复制粘贴了)
怎么发现?很简单,用 df.duplicated().sum() 看一眼。但我建议你检查时带上时间戳字段,因为有时候数据值相同但时间不同,那是正常现象。
避坑指南:我曾经遇到过一个风场,重复率高达 12%。原因是他们的采集程序有个 bug——每次断网重连后,会把缓存的数据再发一遍。结果就是,每天凌晨的数据重复率特别高。所以,检查重复数据时,最好按时间段分组统计。
3.4 时间戳错乱
时间戳错乱,是风电数据里最隐蔽的异常。数据本身没问题,但时间对不上。比如 13:00 的数据跑到了 14:00 的位置,或者时间戳出现了倒序。
常见问题:
- 时区未统一(有的用 UTC,有的用北京时间)
- 时钟漂移(工控机时间不准,越跑越偏)
- 夏令时切换(虽然国内不用,但进口机组可能有)
- 数据延迟上报(比如 10:00 的数据,11:30 才入库)
检测方法:
# 检查时间戳是否严格递增
time_diff = df['timestamp'].diff()
print(time_diff.min(), time_diff.max())
# 检查是否有负的时间差(说明时间倒序)
print((time_diff < pd.Timedelta(0)).sum())
我的经验:时间戳错乱最坑的地方在于——它不会影响单点统计,但会彻底毁掉时序模型。比如 LSTM 训练时,如果时间顺序是乱的,模型学到的全是假规律。所以我每次拿到数据,第一件事就是 sort_values('timestamp'),然后检查时间间隔是否均匀。
3.5 死值(恒定值)
死值,就是数据长时间保持不变。比如风速连续 1 小时都是 8.5 m/s,或者功率一直卡在 200 kW 不动。
为什么会出现?
- 传感器卡死(比如风速计轴承卡住)
- 信号线断路(输出一个固定值)
- 数据冻结(采集程序死锁,重复上报最后一条数据)
怎么检测?我一般用滑动窗口法:
def detect_dead_values(series, window=10, threshold=0.01):
"""
检测死值:窗口内标准差小于阈值,判定为死值
"""
rolling_std = series.rolling(window=window).std()
return rolling_std < threshold
特别提醒:死值检测的窗口大小很关键。窗口太小,正常平稳数据会被误判;窗口太大,死值段会被淹没。我一般根据采样频率来定——比如 10 分钟一条数据,窗口设 6(即 1 小时)。如果连续 1 小时纹丝不动,基本可以判定是死值。
知识体系总览
下面这张图,把这五类异常数据的关系和检测方法串起来了。你可以把它当作数据清洗的“作战地图”。
嗯,这五类异常数据,基本覆盖了风电数据清洗中 90% 的问题。你可能会问:这么多异常,先处理哪个?我个人习惯的顺序是:先修时间戳,再去重,然后处理缺失值和死值,最后处理异常值。因为时间戳和重复数据会影响后续所有操作,优先级最高。
好了,这一章就到这里。记住一句话:数据清洗不是一次性工作,而是贯穿整个数据分析流程的持续动作。每次建模前,都值得再检查一遍数据质量。