4、基于统计学的异常检测:3σ原则、箱线图法、Z-Score方法、移动平均法

各位同事,咱们今天聊点实在的。风电场的数据,说白了就是一堆带噪声的数字。你打开SCADA系统,看到风速、功率、桨距角这些曲线,里面总有些“不听话”的点——要么突然跳上天,要么直接掉到地板上。这些点,就是我们要揪出来的异常数据。

我做了这么多年风电数据分析,最深的体会就是:别一上来就上深度学习,先拿统计学方法过一遍。简单、高效、可解释,这才是工程界的王道。今天我就把这四种最常用的统计学方法,掰开了揉碎了讲给你听。

基于统计学的异常检测方法 异常数据识别 3σ原则 正态分布假设 箱线图法 四分位距 IQR Z-Score 方法 标准化距离 移动平均法 滑动窗口 + 残差

4.1 3σ原则——最直观的“三倍标准差”

这个方法,我估计你在大学概率论课上都学过。但真正在风电场用起来,还是有些门道的。

核心思想:如果数据服从正态分布,那么99.7%的数据会落在均值±3σ的范围内。落在外面的,就是异常。

嗯,这里要注意——前提是数据得近似正态分布。风电功率数据在额定功率附近往往有个“平台”,不是完美的钟形曲线。我建议你先画个直方图看看,如果明显双峰或者偏态严重,3σ原则就不太灵了。

适用场景:风速数据(通常接近正态)、功率在部分工况下的波动、温度传感器读数。
import numpy as np

def detect_3sigma(data):
    mean = np.mean(data)
    std = np.std(data)
    lower = mean - 3 * std
    upper = mean + 3 * std
    anomalies = [(i, v) for i, v in enumerate(data) if v < lower or v > upper]
    return anomalies, lower, upper

# 举个栗子:某风电场10分钟平均风速
wind_speed = [5.2, 5.8, 6.1, 12.5, 5.5, 6.0, 5.9, 6.3, 5.7, 6.2]
anomalies, low, up = detect_3sigma(wind_speed)
print(f"正常范围: [{low:.2f}, {up:.2f}]")
print(f"异常点: {anomalies}")
我的小经验:实际项目中,我经常把阈值从3σ放宽到2.5σ。为什么?因为风电场数据噪声大,3σ太严格了,很多“真异常”反而漏掉了。你可以在验证集上调调这个参数。

4.2 箱线图法——不看分布,看分位

箱线图的好处是不依赖正态分布假设。你想想看,风功率曲线那个“弯弯绕”的形状,用箱线图反而更稳。

怎么算

  • 计算下四分位数 Q1(25%分位)和上四分位数 Q3(75%分位)
  • 四分位距 IQR = Q3 - Q1
  • 正常范围:[Q1 - 1.5×IQR, Q3 + 1.5×IQR]
  • 超出这个范围的,就是异常值

我在项目中遇到过一件事:某台机组功率曲线在低风速段总有“掉坑”的数据点。用3σ原则死活揪不出来,换成箱线图法,一下子就识别出来了。为什么?因为低风速段数据分布不对称,箱线图不受这个影响。

import numpy as np

def detect_iqr(data):
    q1 = np.percentile(data, 25)
    q3 = np.percentile(data, 75)
    iqr = q3 - q1
    lower = q1 - 1.5 * iqr
    upper = q3 + 1.5 * iqr
    anomalies = [(i, v) for i, v in enumerate(data) if v < lower or v > upper]
    return anomalies, lower, upper

# 功率数据示例(单位:kW)
power = [1500, 1520, 1480, 1510, 800, 1490, 1530, 1470, 1515, 1525]
anomalies, low, up = detect_iqr(power)
print(f"IQR正常范围: [{low:.0f}, {up:.0f}]")
print(f"异常点: {anomalies}")
注意:箱线图的1.5倍IQR是经验值。如果你发现异常点太多或太少,可以调整这个系数。我有时候会用到2.0倍IQR,尤其是数据本身波动就大的场景。

4.3 Z-Score方法——标准化后的“距离”

