卡方分箱及代码实现

本文先从统计基础的卡方分布、卡方检验说起,之后再到卡方分箱的理解就比较容易,最后是利用Python如何实现卡方分箱。

1.卡方分布

  1. 定义: 设随机变量 X 1 X_1 X1 X 2 X_2 X2 X 3 X_3 X3,…, X n X_n Xn相互独立,且 X i X_i Xi(i=1,2,3,…,n)服从标准正态分布N(0,1),则他们的平方和 ∑ X i 2 \sum X_i^2 Xi2服从自由度为n的 χ 2 \chi^2 χ2分布。
  2. χ 2 \chi^2 χ2分布示意图
    卡方分布示意图在这里插入图片描述
  • 如上图,df自由度越小,分布就越向左倾斜,随着自由度逐渐增大, χ 2 \chi^2 χ2分布趋近于正态分布。

2.卡方统计量

χ 2 \chi^2 χ2统计量:

χ 2 = ∑ ( f 0 − f e ) 2 f e \chi^2=\sum\frac{(f_0-f_e)^2}{f_e} χ2=fe(f0fe)2

f 0 f_0 f0表示观测值频数, f e f_e fe表示期望值频数

  • χ 2 \chi^2 χ2统计量描述了观察值与期望值的接近程度,两者越接近,即 ( f 0 − f e ) (f_0-f_e) (f0fe)的绝对值越小,计算的 χ 2 \chi^2 χ2值就越小;反之,则 χ 2 \chi^2 χ2越大。

3.卡方检验

  • χ 2 \chi^2 χ2检验是对分类数据的频数进行分析的统计方法;用于分析分类变量与分类变量之间的关系(相关程度)

  • 通过对 χ 2 \chi^2 χ2的计算结果与 χ 2 \chi^2 χ2分布中的临界值进行比较,做出是否拒绝原假设的统计决策。

  • χ 2 \chi^2 χ2检验分为拟合优度检验和独立性检验

3.1 拟合优度检验

  • 定义:拟合优度检验是对一个分类变量的检验,即根据总体的分布情况,计算出分类变量中各分类的期望频数,与分布的观测频数进行对比,判断期望频数与观察频数是否有显著差异。
  • 举例:泰坦尼克号例子中,我们关注存活情况与性别是否有关系。
    船上共有2208人,其中男性1738人,女性470人;幸存者718人,其中男性374人女性344人,以 α \alpha α=0.1的显著性水平检验存活状况是否与性别相关。
    解:
    1、提出假设:
    H 0 H_0 H0:观察频数与期望频数一致
    H 1 H_1 H1:观察频数与期望频数不一致
    2、根据观察频数计算期望频数:
    总体存活率为718/2208=0.325,如果是否活下与性别无关,那么1738名男性中应该存活的人数为17380.325=565人,在470位女性中应存活4700.325=153人。故565和153就是期望频数,实际存活数就是观察频数。
    3、计算卡方统计量:
    在这里插入图片描述
    4、根据自由度和显著性水平,查找卡方分布表临界值,并与上一步 χ 2 \chi^2 χ2值进行比较,做出接受或拒接原假设的决策。
    自由度df=R-1=1,R为分类变量类型的个数。 显著性水平为0.1, 查表得 χ 0.1 2 ( 1 ) \chi^2_{0.1}(1) χ0.12(1)=2.706;
    所以 χ 2 \chi^2 χ2远大于 χ 0.1 2 ( 1 ) \chi^2_{0.1}(1) χ0.12(1),拒绝 H 0 H_0 H0,接受 H 1 H_1 H1,说明存活情况与性别有关。

