KS检验

1、KS-检验(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检验最为非参数检验在分析两组数据之间是否不同时相当常用。
PS:t-检验的假设是检验的数据满足正态分布,否则对于小样本不满足正态分布的数据用t-检验就会造成较大的偏差,虽然对于大样本不满足正态分布的数据而言t-检验还是相当精确有效的手段。

2、原理

2、1首先观察下分析数据

对于以下两组数据:

controlB={1.26, 0.34, 0.70, 1.75, 50.57, 1.55, 0.08, 0.42, 0.50, 3.20, 0.15, 0.49, 0.95, 0.24, 1.37, 0.17, 6.98, 0.10, 0.94, 0.38}
treatmentB= {2.37, 2.16, 14.82, 1.73, 41.04, 0.23, 1.32, 2.91, 39.41, 0.11, 27.44, 4.51, 0.51, 4.50, 0.18, 14.68, 4.66, 1.30, 2.06, 1.19}

对于controlB,这些数据的统计描述如下:

Mean = 3.61
Median = 0.60
High = 50.6 Low = 0.08
Standard Deviation = 11.2

可以发现这组数据并不符合正态分布, 否则大约有15%的数据会小于均值-标准差(3.61-11.2),而数据中显然没有小于0的数。

2、2 观察数据的累计分段函数

对controlB数据从小到大进行排序:

sorted controlB={0.08, 0.10, 0.15, 0.17, 0.24, 0.34, 0.38, 0.42, 0.49, 0.50, 0.70, 0.94, 0.95, 1.26, 1.37, 1.55, 1.75, 3.20, 6.98, 50.57}

10%的数据(2/20)小于0.15,85%(17/20)的数据小于3。所以,对任何数x来说,其累计分段就是所有比x小的数在数据集中所占的比例。下图就是controlB数据集的累计分段图
在这里插入图片描述
可以看到大多数数据都几种在图片左侧(数据值比较小),这就是非正态分布的标志。为了更好的观测数据在x轴上的分布,可以对x轴的坐标进行非等分的划分。在数据都为正的时候有一个很好的方法就是对x轴进行log转换。下图就是上图做log转换以后的图:
在这里插入图片描述

将treatmentB的数据也做相同的图(如下),可以发现treatmentB和controlB的数据分布范围大致相同(0.1 - 50)。但是对于大部分x值,在controlB数据集中比x小的数据所占的比例比在treatmentB中要高,也就是说达到相同累计比例的值在treatment组中比control中要高。KS检验使用的是两条累计分布曲线之间的最大垂直差作为D值(statistic D)作为描述两组数据之间的差异。在此图中这个D值出现在x=1附近,而D值为0.45(0.65-0.25)

在这里插入图片描述
值得注意的是虽然累计分布曲线的性状会随着对数据做转换处理而改变(如log转换),但是D值的大小是不会变的。

3、百分比图(percentile plot)

估算分布函数肩形图(Estimated Distribution Function Ogive)是一种累计分段图的替代方式。其优势在于可以让你使用概率图纸作图(坐标轴经过特殊分段处理,y轴上的数值间隔符合正态分布),从而根据概率在y轴上的分布可以直观的判断数据到底有多符合正态分布,因为正态分布的数据在这种坐标上是呈一条直线。
那么这种图是如何画的呢?
假设我们有这5个数{-0.45, 1.11, 0.48, -0.82, -1.26},从小到大对它们进行排序,{ -1.26, -0.82, -0.45, 0.48, 1.11 }。0.45是中位数,百分比为0.5,而0.45的累计分布函数中占了0.4到0.6的区间。根据数据x在数据集(N)中排位r可以计算x的百分数(percentile)为r/(N+1)。将上述数据与他们的百分数配对,得到{ (-1.26,.167), (-0.82,.333), (-0.45,.5), (0.48,.667), (1.11,.833) }。然后将各点之间用直线连接就是百分比图了。如下图中红线所示(另一条线为累计分段曲线)。

在这里插入图片描述
treatmentB的数据近似对数正态分布,其几何均值为2.563,标准差为6.795。该数据的百分图(红)与其近似的对数正态分布曲线(蓝)如下。

在这里插入图片描述
由于数据近似正态分布,所以对其采用t-检验是最佳的检验方法。

实践

scipy库中一个kstest方法来实现检测功能

kstest(rvs, cdf, args=(), N=20, alternative='two-sided', mode='auto'):

rvs:str, array_like, 或 callable;如果是字符串,则应该是其中的分布名称scipy.stats。如果是数组,则它应该是一维随机变量观测值的数组。如果是可调用的,它应该是生成随机变量的函数;必须具有关键字参数大小。
cdf:str 或 callable;如果是字符串,则应该是其中的分布名称scipy.stats。如果rvs是字符串,则cdf可以为False或与rvs相同。如果是可调用的,则该可调用的用于计算cdf。
args:tuple, sequence, 可选参数;分发参数,如果rvs或cdf是字符串,则使用。
N:int, 可选参数;如果rvs是字符串或可调用的样本大小。默认值为20。
alternative:{‘two-sided’, ‘less’, ‘greater’}, 可选参数;定义替代假设。提供以下选项(默认为“ two-sided”):

import numpy as np
import pandas as pd
from scipy import stats

data = [87,77,92,68,80,78,84,77,81,80,80,77,92,86,
       76,80,81,75,77,72,81,72,84,86,80,68,77,87,
       76,77,78,92,75,80,78]
# 样本数据,35位健康男性在未进食之前的血糖浓度

df = pd.DataFrame(data, columns =['value'])
e = df['value'].mean()  # 计算均值
std = df['value'].std()  # 计算标准差
stats.kstest(df['value'], 'norm', (e, std))
# .kstest方法:KS检验,参数分别是:待检验的数据,检验方法(这里设置成norm正态分布),均值与标准差
# 结果返回两个值:statistic → D值,pvalue → P值
# p值大于0.05,为正态分布

#KstestResult(statistic=0.1590180704824098, pvalue=0.3066297258358026)
# p值大于0.05,不拒绝原假设,因此上面的数据服从正态分布。
#且一般情况下, stats.kstest(df[‘value’], ‘norm’, (u, std))一条语句就得到p值的结果。

#from scipy import stats
#stats.kstest(rvs, cdf, args=(),…)
#其中rvs可以是数组、生成数组的函数或者scipy.stats里面理论分布的名字
#cdf可以与rvs一致。若rvs和cdf同是数组,则是比较两数组的分布是否一致;一个是数组,另一个是理论分布的名字,则是看样本是否否和理论分布
#args是一个元组,当rvs或者cds是理论分布时,这个参数用来存储理论分布的参数,如正态分布的mean和std。

下面是通过代码实现的获取累计这折线图。

import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plot

data = [87,77,92,68,80,78,84,77,81,80,80,77,92,86,
       76,80,81,75,77,72,81,72,84,86,80,68,77,87,
       76,77,78,92,75,80,78]
# 样本数据,35位健康男性在未进食之前的血糖浓度

df = pd.DataFrame(data, columns =['value'])
e = df['value'].mean()  # 计算均值
std = df['value'].std()  # 计算标准差
sd = np.random.rand(35)
stats.kstest(df['value'], 'norm', (e, std))
df.sort_values(by=["value"],inplace= True)
df.index = range(1,len(df)+1)
index = df.index/df.shape[0]
plot.scatter(np.log(df.value),index,color="red")
plot.plot(np.log(df.value),index,color="red")
#正态
xdata = np.linspace(68,93,1000)
ydata = [stats.norm.cdf(i,e,std) for i in xdata]
plot.plot(np.log(xdata),ydata,color="blue")
plot.show()

在这里插入图片描述
下面经过计算可以看出,ks的检验过程

import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plot

data = [87,77,92,68,80,78,84,77,81,80,80,77,92,86,
       76,80,81,75,77,72,81,72,84,86,80,68,77,87,
       76,77,78,92,75,80,78]
# 样本数据,35位健康男性在未进食之前的血糖浓度

df = pd.DataFrame(data, columns =['value'])
e = df['value'].mean()  # 计算均值
std = df['value'].std()  # 计算标准差
sd = np.random.rand(35)
print(stats.kstest(df['value'], 'norm', (e, std)))
print('--'*40)
df.sort_values(by=["value"],inplace= True)
df.index = range(1,len(df)+1)
index = df.index/df.shape[0]


#
df["py"] = df["value"].apply(stats.norm.cdf,loc=e, scale=std)
#print(df.py)
df["fy"] = index - df["py"]
print(df.loc[:,["value","fy"]])

KstestResult(statistic=0.1590180704824098, pvalue=0.3056480127078781)
--------------------------------------------------------------------------------
    value        fy
1      68  0.004590
2      68  0.033162
3      72 -0.010397
4      72  0.018174
5      75 -0.069352
6      75 -0.040781
7      76 -0.064229
8      76 -0.035657
9      77 -0.064918
10     77 -0.036346
11     77 -0.007775
12     77  0.020797
13     77  0.049368
14     77  0.077940
15     78  0.044012
16     78  0.072583
17     78  0.101155
18     80 -0.002986
19     80  0.025585
20     80  0.054157
21     80  0.082728
22     80  0.111300
23     80  0.139871
24     81  0.101875
25     81  0.130447
26     81  0.159018
27     84  0.008123
28     84  0.036694
29     86 -0.025444
30     86  0.003128
31     87 -0.003475
32     87  0.025096
33     92 -0.037649
34     92 -0.009078
35     92  0.019494

通过输出的结果,我们可以看到,在26号数时D检测值最大

  • 5
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值