2、数据质量初探:缺失值类型、异常值识别与完整性检查

大家好,我是老张。在风场干了这么多年数据,我有个习惯——拿到SCADA数据的第一件事,不是急着建模,而是先「摸摸底」。

说白了,就是看看这批数据到底干不干净。你想想看,如果原始数据本身就有问题,后面再花哨的特征工程也是白搭。今天我们就来聊聊数据质量初探的三个核心问题:缺失值、异常值和完整性检查。

2.1 缺失值类型:随机缺失 vs 连续缺失

缺失值这东西,在风场数据里太常见了。我个人习惯把它们分成两类:随机缺失和连续缺失。

随机缺失,就是数据像被「随机打孔」一样。比如某个传感器偶尔闪断一下,或者通讯瞬间丢包。这种缺失通常没有规律,可能上午缺一个点,下午又缺一个点。

连续缺失就麻烦多了。比如风机停机检修,一停就是几个小时甚至几天,这段时间的数据全是空的。我在项目中遇到过,有一次某台风机连续缺失了72小时的数据,后来一查,是通讯模块彻底烧了。

判断方法:

  • 随机缺失:缺失点之间间隔不固定,前后都有正常数据
  • 连续缺失:连续多个时间点(比如超过10个采样点)全部为空

嗯,这里要注意:连续缺失的长度阈值没有固定标准。我个人习惯根据采样频率来定——如果是10分钟一条数据,连续缺失超过6个点(即1小时),我就把它标记为连续缺失。

2.2 异常值识别:停机数据与限功率数据

异常值识别,是数据清洗的重头戏。风场数据里最常见的两种异常,就是停机数据和限功率数据。

停机数据:风机不转了,功率为0,风速可能还在变。这种数据如果混进训练集,模型会学歪——它会以为「风速很高时功率也可以是0」。我曾经吃过这个亏,第一次做功率曲线拟合时没剔除停机数据,结果模型在低风速段预测偏差特别大。

限功率数据:电网调度要求风机降功率运行。这时候风速明明够,但功率被人为压低了。这种数据更隐蔽,因为功率不为0,看起来像是正常数据。

识别技巧:

  • 停机数据:功率 < 0.01 MW(接近0),且持续超过5分钟
  • 限功率数据:风速 > 切入风速,但功率明显低于理论功率曲线

怎么识别限功率?我一般用功率曲线做参考。如果风速在8m/s以上,功率却只有额定功率的60%以下,十有八九是被限了。

2.3 数据完整性检查

完整性检查,说白了就是看看数据有没有「断档」。风场SCADA系统有时候会抽风,比如存储满了、网络断了,导致某段时间的数据完全丢失。

我常用的检查方法很简单:

  1. 检查时间戳是否连续——相邻两条数据的时间差是否等于采样间隔
  2. 检查数据量是否合理——比如一天应该有144条10分钟数据,如果只有100条,那肯定有问题
  3. 检查关键字段是否完整——风速、功率、桨距角这些核心参数不能缺

避坑指南:我曾经遇到过一个项目,数据量看起来挺全的,但仔细一查,有连续3天的数据时间戳是重复的——系统故障导致同一时间点存了多条记录。这种「假完整」比真缺失更坑人。

2.4 知识体系总览

下面这张图,是我自己总结的数据质量初探流程。你可以把它当成一个检查清单,每次拿到新数据都走一遍:

数据质量初探流程 原始SCADA数据 缺失值检查:随机缺失 vs 连续缺失 异常值识别:停机数据 / 限功率数据 完整性检查:时间戳 / 数据量 / 关键字段 每个步骤发现问题后,记录并标记,不要直接删除

2.5 实战代码示例

光说不练假把式。下面是我常用的数据质量初探代码,你可以直接拿去用:

import pandas as pd
import numpy as np

# 读取数据
df = pd.read_csv('wind_scada.csv', parse_dates=['timestamp'])
df.set_index('timestamp', inplace=True)

# 1. 缺失值检查
missing_count = df.isnull().sum()
print("各字段缺失数量:")
print(missing_count)

# 检查连续缺失(以功率字段为例)
power_missing = df['power'].isnull().astype(int)
power_missing_groups = (power_missing != power_missing.shift()).cumsum()
continuous_missing = power_missing_groups[power_missing == 1].value_counts()
print(f"\n连续缺失段数:{len(continuous_missing)}")
print(f"最长连续缺失:{continuous_missing.max()} 个采样点")

# 2. 异常值识别
# 停机数据:功率接近0且持续
df['is_stopped'] = (df['power'] < 0.01) & (df['wind_speed'] > 3)

# 限功率数据:风速够但功率偏低
df['is_curtailed'] = (df['wind_speed'] > 5) & (df['power'] < 0.3 * df['rated_power'])

print(f"\n停机数据占比:{df['is_stopped'].mean():.2%}")
print(f"限功率数据占比:{df['is_curtailed'].mean():.2%}")

# 3. 完整性检查
expected_interval = pd.Timedelta('10min')
actual_intervals = df.index.to_series().diff()
gaps = actual_intervals[actual_intervals > expected_interval * 1.5]
print(f"\n数据断档次数:{len(gaps)}")
print(f"最大断档时长:{gaps.max()}")

我的习惯:每次做完初探,我都会把发现的问题记录到一个单独的日志文件里。这样后续做特征工程时,可以随时回溯哪些数据被标记过。别偷懒,这个习惯救过我很多次。

好了,数据质量初探就聊到这儿。记住一句话:数据清洗花的时间,后面建模会加倍还给你。下一章我们接着聊缺失值处理的具体方法。


公众号:蓝海资料掘金营,微信deep3321