3.2 列联分析:独立性检验

  • 定义:独立性检验对两个分类变量的检验,分析过程通过列联表(contingency table)方式呈现,实际就转换为分析列联表中行变量与列变量是否相互独立(或有关联)。

  • 举例:一种原料来自三个不同地区,原料质量又被分成三个不同等级,从这批原料中随机抽取500个检验,如下表;那么检验地区和等级直接是否存在依赖关系。
    在这里插入图片描述
    解:
    1、提出假设:
    H 0 H_0 H0:地区和原料等级之间是独立的
    H 1 H_1 H1:地区和原料等级之间是不独立的(存在依赖关系)

    2、根据观察频数计算期望频数:

    • 以列联表表第一行第一列(甲,一级)为例,甲地区的合计为140,用140/500作为甲地区的原料比例估计值;一级原料的合计为162,用162/500作为一级原料的比例的估计值。如果地区与原料等级独立,那么第一行第一列单元格的期望值比例为:
      (140/500)x(162/500)= 0.09072,故相应的频数为 0.09072 x 500 = 45.36。
  • 推广,可采用以下方式计算任一单元格的期望频数:

    f e f_e fe = R T n \frac{RT}{n} nRT * C T n \frac{CT}{n} nCT * n n n = C T ∗ R T n \frac{CT *RT}{n} nCTRT

    RT为给定单元格所在行的合计,CT为给定单元格所在列的统计,n为观测值的个数,即样本量。

    3. 计算卡方统计量
    在这里插入图片描述
    4、根据自由度和显著性水平,查找卡方分布表临界值,并与上一步 χ 2 \chi^2 χ2值进行比较,做出接受或拒接原假设的决策。
    自由度df=(R-1)(C-1)=4,显著性水平取0.05,查看可知 χ 0.05 2 ( 4 ) \chi^2_{0.05}(4) χ0.052(4)=9.488;
    所以 χ 2 \chi^2 χ2远大于 χ 0.05 2 ( 4 ) \chi^2_{0.05}(4) χ0.052(4),拒绝 H 0 H_0 H0,接受 H 1 H_1 H1,说明地区和原料等级存在依赖关系,即原料等级受地区的影响。

4. 卡方分箱原理及流程

卡方分箱是基于卡方检验中第二种列联独立性检验原理进行分箱。

以下是截取卡方分箱论文中分箱的算法:
在这里插入图片描述
在这里插入图片描述

  1. 上诉算法理解分为初始化和自底向上的合并过程

    • 初始化:首先根据连续变量的值的大小排序,进行初始的离散处理

    • 合并:
      箱子合并过程分为两个步骤,连续重复进行:
      1. 计算每个相邻箱子的 χ 2 \chi^2 χ2
      2. 对低卡方值的相邻箱子进行合并
      (根据卡方检验原理可知卡方值越低,表明两个类别越独立,相互影响的程度越小;或者另一种理解是两箱分布相似,可以进行合并。)

      合并停止条件:

      • 直到所有相邻箱子的 χ 2 \chi^2 χ2值大于等于设置的 χ 2 \chi^2 χ2阈值
        (根据自由度和显著性水平选取合适的 χ 2 \chi^2 χ2阈值;自由度则是根据数据能够确定的为(R-1)x(C-1),因为都是计算相邻两箱的,故R=2;C也可根据数据情况确定。显著性水平推荐选择0.1,0.05,0.01。)
      • 或者,箱子数量达到预先设置的数量
        (可以设置最小分箱数和最大分箱数,推荐最大分箱数在10-15间)
  2. 公式的理解
    在这里插入图片描述
    与卡方检验中列联独立性检验的 χ 2 \chi^2 χ2值的计算是相同的
    A i j A_{ij} Aij就是 f 0 f_0 f0 E i j E_{ij} Eij就是 f e f_e fe
    k就是列的分类变量类型个数
    m=2(只计算行相邻两箱)

具体也可看下面示例:
ps:注意每两箱计算时列的合计数是不同的,只使用两箱的列合计数,而不是全部的列合计数,行合计数不受影响。样本数量也只使用两箱的样本数量。
在这里插入图片描述
在这里插入图片描述

5.利用Python实现卡方分箱

