2. 数据分布诊断:偏度与峰度、Q-Q图、Shapiro-Wilk检验、Kolmogorov-Smirnov检验
做量化因子研究,我最怕什么?不是策略回测亏钱,而是数据本身有问题,我还傻乎乎地往上堆模型。
你想想看,很多非线性因子转换方法——比如Box-Cox、Yeo-Johnson——它们对数据分布是有要求的。数据本来就不符合正态,你硬要套用某些转换,结果只会更糟。所以,在做任何转换之前,先给数据做个「体检」,这是基本功。
这一节,我们就聊聊怎么诊断数据分布。我会结合自己踩过的坑,把偏度、峰度、Q-Q图、Shapiro-Wilk和K-S检验这几个工具讲透。
2.1 偏度与峰度:两个最直观的「形状指标」
先说偏度。偏度衡量的是数据分布的对称性。说白了,就是看数据是「左偏」还是「右偏」。
- 偏度 = 0:数据分布对称,比如标准正态分布。
- 偏度 > 0:右偏(正偏),尾巴拖在右边。大部分数据集中在左侧,右侧有少量极端大值。
- 偏度 < 0:左偏(负偏),尾巴拖在左边。大部分数据集中在右侧,左侧有少量极端小值。
我在项目中遇到过一件事:某只股票的日收益率数据,偏度算出来是-2.3。我当时没在意,直接做了标准化处理。结果后续的因子IC分析全乱了。后来才发现,那段时间该股票出了几次跌停,导致左尾特别重。嗯,偏度这个指标,能帮你快速发现数据里有没有「异常尾巴」。
再说峰度。峰度衡量的是数据分布的「尖峭程度」和「尾部厚度」。
- 峰度 = 3:正态分布的峰度值(有些软件会做减3处理,得到超额峰度)。
- 峰度 > 3:尖峰厚尾。数据集中在均值附近,但尾部也有较多极端值。
- 峰度 < 3:低峰薄尾。数据分布比较平坦,极端值较少。
重要提醒:在金融数据里,峰度大于3是常态。因为收益率数据往往有「聚集效应」——大部分时间波动很小,但偶尔会出现极端波动。如果你看到峰度超过10,那就要警惕了,数据里可能有异常值或者结构性突变。
计算偏度和峰度,Python里一行代码搞定:
import scipy.stats as stats
# 假设 factor_data 是你的因子值序列
skewness = stats.skew(factor_data)
kurtosis = stats.kurtosis(factor_data, fisher=True) # Fisher=True 返回超额峰度(正态=0)
print(f"偏度: {skewness:.3f}")
print(f"超额峰度: {kurtosis:.3f}")
2.2 Q-Q图:用眼睛「看」分布
偏度和峰度是数值指标,但有时候,一张图比一个数字更有说服力。
Q-Q图(分位数-分位数图)的原理很简单:把你的数据的分位数,和理论正态分布的分位数画在一起。如果数据点大致落在一条直线上,说明数据接近正态分布。
我个人习惯在因子分析的初期,先画一张Q-Q图。为什么?因为我能直观地看到「哪里出了问题」。
- 中间部分偏离直线:说明数据的主体部分不符合正态。
- 两端偏离直线:说明尾部有异常值。左端向下弯,说明左尾比正态更厚(左偏);右端向上弯,说明右尾更厚(右偏)。
- S形弯曲:说明数据分布比正态更「扁平」或者更「尖峭」。
我的经验:在Q-Q图上,我一般不太关注中间那部分是否完美贴合直线。我更关注两端的「尾巴」。因为金融数据里,极端值往往才是影响因子表现的关键。如果两端偏离严重,我会考虑做截尾处理或者使用鲁棒统计方法。
画Q-Q图的代码:
import matplotlib.pyplot as plt
import scipy.stats as stats
stats.probplot(factor_data, dist="norm", plot=plt)
plt.title("Q-Q图:因子值 vs 正态分布")
plt.show()
2.3 Shapiro-Wilk检验:小样本的「金标准」
Shapiro-Wilk检验(简称S-W检验)是检验正态性的经典方法。它的原假设是「数据来自正态分布」。如果p值小于显著性水平(通常取0.05),就拒绝原假设,认为数据不符合正态分布。
这个检验有个特点:它对小样本非常敏感。样本量在50以下时,S-W检验的统计功效很高,能有效检测出偏离正态的情况。
但要注意——样本量太大时,S-W检验会变得过于敏感。我曾经用5000个数据点做S-W检验,p值直接是0.000,哪怕数据只是轻微偏离正态。所以,我一般只在样本量小于200时使用S-W检验。
stat, p_value = stats.shapiro(factor_data)
print(f"Shapiro-Wilk统计量: {stat:.4f}")
print(f"p值: {p_value:.4f}")
if p_value > 0.05:
print("无法拒绝原假设,数据可能符合正态分布")
else:
print("拒绝原假设,数据不符合正态分布")
避坑指南:我曾经在分析一个包含1000只股票的因子数据时,直接用S-W检验,结果所有股票都显示「非正态」。后来才发现,样本量太大导致检验过于敏感。对于大样本,我更推荐用K-S检验或者直接看Q-Q图做定性判断。
2.4 Kolmogorov-Smirnov检验:大样本的「主力军」
Kolmogorov-Smirnov检验(简称K-S检验)是另一种常用的正态性检验。它比较的是经验分布函数和理论分布函数之间的最大距离。
相比S-W检验,K-S检验有几个优点:
- 适用于大样本:样本量几千几万都没问题。
- 不限于正态分布:你可以检验数据是否符合任何理论分布(比如t分布、均匀分布)。
- 对位置和尺度参数不敏感:K-S检验是「分布形状」的检验,不受均值和标准差的影响。
但K-S检验也有缺点:它对分布尾部的差异不太敏感。如果数据只是在尾部有少量异常值,K-S检验可能检测不出来。
stat, p_value = stats.kstest(factor_data, 'norm', args=(factor_data.mean(), factor_data.std()))
print(f"K-S统计量: {stat:.4f}")
print(f"p值: {p_value:.4f}")
我的建议:在实际工作中,我不会只依赖某一个检验。我会把偏度、峰度、Q-Q图、S-W检验和K-S检验结合起来看。如果多个指标都指向「非正态」,那我才会放心地做非线性转换。如果只有一两个指标有异常,我会先检查数据质量,看看是不是有数据录入错误或者异常值。
2.5 知识体系总览
下面这张图,我把这节的核心内容梳理了一下。你可以把它当作一个「诊断流程图」来用。
2.6 实战中的选择策略
说了这么多,你可能会问:到底该用哪个?
我的做法是这样的:
- 先看偏度和峰度:花30秒算一下,心里有个数。如果偏度绝对值大于1,或者超额峰度大于5,我就知道数据肯定有问题。
- 再画Q-Q图:用眼睛扫一遍,重点看尾巴。如果尾巴翘得厉害,我会考虑做截尾或者使用鲁棒方法。
- 最后做统计检验:样本量小于200用S-W,大于200用K-S。但记住,p值只是参考,不要迷信。
一个小技巧:在做因子研究时,我经常把偏度、峰度和Q-Q图放在一个「诊断报告」里,每次处理新数据都先跑一遍。这样能快速发现数据质量问题,避免后续分析走弯路。
好了,数据分布诊断这部分就聊到这里。记住一句话:没有「完美正态」的数据,只有「够用」的数据。我们的目标不是让数据变成完美的正态分布,而是了解数据的真实形态,然后选择合适的转换方法。
公众号:蓝海资料掘金营,微信deep3321