Z-Score说白了就是:这个点离均值有几个标准差那么远

公式很简单:Z = (x - μ) / σ

通常认为 |Z| > 3 就是异常。但这里有个坑——Z-Score对极端值非常敏感。为什么?因为均值和标准差本身会被异常值“带偏”。

我曾经处理过一个数据集,里面有个传感器坏了,读数飙到10000,结果均值和标准差都被拉得很大,导致其他真正的异常反而被掩盖了。这就是所谓的掩蔽效应

改进方案:用中位数代替均值,用MAD(中位数绝对偏差)代替标准差。这叫稳健Z-Score,抗干扰能力强得多。
import numpy as np

def zscore_detect(data, threshold=3):
    mean = np.mean(data)
    std = np.std(data)
    z_scores = [(x - mean) / std for x in data]
    anomalies = [(i, data[i]) for i, z in enumerate(z_scores) if abs(z) > threshold]
    return anomalies, z_scores

# 稳健版本
def robust_zscore_detect(data, threshold=3):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    # 如果MAD为0,特殊处理
    if mad == 0:
        return [], []
    modified_z = 0.6745 * (data - median) / mad
    anomalies = [(i, data[i]) for i, z in enumerate(modified_z) if abs(z) > threshold]
    return anomalies, modified_z
选哪个?数据干净、分布对称,用普通Z-Score。数据有噪声、有离群点,用稳健Z-Score。我个人的习惯是:先跑一遍稳健版本看看情况

4.4 移动平均法——抓住“时间上的异常”

前面三种方法都是“只看数值,不看时间”。但风电场数据是时序的——上一秒和下一秒有关系。移动平均法就是利用这个关系。

思路

  1. 选一个窗口大小(比如5个点、10个点)
  2. 计算窗口内的平均值(或中位数)作为“期望值”
  3. 计算实际值与期望值的差值(残差)
  4. 如果残差超过某个阈值,就是异常

举个例子:风速从5m/s突然跳到15m/s,然后又跳回来。这种“尖峰”在物理上不可能(风有惯性),但传感器可能误报。移动平均法对这种孤立跳变点特别敏感。

import numpy as np

def moving_average_detect(data, window=5, threshold=2.0):
    anomalies = []
    for i in range(window, len(data)):
        window_data = data[i-window:i]
        avg = np.mean(window_data)
        residual = abs(data[i] - avg)
        if residual > threshold * np.std(window_data):
            anomalies.append((i, data[i], residual))
    return anomalies

# 模拟一个带尖峰的风速序列
wind = [5.0, 5.2, 5.1, 5.3, 5.0, 15.8, 5.1, 5.4, 5.2, 5.0]
anomalies = moving_average_detect(wind, window=4, threshold=2.0)
print(f"移动平均法检测到的异常: {anomalies}")
窗口大小怎么选?没有标准答案。我一般先看数据的采样频率——10分钟数据用5~10个点的窗口,1秒数据用50~100个点的窗口。窗口太小,噪声大;窗口太大,反应慢。你可以用网格搜索调参。

4.5 四种方法怎么选?一张表说清楚

方法 数据要求 优点 缺点 风电典型场景
3σ原则 近似正态分布 简单、计算快 对偏态数据失效 风速、温度
箱线图法 无分布要求 稳健、不受极端值影响 对样本量敏感 功率、桨距角
Z-Score 近似正态分布 标准化后可比 掩蔽效应 多传感器对比
移动平均法 时序数据 捕捉时间相关性 窗口参数难调 跳变、毛刺检测

最后说一句:别指望一种方法打天下。我通常的做法是:先用箱线图法扫一遍全局异常,再用移动平均法抓局部跳变,最后用3σ原则做个交叉验证。三种方法都标记为异常的,基本可以确定是“实锤”了。

好了,这四种方法你可以在自己的数据上跑一跑。下一节我们会聊更高级的方法——基于机器学习的异常检测,到时候你会发现,统计方法其实是ML方法的基础。先把今天这些吃透,后面才跟得上。


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