def calc_chiSquare(sampleSet, feature, target):
创新互联公司长期为数千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为广信企业提供专业的成都做网站、成都网站制作,广信网站改版等技术服务。拥有10年丰富建站经验和众多成功案例,为您定制开发。
'''
计算某个特征每种属性值的卡方统计量
params:
sampleSet: 样本集
feature: 目标特征
target: 目标Y值 (0或1) Y值为二分类变量
return:
卡方统计量dataframe
feature: 特征名称
act_target_cnt: 实际坏样本数
expected_target_cnt:期望坏样本数
chi_square:卡方统计量
'''
# 计算样本期望频率
target_cnt = sampleSet[target].sum()
sample_cnt = len(sampleSet[target])
expected_ratio = target_cnt * 1.0/sample_cnt
# 对变量按属性值从大到小排序
df = sampleSet[[feature, target]]
col_value = list(set(df[feature]))
# 计算每一个属性值对应的卡方统计量等信息
chi_list = []; target_list = []; expected_target_list = []
for value in col_value:
df_target_cnt = df.loc[df[feature] == value, target].sum()
df_cnt = len(df.loc[df[feature] == value, target])
expected_target_cnt = df_cnt * expected_ratio
chi_square = (df_target_cnt - expected_target_cnt)**2 / expected_target_cnt
chi_list.append(chi_square)
target_list.append(df_target_cnt)
expected_target_list.append(expected_target_cnt)
# 结果输出到dataframe, 对应字段为特征属性值, 卡方统计量, 实际坏样本量, 期望坏样本量
chi_stats = pd.DataFrame({feature:col_value, 'chi_square':chi_list,
'act_target_cnt':target_list, 'expected_target_cnt':expected_target_list})
return chi_stats[[feature, 'act_target_cnt', 'expected_target_cnt', 'chi_square']]
def chiMerge_maxInterval(chi_stats, feature, maxInterval=5):
'''
卡方分箱合并--最大区间限制法
params:
chi_stats: 卡方统计量dataframe
feature: 目标特征
maxInterval:最大分箱数阈值
return:
卡方合并结果dataframe, 特征分割split_list
'''
group_cnt = len(chi_stats)
split_list = [chi_stats[feature].min()]
# 如果变量区间超过最大分箱限制,则根据合并原则进行合并
while(group_cnt maxInterval):
min_index = chi_stats[chi_stats['chi_square']==chi_stats['chi_square'].min()].index.tolist()[0]
# 如果分箱区间在最前,则向下合并
if min_index == 0:
chi_stats = merge_chiSquare(chi_stats, min_index+1, min_index)
# 如果分箱区间在最后,则向上合并
elif min_index == group_cnt-1:
chi_stats = merge_chiSquare(chi_stats, min_index-1, min_index)
# 如果分箱区间在中间,则判断与其相邻的最小卡方的区间,然后进行合并
else:
if chi_stats.loc[min_index-1, 'chi_square'] chi_stats.loc[min_index+1, 'chi_square']:
chi_stats = merge_chiSquare(chi_stats, min_index, min_index+1)
else:
chi_stats = merge_chiSquare(chi_stats, min_index-1, min_index)
group_cnt = len(chi_stats)
chiMerge_result = chi_stats
split_list.extend(chiMerge_result[feature].tolist())
return chiMerge_result, split_list
def chiMerge_minChiSquare(chi_stats, feature, dfree=4, cf=0.1, maxInterval=5):
'''
卡方分箱合并--卡方阈值法
params:
chi_stats: 卡方统计量dataframe
feature: 目标特征
maxInterval: 最大分箱数阈值, default 5
dfree: 自由度, 最大分箱数-1, default 4
cf: 显著性水平, default 10%
return:
卡方合并结果dataframe, 特征分割split_list
'''
threshold = get_chiSquare_distuibution(dfree, cf)
min_chiSquare = chi_stats['chi_square'].min()
group_cnt = len(chi_stats)
split_list = [chi_stats[feature].min()]
# 如果变量区间的最小卡方值小于阈值,则继续合并直到最小值大于等于阈值
while(min_chiSquare threshold and group_cnt maxInterval):
min_index = chi_stats[chi_stats['chi_square']==chi_stats['chi_square'].min()].index.tolist()[0]
# 如果分箱区间在最前,则向下合并
if min_index == 0:
chi_stats = merge_chiSquare(chi_stats, min_index+1, min_index)
# 如果分箱区间在最后,则向上合并
elif min_index == group_cnt-1:
chi_stats = merge_chiSquare(chi_stats, min_index-1, min_index)
# 如果分箱区间在中间,则判断与其相邻的最小卡方的区间,然后进行合并
else:
if chi_stats.loc[min_index-1, 'chi_square'] chi_stats.loc[min_index+1, 'chi_square']:
chi_stats = merge_chiSquare(chi_stats, min_index, min_index+1)
else:
chi_stats = merge_chiSquare(chi_stats, min_index-1, min_index)
min_chiSquare = chi_stats['chi_square'].min()
group_cnt = len(chi_stats)
chiMerge_result = chi_stats
split_list.extend(chiMerge_result[feature].tolist())
return chiMerge_result, split_list
def get_chiSquare_distuibution(dfree=4, cf=0.1):
'''
根据自由度和置信度得到卡方分布和阈值
params:
dfree: 自由度, 最大分箱数-1, default 4
cf: 显著性水平, default 10%
return:
卡方阈值
'''
percents = [0.95, 0.90, 0.5, 0.1, 0.05, 0.025, 0.01, 0.005]
df = pd.DataFrame(np.array([chi2.isf(percents, df=i) for i in range(1, 30)]))
df.columns = percents
df.index = df.index+1
# 显示小数点后面数字
pd.set_option('precision', 3)
return df.loc[dfree, cf]
def merge_chiSquare(chi_result, index, mergeIndex, a = 'expected_target_cnt',
b = 'act_target_cnt', c = 'chi_square'):
'''
params:
chi_result: 待合并卡方数据集
index: 合并后的序列号
mergeIndex: 需合并的区间序号
a, b, c: 指定合并字段
return:
分箱合并后的卡方dataframe
'''
chi_result.loc[mergeIndex, a] = chi_result.loc[mergeIndex, a] + chi_result.loc[index, a]
chi_result.loc[mergeIndex, b] = chi_result.loc[mergeIndex, b] + chi_result.loc[index, b]
chi_result.loc[mergeIndex, c] = (chi_result.loc[mergeIndex, b] - chi_result.loc[mergeIndex, a])**2 /chi_result.loc[mergeIndex, a]
chi_result = chi_result.drop([index])
chi_result = chi_result.reset_index(drop=True)
return chi_result
for col in bin_col:
chi_stats = calc_chiSquare(exp_f_data_label_dr, col, 'label')
chiMerge_result, split_list = chiMerge_maxInterval(chi_stats, col, maxInterval=5)
print(col, 'feature maybe split like this:', split_list)
如果随机变量X的所有取值都可以逐个列举出来,则称X为离散型随机变量。相应的概率分布有二项分布,泊松分布。
如果随机变量X的所有取值无法逐个列举出来,而是取数轴上某一区间内的任一点,则称X为连续型随机变量。相应的概率分布有正态分布,均匀分布,指数分布,伽马分布,偏态分布,卡方分布,beta分布等。(真多分布,好恐怖~~)
在离散型随机变量X的一切可能值中,各可能值与其对应概率的乘积之和称为该随机变量X的期望值,记作E(X) 。比如有随机变量,取值依次为:2,2,2,4,5。求其平均值:(2+2+2+4+5)/5 = 3。
期望值也就是该随机变量总体的均值。 推导过程如下:
= (2+2+2+4+5)/5
= 1/5 2 3 + 4/5 + 5/5
= 3/5 2 + 1/5 4 + 1/5 5
= 0.6 2 + 0.2 4 + 0.2 5
= 60% 2 + 20% 4 + 20%*5
= 1.2 + 0.8 + 1
= 3
倒数第三步可以解释为值为2的数字出现的概率为60%,4的概率为20%,5的概率为20%。 所以E(X) = 60% 2 + 20% 4 + 20%*5 = μ = 3。
0-1分布(两点分布),它的随机变量的取值为1或0。即离散型随机变量X的概率分布为:P{X=0} = 1-p, P{X=1} = p,即:
则称随机变量X服从参数为p的0-1分布,记作X~B(1,p)。
在生活中有很多例子服从两点分布,比如投资是否中标,新生婴儿是男孩还是女孩,检查产品是否合格等等。
大家非常熟悉的抛硬币试验对应的分布就是二项分布。抛硬币试验要么出现正面,要么就是反面,只包含这两个结果。出现正面的次数是一个随机变量,这种随机变量所服从的概率分布通常称为 二项分布 。
像抛硬币这类试验所具有的共同性质总结如下:(以抛硬币为例)
通常称具有上述特征的n次重复独立试验为n重伯努利试验。简称伯努利试验或伯努利试验概型。特别地,当试验次数为1时,二项分布服从0-1分布(两点分布)。
举个栗子:抛3次均匀的硬币,求结果出现有2个正面的概率 。
已知p = 0.5 (出现正面的概率) ,n = 3 ,k = 2
所以抛3次均匀的硬币,求结果出现有2个正面的概率为3/8。
二项分布的期望值和方差 分别为:
泊松分布是用来描述在一 指定时间范围内或在指定的面积或体积之内某一事件出现的次数的分布 。生活中服从泊松分布的例子比如有每天房产中介接待的客户数,某微博每月出现服务器瘫痪的次数等等。 泊松分布的公式为 :
其中 λ 为给定的时间间隔内事件的平均数,λ = np。e为一个数学常数,一个无限不循环小数,其值约为2.71828。
泊松分布的期望值和方差 分别为:
使用Python绘制泊松分布的概率分布图:
因为连续型随机变量可以取某一区间或整个实数轴上的任意一个值,所以通常用一个函数f(x)来表示连续型随机变量,而f(x)就称为 概率密度函数 。
概率密度函数f(x)具有如下性质 :
需要注意的是,f(x)不是一个概率,即f(x) ≠ P(X = x) 。在连续分布的情况下,随机变量X在a与b之间的概率可以写成:
正态分布(或高斯分布)是连续型随机变量的最重要也是最常见的分布,比如学生的考试成绩就呈现出正态分布的特征,大部分成绩集中在某个范围(比如60-80分),很小一部分往两端倾斜(比如50分以下和90多分以上)。还有人的身高等等。
正态分布的定义 :
如果随机变量X的概率密度为( -∞x+∞):
则称X服从正态分布,记作X~N(μ,σ²)。其中-∞μ+∞,σ0, μ为随机变量X的均值,σ为随机变量X的标准差。 正态分布的分布函数
正态分布的图形特点 :
使用Python绘制正态分布的概率分布图:
正态分布有一个3σ准则,即数值分布在(μ-σ,μ+σ)中的概率为0.6827,分布在(μ-2σ,μ+2σ)中的概率为0.9545,分布在(μ-3σ,μ+3σ)中的概率为0.9973,也就是说大部分数值是分布在(μ-3σ,μ+3σ)区间内,超出这个范围的可能性很小很小,仅占不到0.3%,属于极个别的小概率事件,所以3σ准则可以用来检测异常值。
当μ=0,σ=1时,有
此时的正态分布N(0,1) 称为标准正态分布。因为μ,σ都是确定的取值,所以其对应的概率密度曲线是一条 形态固定 的曲线。
对标准正态分布,通常用φ(x)表示概率密度函数,用Φ(x)表示分布函数:
假设有一次物理考试特别难,满分100分,全班只有大概20个人及格。与此同时语文考试很简单,全班绝大部分都考了90分以上。小明的物理和语文分别考了60分和80分,他回家后告诉家长,这时家长能仅仅从两科科目的分值直接判断出这次小明的语文成绩要比物理好很多吗?如果不能,应该如何判断呢?此时Z-score就派上用场了。 Z-Score的计算定义 :
即 将随机变量X先减去总体样本均值,再除以总体样本标准差就得到标准分数啦。如果X低于平均值,则Z为负数,反之为正数 。通过计算标准分数,可以将任何一个一般的正态分布转化为标准正态分布。
小明家长从老师那得知物理的全班平均成绩为40分,标准差为10,而语文的平均成绩为92分,标准差为4。分别计算两科成绩的标准分数:
物理:标准分数 = (60-40)/10 = 2
语文:标准分数 = (85-95)/4 = -2.5
从计算结果来看,说明这次考试小明的物理成绩在全部同学中算是考得很不错的,而语文考得很差。
指数分布可能容易和前面的泊松分布混淆,泊松分布强调的是某段时间内随机事件发生的次数的概率分布,而指数分布说的是 随机事件发生的时间间隔 的概率分布。比如一班地铁进站的间隔时间。如果随机变量X的概率密度为:
则称X服从指数分布,其中的参数λ0。 对应的分布函数 为:
均匀分布的期望值和方差 分别为:
使用Python绘制指数分布的概率分布图:
均匀分布有两种,分为 离散型均匀分布和连续型均匀分布 。其中离散型均匀分布最常见的例子就是抛掷骰子啦。抛掷骰子出现的点数就是一个离散型随机变量,点数可能有1,2,3,4,5,6。每个数出现的概率都是1/6。
设连续型随机变量X具有概率密度函数:
则称X服从区间(a,b)上的均匀分布。X在等长度的子区间内取值的概率相同。对应的分布函数为:
f(x)和F(x)的图形分别如下图所示:
均匀分布的期望值和方差 分别为:
若n个相互独立的随机变量 ,均服从标准正态分布(也称独立同分布于标准正态分布),则这n个服从标准正态分布的随机变量的平方和
构成一新的随机变量,其卡方分布规律称为 分布(chi-square distribution),其中参数 称为自由度,正如正态分布中均值或方差不同就是另一个正态分布一样,自由度不同就是另一个分布。卡方分布是由正态分布构造而成的一个新的分布,当自由度 很大时, 分布近似为正态分布。 对于任意正整数 , 自由度为 的卡方分布是一个随机变量 的机率分布。
有几种不同的分布方式。
01两点分布0-1分布(两点分布),它的随机变量的取值为1或0即离散型随机变量X的概率分布为:P{X=0}=1-p,P{X=1}=p,二项分布Binomialdistribution,泊松分布Poissondistribution正态分布,均匀分布Uniformdistribution,。
离散型随机变量如果随机变量X的所有取值都可以逐个列举出来,则称X为离散型随机变量相应的概率分布有二项分布,泊松分布连续型随机变量如果随机变量X的所有取值无法逐个列举出来,而是取数轴上某一区间内的任一点,则称X为连续型随机变量相应的概率分布有正态分布,均匀分布,指数分布,伽马分布,偏态分布,卡方分布,beta分布等(真多分布,好恐怖~~)期望值在离散型随机变量X的一切可能值中,各可能值与其对应概率的乘积之和称为该随机变量X的期望值,记作E(X)比如有随机变量,取值依次为:2,2,2,4,5求其平均值:(2+2+2+4+5)/5=3。
为什么要叫“卡方”?因为原名是“chi-squared”,一半是音译,一半是意译。其中,chi 是希腊字母 的读音,其实读音更像是“开”,而不是“卡”。square表示平方,因此在英语中,卡方分布写作 distribution。
在理解卡方检验之前,应当理解卡方分布。卡方分布是一种连续概率分布。
如果一个随机变量 服从标准正态分布,即 ,那么 就服从自由度为1的卡方分布。记作 或者
而如果 都服从标准正态分布,那么它们的平方和服从自由度为 的卡方分布,记作:
或者写作 。
对于非负自变量 的自由度为 的卡方分布的概率密度函数 (简称"pdf"):
(1)为什么 非负?因为根据定义,卡方分布的自变量是一个平方和。
(2)这里的 是一个函数。关于这个函数具体是什么,以及上门的概率密度函数如何推导,这里不展开,只需要知道有这么个函数即可。实在是好奇的,可以 参考这里 。
(3)卡方分布的均值为 ,而标准差为 。
(4)自由度越大,该函数图像越对称。
(5)为什么这里 需要正态分布,我的理解是,如果零假设为真,那么观测值和期望值之间的波动程度,应该是正态分布的,或者说“噪声”应该是正态分布的。
卡方检验有两个用途:
拟合优度检验 chi-squared test goodness of fit
独立性检验 chi-squared test of independence
某新闻说某个篮球明星的原地两连投的单次命中率是0.8,根据历次比赛的数据汇总得到下面的表格:
意思是说,在比赛中,有5次两连投是一次都没中,有82次是在两连投中命中1次。现在,我们来用卡方检验验证新闻说的0.8的命中率是否正确。零假设如下:
:两连投的成功次数符合二项分布,且概率为
(1)先根据零假设计算“期望”的命中次数分布:
由于总的观察次数为 ,于是在 成立的前提下,可以计算每种两连投结果的期望次数:
0次命中:
1次命中:
2次命中:
显然,期望的观察次数和实际的观察次数是有偏差的,那么问题在于这个偏差是否大到具有统计显著性,进而可以否定零假设。
(2)我们来构造卡方检验统计量(chi-squared test statistic):
这个值是把表里每个格子的实际值和期望值进行对比。为什么要用平方?目的在于规避正负号的影响。为什么要除以期望值?目的在于消除数量绝对值的影响。例如你预算3块钱的水,商家加价50元,那么这个波动是你无法忍受的,而你预算20万的车,商家加价50元,则变得可以忍受。也就是说,除以期望值目的在于聚焦于变化率,而不是变化量。
之后,把这些“变化率”加总得到 。而计算自由度有一个公式:
其中 R 表示行数,C 表示列数。对于本例:
从另一个角度解释为什么 :前面的定义是如果是 个符合标准正态分布的 相加,则自由度是 ,但是这里自有两个格子可以自由变化,第三个格子可以用总观察数减出来,例如 。
因此,真正自由的只有2个格子,所以自由度是2。
好了,将格子的数据代入,求出检验统计量:
(3)根据自由度为2的卡方分布,找到检验统计量对应的位置:
不难理解,随着统计量增大,表示预期的分布和实际的分布的差异也就越来越大。
另外,由于通常意义上,p值是越小越能推翻零假设,那么显然我们需要用右侧的面积来表示p值,这里用Python计算来代替查表:
输出:statistic: 17.26, pvalue: 0.0002
由于p值很小(假设我们的显著性水平的0.05),那么我们可以推翻零假设。
进一步的,我们来探索下,该运动员的两连投的成功次数分数是否真的符合二项分布。零假设:
:两连投的成功次数符合二项分布。
既然符合二项分布,那么我们需要先估算一下最合理的 概率,那当然是用总命中数除以总投篮数来计算了:
然后,用该概率值重复之前的计算,也就是先计算出一个期望的表格:
注意,这里的 ,这是因为,我们每从数据估计一个参数,那么我们就损失一个自由度。这里用了一个平均命中的概率,因此自由度只有 。
这时候,在使用 Python 进行计算时,注意调整默认的自由度:
这里的 ddof 就是额外损失的自由度,本意是“delta degree of freedom”
输出:statistic: 0.34, pvalue: 0.56
可以看到p值很大,因此不足以推翻零假设,也就是说该运动员的投篮命中次数可能真的是二项分布。
下面表格表示喝酒频率和与警察发生麻烦的频数。
以第一列为例,表示从不喝酒的人中,4992人不发生麻烦,71人会发生麻烦。
现在的问题是,能否从以下数据推断说喝酒频率和与警察发生麻烦这两个事件相互独立?
我们的零假设应该如何设计?如果要说明两者相互独立,那么上表的分布应该满足乘法公式。也就是说两个独立事件一起发生的概率等于分别发生的概率之积。
于是我们有:
发生麻烦的总人数除以总人数
不喝酒的总人数除以总人数
进一步,根据总人数算出不喝酒而发生麻烦的人数的期望(下标表示零假设):
用类似的算法,计算每一个格子在零假设成立的情况下的值,写在原表数据下的括号里:
仔细观察可以看出,其实每个格子就是对应的:
另外可以看到,零假设下的各个格子的行列之和与原来相同。这不是偶然的,我们用字母代替计算一下就知道了:
于是第一列的两个格子应该是:
对于其他格子、行的总和,都一样,这里不多说了。
好,继续分析。我们直接用上表计算卡方统计量和p值:
这部分计算方法和拟合优度是一样的,就不赘述了。计算发现这个p值非常小,接近0,因此我们可以推翻零假设。也就是说,喝酒的频率和被警察找麻烦的并不是独立的,而是相关的。
关于独立性检验,有一个比卡方检验更精准的检验,叫 fisher's exact test。它通过直接计算否定零假设的概率,也就直接得到了一个准确的p值。有一个经典的女士品茶的统计学故事。有一个女士号称可以区分出一杯茶是先倒入了奶还是先倒入了茶。统计学家 Fisher 为了验证她的说法,做了一个实验。拿了8杯茶,4杯是先茶后奶,4杯是先奶后茶。
实验结果是全部说对了。那么问题是,这是否具有统计显著性呢?比如说一个人猜对了一次硬币,他的预测能力靠谱吗?
我们假设女士的判断是完全随机的,这个是我们的零假设。那么8杯里面抽中4杯全对的概率是:
如果显著性水平是0.01,那么我们不能推翻零假设,即不敢确定这位女士真有这个识别能力。如果显著性水平定在0.05,则我们可以认为她确实有这个识别能力。
如果让实验结果有更大的说服力呢?一个简单的办法就是增加茶的数量,比如我们设定为两种茶各10杯,要求10杯都判断正确,那么p值为多少呢?
这个算起来比较麻烦,这里我写一个 python 脚本来计算:
计算结果:
这个p值就小得很夸张了,基本可以断定零假设不成立了。
那么,回到实验本身,如果女士只选对了三杯,那么在零假设的前提下,这个发生的概率是多少?
这个概率比较大了,原大于通常使用的显著性水平 0.05,因此我们没有办法推翻零假设。为什么要这样 乘 呢?这个是因为一共有这么多种取法。你把所有可能的取法罗列处理,就是16种,然后除以总的取法数,就是随机取到这样结果的概率。
比较 Fisher's exact test 和 chi-squared test,可以 参考这篇文章 。
一般来说,两者都适用的情况下,应该优先选择 Fisher's exact test,因为它是精确值。如果实验观察的数量很小(小于10),应该不使用 chi-squared test。
下面使用一个脚本来计算: