评估一元边缘分布的正态性

1 检查是否对称

一般来说,统计量较小的时候使用点图,n较大的时候使用直方图,可以揭示一元分布的一个尾部比另一个长的多的情况.
例子

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt  
from scipy import stats
from matplotlib import style

mu, sigma = 0, 0.
s = np.random.normal(mu, sigma, 1000)
f, ax = plt.subplots(figsize=(10, 7))
plt.scatter(x=range(len(s)), y=s, c='r')
plt.xlim(0,500)
plt.show()
散点图
sns.distplot(s)
直方图

是不是很对称啊
我们熟悉一下更多的正态分布样图

style.use('fivethirtyeight')
mu_params = [-1, 0, 1]
sd_params = [0.5, 1, 1.5]
x = np.linspace(-7, 7, 100)
f, ax = plt.subplots(len(mu_params), len(sd_params), sharex=True, sharey=True, figsize=(12,8))
for i in range(3):
    for j in range(3):
        mu = mu_params[i]
        sd = sd_params[j]
        y = stats.norm(mu, sd).pdf(x)
        ax[i, j].plot(x, y)
        ax[i, j].plot(0,0, label='mu={:3.2f}\nsigma={:3.2f}'.format(mu,sd), alpha=0)
        ax[i, j].legend(fontsize=10)
ax[2,1].set_xlabel('x', fontsize=16)
ax[1,0].set_ylabel('pdf(x)', fontsize=16)
plt.suptitle('Gaussian PDF', fontsize=16)
plt.tight_layout()
plt.show()
更多分布图

2 区间检查

一元正态分布属于区间(μ-σ,μ+σ)内的取值概率为0.683,
属于区间(μ-2σ,μ+2σ)内的取值概率为0.954
在(μ-3σ,μ+3σ)内的取值概率为0.997

sigma = 2
list(map(lambda x: stats.norm.cdf(x*sigma,loc=0,scale=sigma) - stats.norm.cdf(-x*sigma,loc=0,scale=sigma), range(1, 6)))

[0.6826894921370859,
 0.9544997361036416,
 0.9973002039367398,
 0.9999366575163338,
 0.9999994266968562]

3 QQ图与PP图

QQ图(Quantile Quantile Plot)有两个作用,1检查一组数据是否服从同一分布,2检查两个分布是否服从同一分布
QQ图原理是比较两组数据的累计分布函数来判断两组数据是否是服从同一分布,所以第一步我们应该做两组数据的累计分布。首先,作为对比我们看一下标准正太分布的累计分布图

x = np.linspace(-5, 5, 100)
y = stats.norm.cdf(x, 0, 1)
plt.plot(x, y)
正态分布累计图

然后,绘制目标数据(这里使用随机生成的数据集)的累计分布函数图

x = np.random.normal(0, 1, 100)
sorted_ = np.sort(x)
yvals = np.arange(len(sorted_))/float(len(sorted_))
plt.plot(sorted_, yvals)
实际的累计图

直观上对比,目标累计分布函数图和标准正太累计分布函数图差异不大,事实是不是这样呢?最后我们就可以做pp图做对比。

x_label = stats.norm.ppf(yvals)  #对目标累计分布函数值求标准正太分布累计分布函数的逆
plt.scatter(x_label, sorted_)
stats.probplot(x, dist="norm", plot=plt)
plt.show()
image.png

下面做个QQ图进行比较

import statsmodels.api as sm
nsample = 100
plt.figure(figsize=(10, 8))
sm.qqplot(stats.t.rvs(25, size=nsample), line='45')
QQ图

除了正态分布,我们还可以检测t分布(自动度较小)

nsample = 100
plt.figure(figsize=(10, 8))
x = stats.t.rvs(3, size=nsample)
res = stats.probplot(x, plot=plt)
t分布

检测t分布(自动度较大)

nsample = 100
plt.figure(figsize=(10, 8))
x = stats.t.rvs(25, size=nsample)
res = stats.probplot(x, plot=plt)
t分布(自动度较大)

两种正态分布的混合

nsample = 100
plt.figure(figsize=(10, 8))
x = stats.norm.rvs(loc=[0,5], scale=[1,1.5],size=(nsample,2)).ravel()
res = stats.probplot(x, plot=plt)
两种正态分布的混合

loggamma分布

plt.figure(figsize=(10, 8))
x = stats.loggamma.rvs(c=2.5, size=500)
stats.probplot(x, dist=stats.loggamma, sparams=(2.5,), plot=plt)
loggamma分布

4 正态的相关系数

例如序列x = [-1.0, -0.1, 0.16, 0.41, 0.62, 0.8, 1.26, 1.54, 1.71, 2.3]
则均值sum(x)/len(x) = 0.77
概率水平

n =10
list(map(lambda x:(x-0.5)/n, range(1, n+1)))

[0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
对应的分位数为

map(lambda x:stats.norm.ppf((x-0.5)/n), range(1, n+1))

为了求相关系数,先求三个数,然后计算rq

x = [-1.0, -0.1, 0.16, 0.41, 0.62, 0.8, 1.26, 1.54, 1.71, 2.3]
n=len(x)
x_ = sum(x)/n
r1 = sum(map(lambda x: (x[0]-x_)*x[1], zip(x, map(lambda x:stats.norm.ppf((x-0.5)/n), range(1, n+1)))))
r2 = sum(map(lambda x: (x-x_)*(x-x_), x))
r3 = sum(map(lambda x:stats.norm.ppf((x-0.5)/n)*stats.norm.ppf((x-0.5)/n), range(1, n+1)))
rq = r1/(np.sqrt(r2)*np.sqrt(r3))

rq = 0.9943587408592187
在显著性水平10%下,把rq = 0.9943587408592187与查表n=10, a=0.10相对照,进行正态性检验,则rq>0.9341 ,因此不拒绝正态性假定

5 柯尔莫可洛夫-斯米洛夫检验Kolmogorov-Smirnov test

柯尔莫哥洛夫-斯米尔诺夫检验(以下简称K-S检验)是用累计次数或累计频率来判断两组数据之间是否存在显著差异的方法。它是将需要做统计分析的数据和另一组标准数据进行对比,求得它和标准数据之间的偏差的方法。


Kolmogorov-Smirnov test

蓝线表示数据,红线表示假设假定符合的分布。
而X轴表示数据值的大小,Y轴表示的数据累计所占百分比。如果简单理解实际上就是概率密度函数的积分。这这个图里面红线实际上就是正态分布的情况,而蓝线因为是离散化的数据,所以呈现的是阶梯状。两条线之间的最大距离也就是黑色箭头表现的位置就表示了二者之间的最大区别程度,称为D,D值的大小则决定了两组数据间的差异。用这种百分数的差别来表现差异,有一个最明显的好处,那就是不会因为某一个点的异常而否定所以的点。此外,KEST还可以检验多种分布,只需要把红线换成其他的线即可。
到这里我们仅仅得到了D值,还不能完全判定两者的符合程度,这时候还需要引入显著度α(alpha默认为0.05)

kstest(x, 'norm')

KstestResult(statistic=0.36355946289143287, pvalue=0.1084952486818282)
由于pvalue=0.1084952486818282 > 0.05 因此不能拒绝原来假设

6 夏皮罗-威尔克检验

该检验是由S.S.Shapiro与M.B.Wilk提出的,又被称之为W检验,主要检验研究对象是否符合正态分布。假设: 一定样本量n(8<n<50)的研究对象总是符合正态分布。
将样本量为n的样本按照大小顺序编排,然后根据公式计算统计量W的值,该值越接近于1,且显著水平大于0.05时,我们就没法拒绝原假设


Shapiro Wilk

xi为排序后的样本数据,ai为待估常量,统计量越大则表示数据越符合正态分布,但是仅凭这一个参数是不够的,在非正态分布的小样本数据中也经常会出现较大的W值。该统计量的分布是未知的,因此需要通过模拟或者查表来估计其概率。由于原假设是其符合正态分布,所以当P值小于指定显著水平时表示其不符合正态分布。

x = [-1.0, -0.1, 0.16, 0.41, 0.62, 0.8, 1.26, 1.54, 1.71, 2.3]
stats.shapiro(x)

(0.9899559020996094, 0.9967610239982605)
0.9967610239982605>0.05 妥妥的正态分布

7 正态性检测汇总

from statsmodels.stats.diagnostic import lillifors
 
group1=[2,3,7,2,6]
group2=[10,8,7,5,10]
group3=[10,13,14,13,15]
list_groups=[group1,group2,group3]
list_total=group1+group2+group3
 
 
#正态分布测试
def check_normality(testData):
    #20<样本数<50用normal test算法检验正态分布性
    if 20<len(testData) <50:
       p_value= stats.normaltest(testData)[1]
       if p_value<0.05:
           print("use normaltest")
           print("data are not normal distributed")
           return  False
       else:
           print("use normaltest")
           print("data are normal distributed")
           return True
     
    #样本数小于50用Shapiro-Wilk算法检验正态分布性
    if len(testData) <50:
       p_value= stats.shapiro(testData)[1]
       if p_value<0.05:
           print("use shapiro:")
           print("data are not normal distributed")
           return  False
       else:
           print("use shapiro:")
           print("data are normal distributed")
           return True
       
    if 300>=len(testData) >=50:
       p_value= lillifors(testData)[1]
       if p_value<0.05:
           print("use lillifors:")
           print("data are not normal distributed")
           return  False
       else:
           print("use lillifors:")
           print("data are normal distributed")
           return True
     
    if len(testData) >300: 
       p_value= stats.kstest(testData,'norm')[1]
       if p_value<0.05:
           print("use kstest:")
           print("data are not normal distributed")
           return  False
       else:
           print( "use kstest:")
           print("data are normal distributed")
           return True
 
 
#对所有样本组进行正态性检验
def NormalTest(list_groups):
    for group in list_groups:
        #正态性检验
        status=check_normality(group1)
        if status==False :
            return False
             
     
#对所有样本组进行正态性检验   
NormalTest(list_groups)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 《R语言与统计分析》的读书笔记 本书的重点内容及感悟: 第三章 概率与分布 1、随机抽样 通过sample()来实...
    格式化_001阅读 6,744评论 1 12
  • 最近在做薪酬绩效分析报告,借助Power pivot处理数据,便找了些数据分析的理论知识。真正做分析的时候,发现已...
    豌豆射手Dany阅读 6,367评论 0 2
  • Xavier A, Muir WM, Craig B, Rainey KM (2016) Walking thro...
    董八七阅读 2,442评论 0 3
  • Normally distributed pseudorandom numbers 目录:1.关于正太分布 ...
    horu阅读 3,997评论 0 1
  • 昨晚,加班一个小时,下班后直奔五道口,去了一家网评还不错的酒吧,名字叫“醺”,大概取自“才倾一盏即醺人”吧! 三个...
    AmyYj阅读 598评论 5 3