def chimerge(data,col,target,n=20,alpha=0.05,max_groups=8):
    '''
    data: 输入的pandas DataFrame数据集
    col: 需要分箱得连续型变量名
    target: Y值
    n:初始化分箱的个数,一般选取比较大的值(使用的是等距分箱)
    alpha:卡方分布的显著性水平
    max_groups: 最大分箱个数
    '''
    # 先进行初略的等距分箱
    df = data[[col,target]]
    df['cut'],bins = pd.cut(df[col],bins=n,retbins=True,right=False)   # right=False,使得区间为左闭右开如[0,10),与下面的cutoff相一致
    freq_tab = pd.crosstab(df['cut'],df[target],dropna=False)  # 注意要添加dropna=False,否则全为0的组就不显示
    freq = freq_tab.values  # 转换为array数组
    cutoff = bins[:-1]      # 分组区间是左闭右开的,如cutoffs = [1,2,3],则表示区间 [1,2) , [2,3) ,[3,3+)。
    
    # 以下代码确保每一箱中都有target正负样本
    # 这样做的好处一是后续如果计算WOE时有这个需要,二是也能保证计算的期望频数fe不为0。

    for i in range(n):
    # 如果第一箱没有包含正样本或负样本,则向下一组合并
    # 但即使原来第一箱和第二进行和合并,还是不能保证新的第一箱都包含正负样本,故使用continue跳出本次循环,开始下一次循环
        if 0 in freq[0]:
            freq[0] = freq[0] + freq[1]
            freq =  np.delete(freq,1,0)
            cutoff = np.delete(cutoff,1,0)
            continue
    # 经过上面代码确保第一箱都包含正负样本,则判断之后的每箱,是否都包含正负样本,如果不包含,则向前一箱合并
        for i in range(1,len(freq)):
            if 0 in freq[i]:
                freq[i-1] = freq[i] + freq[i-1]
                freq =  np.delete(freq,i,0)
                cutoff = np.delete(cutoff,i,0)
                break
        else:
            break

   # 计算相邻箱的卡方值
    threshold = scipy.stats.chi2.isf(alpha,freq.shape[-1])  # 卡方阈值根据显著性水平和自由度设置
    while len(freq) > max_groups:     # 先根据设定的最大分箱数,合并最小的卡方值
        chi_vs=[]
        for i in range(len(freq)-1):
            chi_v = scipy.stats.chi2_contingency(freq[i:i+2])[0]
            chi_vs.append(chi_v)
        i = chi_vs.index(min(chi_vs))
        freq[i] = freq[i] + freq[i+1]
        freq = np.delete(freq,i+1,0)
        cutoff = np.delete(cutoff,i+1,0)


  # 按照预先设定的分箱数合并完毕后,如果发现最小卡方值还有低于卡方阈值的再接着合并
    while True:   
        for i in range(len(freq)-1):
            chi_vs=[]
            chi_v = scipy.stats.chi2_contingency(freq[i:i+2])[0]
            chi_vs.append(chi_v)
        if min(chi_vs) < threshold:
            i = chi_vs.index(min(chi_vs))
            freq[i] = freq[i] + freq[i+1]
            freq = np.delete(freq,i+1,0)
            cutoff = np.delete(cutoff,i+1,0)
            continue
        else:
            break
    return cutoff,freq
# 将变量的值转换为相应的组
def value_to_group(x,cutoff):
    '''
    x 是需要划分组的标量值
    cutoff 是根据上述chimerge函数返回的箱边值
    '''
    cutoff.sort()
    # 小于最小值的异常值也都放入第一组
    if x < cutoff[0]:
        return 'group-1'
    for i in range(1,len(cutoff)):
        if cutoff[i-1] <= x < cutoff[i]:
            return 'group-{}'.format(i)
    # 最后一组也包含非常大的异常值
    if x >= cutoff[len(cutoff)-1]:
        return 'group-{}'.format(len(cutoff))

以上,如有错误,还请大家指教。

【参考资料】
1.贾俊平统计学第七版
2.卡方分箱论文
3.菜菜的sklearn课堂
4.Python评分卡建模—卡方分箱(2)之代码实现
5.python中break,continue,pass,else的用法和区别详解

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值