数据, 术→技巧

Python检验数据是否正态分布

钱魏Way · · 4,390 次浏览

在对数据进行处理前,有事需要判断数据是否呈正态分布,比如线性回归,比如使用3-sigma判断数据是否异常。常用的正态分布检测方法:

Shapiro-Wilk test

Shapiro-Wilk test是一种在频率上统计检验中检验正态性的方法。该检验的零检验是样本$x_1,\cdots ,x_n$来自于一个正态分布的母体。这个检验的统计量是:

$$W = \frac{(\sum_{i=1}^{n}a_{i}x_{(i)})^2}{\sum_{i=1}^{n}(x_i-\bar{x})^2}$$

其中:

  • $x_{(i)}$用括号包含下标索引i的;不与x混淆,它是第i阶统计量,即样本中的第i个最小数
  • $\overline {x}=(x_{1}+\cdots +x_{n})/n$是样本的平均值。
  • 常量$a_i$通过公式$(a_1,\dots ,a_n)=\frac{m^{T}V^{-1}}{\sqrt{(m^{T}V^{-1}V^{-1}m)}}, m=(m_1,\dots ,m_n)^T$,其中$m_1,\dots ,m_n$是从一个标准的正态分布随机变量上采样的有序独立同分布的统计量的期望值。V是这些有序统计量的协方差。

这个统计检验的假设是样本来自于一个正态母体,因此,一方面,如果p值小于选择的显著度水平(通常0.05),那么在更大概率下我们应该拒绝零假设,数据的证据显示我们的样本不是来自一个正态分布母体。另一方面,如果p值比选择的显著度水平大,那么我们没有证据拒绝零假设,数据来自于一个正态分布。

Python代码:

import numpy as np
from scipy import stats
np.random.seed(0)
x = np.random.randn(1, 1000)
print(stats.shapiro(x[0]))
# (0.9985557794570923, 0.5914123058319092)
# 输出(统计量W的值,P值)
# W的值越接近1就越表明数据和正态分布拟合得越好,P值>指定水平, 不拒绝原假设,可以认为样本数据服从正态分布
# 不适合样本>5000的情况

参考链接:

Kolmogorov-Smirnov test

Kolmogorov-Smirnov是比较一个频率分布f(x)与理论分布g(x)或者两个观测值分布的检验方法。其原假设H0:两个数据分布一致或者数据符合理论分布。D=max|f(x)- g(x)|,当实际观测值D>D(n,α)则拒绝H0,否则接受H0假设。

KS检验与t-检验之类的其他方法不同是KS检验不需要知道数据的分布情况,可以算是一种非参数检验方法。当然这样方便的代价就是当检验的数据分布符合特定的分布时,KS检验的灵敏度没有相应的检验来的高。在样本量比较小的时候,KS检验最为非参数检验在分析两组数据之间是否不同时相当常用。

Kolmogorov检验找出在每一个数据点上经验累积概率与目标分布的累积概率之差的上界,列出公式是这样的:

$$D_n = \underset{x}{sup}|F_n(x)-F(x)|$$

其中sup函数表示一组距离中的上确界,这是个数学概念,表示在原假设$F_n(x)=F(x)$的条件下,$F_n(x)-F(x)$的绝对值的最小上界。$F_n(x),F(x)$分别代表经验的和理论的累积概率。其意图在于如果原假设成立,则$D_n$应该很小,如果很大,则原假设不成立。但是,这个上确界怎么求出来呢?请看下面的公式:

