4. 误差分布特性:正态性检验、偏度与峰度、自相关与异方差
各位工程师,咱们继续聊误差分析。上一节我们把误差的统计指标讲清楚了,这一节要深入一层——看看误差到底长什么样、有什么脾气。
我个人习惯,拿到一组预测误差数据,第一件事不是算RMSE,而是先画个分布直方图。为什么?因为误差的分布特性直接决定了你能不能套用那些经典的统计方法。你想想看,如果误差不服从正态分布,那你用3σ准则去剔除异常值,可能就闹笑话了。
4.1 误差的正态性检验
先说正态性检验。很多统计方法都假设误差服从正态分布,比如置信区间估计、假设检验。但实际负荷预测的误差,往往不是那么“正”。
我记得有一次做某省级电网的日前负荷预测评估,误差直方图一出来,明显是左偏的。当时团队里有人直接用正态分布去算置信区间,结果偏差很大。后来我让他先做正态性检验,才发现p值小于0.001,根本不服从正态。
常用的正态性检验方法有几种:
- Shapiro-Wilk检验:小样本(n<50)时比较灵敏,我一般用它做初步判断。
- Kolmogorov-Smirnov检验:大样本也可以用,但检验力不如Shapiro-Wilk。
- Jarque-Bera检验:基于偏度和峰度,适合大样本场景。
- Q-Q图:可视化方法,直观但主观。我习惯先看Q-Q图,再用统计检验确认。
下面给一段Python代码,演示如何做正态性检验:
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
# 假设errors是预测误差数组
errors = np.random.normal(0, 1, 100) # 示例数据
# Shapiro-Wilk检验
stat, p_value = stats.shapiro(errors)
print(f'Shapiro-Wilk: stat={stat:.4f}, p-value={p_value:.4f}')
# Jarque-Bera检验
stat_jb, p_value_jb = stats.jarque_bera(errors)
print(f'Jarque-Bera: stat={stat_jb:.4f}, p-value={p_value_jb:.4f}')
# Q-Q图
stats.probplot(errors, dist="norm", plot=plt)
plt.title('Q-Q Plot of Prediction Errors')
plt.show()
# 判断:p-value > 0.05 则不能拒绝正态性假设
if p_value > 0.05:
print('误差服从正态分布(显著性水平0.05)')
else:
print('误差不服从正态分布')
4.2 偏度与峰度分析
偏度和峰度,说白了就是描述误差分布形状的两个数字。
偏度(Skewness)衡量分布的不对称性:
- 偏度=0:对称分布(正态分布就是对称的)
- 偏度>0:右偏(正偏),尾巴在右边,说明正误差更多或更大
- 偏度<0:左偏(负偏),尾巴在左边,说明负误差更多或更大
峰度(Kurtosis)衡量分布的“尖胖”程度:
- 峰度=3:正态分布的峰度(有些软件会减去3,得到超额峰度)
- 峰度>3:尖峰厚尾,极端值比正态分布多
- 峰度<3:平峰薄尾,极端值比正态分布少
我在项目中遇到过一件事:某风电场的功率预测误差,峰度高达8.5。这意味着什么?意味着极端误差出现的概率远高于正态分布。如果你还用正态分布去算风险,那风险会被严重低估。
计算偏度和峰度的代码很简单:
from scipy.stats import skew, kurtosis
skewness = skew(errors)
kurt = kurtosis(errors, fisher=True) # Fisher定义,正态分布为0
print(f'偏度: {skewness:.4f}')
print(f'超额峰度: {kurt:.4f}')
if abs(skewness) < 0.5:
print('偏度较小,分布近似对称')
else:
print('偏度较大,分布不对称')
if kurt > 1:
print('尖峰厚尾,注意极端误差')
elif kurt < -1:
print('平峰薄尾,极端误差较少')
else:
print('峰度接近正态分布')
4.3 误差的自相关性
自相关性,说白了就是“今天的误差和昨天的误差有没有关系”。
为什么这个重要?因为如果误差存在自相关,说明你的预测模型没有捕捉到某些时间依赖的模式。比如负荷有日周期性,如果你的模型没学好,那误差就会在每天的同一时段出现类似的偏差。
我记得有一次做某地区夏季负荷预测,误差的自相关系数在滞后24小时处高达0.6。一看就知道,模型没学到日周期规律。后来加了24阶的AR项,自相关就降下来了。
检验自相关的方法:
- 自相关函数(ACF)图:直观看各滞后的相关系数
- Durbin-Watson检验:检验一阶自相关,值接近2表示无自相关
- Ljung-Box检验:检验多个滞后阶数是否存在自相关
import statsmodels.api as sm
from statsmodels.stats.diagnostic import acorr_ljungbox
# 计算ACF
sm.graphics.tsa.plot_acf(errors, lags=40)
plt.title('误差自相关函数(ACF)')
plt.show()
# Ljung-Box检验
result = acorr_ljungbox(errors, lags=[10, 20, 30], return_df=True)
print(result)
# Durbin-Watson检验
from statsmodels.stats.stattools import durbin_watson
dw = durbin_watson(errors)
print(f'Durbin-Watson统计量: {dw:.4f}')
if 1.5 < dw < 2.5:
print('无明显一阶自相关')
else:
print('存在一阶自相关')
4.4 误差的异方差性
异方差性,这个词听起来高大上,其实意思很简单:误差的方差是不是常数。
你想想看,如果白天负荷高的时候误差波动大,晚上负荷低的时候误差波动小,那就是异方差。这对模型有什么影响?会影响置信区间的可靠性,也会影响参数估计的效率。
我做过一个工业园区的负荷预测,发现白天高峰时段的误差方差是夜间低谷时段的3倍。这就是典型的异方差。后来用了加权最小二乘法,效果好了不少。
检验异方差的方法:
- 残差图:横轴是预测值或时间,纵轴是残差,看是否呈喇叭形
- Breusch-Pagan检验:常用方法,检验残差方差是否与自变量相关
- White检验:更一般化,不假设具体形式
- Goldfeld-Quandt检验:将数据分成两段,比较方差是否相等
import statsmodels.api as sm
from statsmodels.stats.diagnostic import het_breuschpagan, het_white
# 假设X是特征矩阵(含截距项),errors是残差
# 这里用简单示例
X = sm.add_constant(np.random.randn(100, 2))
errors = np.random.randn(100)
# Breusch-Pagan检验
bp_test = het_breuschpagan(errors, X)
print(f'Breusch-Pagan: LM stat={bp_test[0]:.4f}, p-value={bp_test[1]:.4f}')
# White检验
white_test = het_white(errors, X)
print(f'White: LM stat={white_test[0]:.4f}, p-value={white_test[1]:.4f}')
# 残差图
plt.scatter(X[:, 1], errors)
plt.xlabel('预测值')
plt.ylabel('残差')
plt.title('残差图 - 检查异方差')
plt.axhline(y=0, color='r', linestyle='--')
plt.show()
4.5 知识体系总览
说了这么多,咱们用一张图把误差分布特性的知识体系串起来:
这张图把四个维度的关系理清楚了。正态性检验和偏度峰度分析,主要看误差的分布形态;自相关和异方差,主要看误差的时间结构和方差稳定性。四个维度互相补充,缺一不可。
实际项目中,我一般按这个顺序来:先做正态性检验和偏度峰度分析,了解误差的基本形态;然后做自相关检验,看看有没有时间依赖;最后做异方差检验,评估方差的稳定性。这样一套下来,误差的“脾气秉性”基本就摸透了。
好了,误差分布特性就讲到这里。下一节咱们聊聊误差补偿的具体方法,到时候会用到今天讲的这些分析结果。