$$D_n = \underset{x}{sup}|F_n(x)-F(x)|=\underset{1\leq k\leq n}{max}\{|F_n(x_k)-F_0(x_k)|,|F_0(x_{k+1}-F_n(x_k)|\}$$

其中k为样本从小到大排列后的序数。从公式中看出$D_n$是经验和目标累积概率之差和错一位后再求出的差中最大的一个。Kolmogorov还给出了这个距离的分布函数,并给出了判断的临界值。当然现在的统计软件都直接计算p值,很少有人查表了。

Kolmogorov-Smirnov检验(K-S检验)基于累积分布函数,用以检验一个经验分布是否符合某种理论分布或比较两个经验分布是否有显著性差异。

两样本K-S检验由于对两样本的经验分布函数的位置和形状参数的差异都敏感而成为比较两样本的最有用且常规的非参数方法之一。

  • 优点:该检验不依赖于要测试的累积分布函数,相比于卡方拟合检验(卡方检验需要50个以上的样本),不需要大量的样本。
  • 缺点:只适用于连续分布;在分布中间敏感,在两端不够敏感;最大的局限在于整个分布需要完全确定,如果位置,形状等参数都是从数据中估计的,判定区间不再有效,因此这些参数一般只能通过模拟得到。

scipy.stats.kstest是一个很强大的检验模块,除了正态性检验,还能检验 scipy.stats 中的其他数据分布类型。

scipy.stats.kstest(rvs, cdf, args=(), N=20, alternative=’two-sided’, mode=’approx’)

  • rvs:待检验的数据
  • cdf:检验方法,这里我们设置为‘norm’,即正态性检验
  • args:分布参数,可选
  • N:样本数量
  • alternative:默认为双尾检验,可以设置为‘less’或‘greater’作单尾检验
  • mode:定义计算p-value的方式

使用示例:

import numpy as np
from scipy import stats

np.random.seed(0)
x = np.random.randn(1, 100)
print(stats.kstest(x[0], 'norm'))
# KstestResult(statistic=0.0582486387238324, pvalue=0.8865884365301941)

输出结果中第一个为统计量,第二个为P值(注:统计量越接近0就越表明数据和标准正态分布拟合的越好,如果P值大于显著性水平,通常是0.05,接受原假设,则判断样本的总体服从正态分布)

参考链接:

Anderson-Darling test

这个方法是由T. W. Anderson和D. A. Darling于1954年提出的,与K-S检验相比,AD检验度量经验累积概率和理论累积概率之差的方法显得更加自然。下面的公式就是其方法:

$$Z = n\int_{-\infty}^{\infty}[F_n(x)-F(x)]^{2}w(x)f(x)dx$$

是不是感觉像是计算方差的公式,我的直观感觉,就是把每个数据点的差求平方以后相加,得到总的分布偏差,这样就考虑了所有的差异点,而不是像K-S检验那样只考虑一个最大的。

公式中f(x)是理论分布密度函数,w(x)是某个权重函数。若w(x)≡1,则为Cramér-Von Mises统计量$W^2$。

用上面的积分公式计算统计量比较麻烦,因此两位统计学家又推导出了简单的计算方法,见下面的公式:

$$A^2=-n-\sum_{i}^{n}\frac{2i-1}{n}[\ln F(Y_i)+\ln (1-F(Y_{n+1-i})]$$

其中$Y_i=\frac{X_i-\hat{\mu}}{\hat{\sigma}}$

注意这个公式计算所采用的数据顺序是从小到大排列的,不是原来的数据排列顺序。

scipy.stats.anderson是由 scipy.stats.kstest 改进而来的,可以做正态分布、指数分布、Logistic 分布、Gumbel 分布等多种分布检验。默认参数为 norm,即正态性检验。

返回:statistic – 统计数;critical_values – 评判值;significance_level – 显著性水平

import numpy as np
from scipy import stats

np.random.seed(0)
x = np.random.randn(1, 100)
print(stats.anderson(x[0], 'norm'))
# AndersonResult(statistic=0.18097695613924714, critical_values=array([0.555, 0.632, 0.759, 0.885, 1.053]), significance_level=array([15. , 10. ,  5. ,  2.5,  1. ]))
# 如果输出的统计量值statistic < critical_values,则表示在相应的significance_level下,接受原假设,认为样本数据来自给定的正态分布。

参考链接:

scipy.stats.normaltest

scipy.stats.normaltest运用了D’Agostino–Pearson综合测试法,返回(得分值,p值),得分值=偏态平方+峰态平方

scipy.stats.normaltest(a, axis=0, nan_policy='propagate')

这里的三个参数都有必要看一下:

  • a:待检验的数据
  • axis:默认为0,表示在0轴上检验,即对数据的每一行做正态性检验,我们可以设置为axis=None来对整个数据做检验
  • nan_policy:当输入的数据中有空值时的处理办法。默认为 propagate,返回空值;设置为raise时,抛出错误;设置为omit时,在计算中忽略空值。

代码示例:

import numpy as np
from scipy import stats

np.random.seed(0)
x = np.random.randn(1, 100)
print(stats.normaltest(x[0]))
# NormaltestResult(statistic=0.45430460563864783, pvalue=0.7967994182504964)

如果pvalue是>0.05,则代表是正态分布。

参考链接:

Lilliefors-test

检验样本数据是否来自正态总体。采用方法是:当总体均值和方差未知时,用样本的均值和方差代替后 再用K-S检验法。据说效果不如Anderson-Darling test。原假设 H0:样本服从正态分布; 备择假设 H1:样本不服从正态分布

import numpy as np
from statsmodels.stats.diagnostic import lilliefors

np.random.seed(0)
x = np.random.randn(1, 100)
print(lilliefors(x[0]))
# 输出(统计量的值,P值)=(0.06433451870420759, 0.40018452551214856),P值>指定水平0.05,接受原假设,可以认为样本数据服从正态分布

参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注