卡方检测表格(详解卡方分箱及应用)

时间:2023-03-28 12:55:08来源:本站整理作者:点击:

  最近在研究评分卡建模的流程,在特征处理的过程中涉及到分箱这一基本的常用技巧,本文就对分箱中的卡方分箱展开详细介绍。  分箱就是将连续型的数据离散化,比如年龄这个变量是,可以分箱为0-18,18-30,30-45,45-60。这也是建立评分卡过程中常见的操作,首先思考一个问题,为什么要进行分箱?直接用年龄这个变量去建模是否可以?其实是可以的。只不过评分卡需要模型有很强的业务可解释性,这和你的建模算法有关。如果你用xgb、lgb等机器学习算法的话,模型会变得不可解释,此时不分箱也是可以的。  分箱的好处主要有这些:

  1. 分箱后的特征对异常数据有更强的鲁棒性。比如年龄中有一个异常值为300,分箱之后就可能划到>80这一箱中,而如果直接入模的话会对模型造成很大干扰。
  2. 特征离散化之后,每个变量有单独的权重,可以为逻辑回归模型引入了非线性,能够提升模型表达能力,加大拟合。
  3. 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。
  4. 可以将缺失作为独立的一类带入模型。
  5. 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展。

  下面开始介绍卡方分箱,首先要先了解卡方检验。因为卡方分箱是一种基于卡方检验的分箱方法,具体来说是基于卡方检验中的独立性检验来实现分箱功能。

卡方检验

  卡方检验就是对分类数据的频数进行分析的一种方法,它的应用主要表现在两个方面:拟合优度检验和独立性检验(列联分析)。

  • 拟合优度检验  拟合优度是对一个分类变量的检验,即根据总体分布状况,计算出分类变量中各类别的期望频数,与分布的观察频数进行对比,判断期望频数与观察频数是否有显著差异,从而达到对分类变量进行分析的目的。比如,泰坦尼克号中我们观察幸存者是否与性别有关,可以理解为一个X是否与Y有必然联系。
  • 独立性检验  独立性检验是两个特征变量之间的计算,它可以用来分析两个分类变量是否独立,或者是否有关联。比如某原料质量和产地是否依赖关系,可以理解为一个X与另一个X是否独立。

卡方检验步骤

  卡方检验也是一种假设检验,与常见的假设检验方法一致。

  • 提出假设,比如假设两个变量之间独立
  • 根据分类的观察频数计算期望频数
  • 根据卡方公式,计算实际频数与期望频数的卡方值
  • 根据自由度和事先确定的显著性水平,查找卡方分布表计算卡法值,并与上一步卡方值比较
  • 得出结果判断是否拒绝原假设

评分卡中的卡方分箱

  下面以年龄变量为例,讲解一下评分卡建模过程中如何对年龄变量进行卡方分箱。先举实际例子再讲理论。

  首先,将年龄从小到大排序,每一个年龄取值为单独一箱。统计对应的违约和不违约的个数。然后进行合并,具体步骤如下:

  1. 如果有1,2,3,4个分箱,那么就需要绑定相邻的两个分箱,共三组:12,23,34。然后分别计算三个绑定组的卡方值。
  2. 从计算的卡方值中找出最小的一个,并把这两个分箱合并:比如,23是卡方值最小的一个,那么就将2和3合并,本轮计算中分箱就变为了1,23,4。

  分箱背后的理论依据:如果两个相邻的区间具有非常类似的类分布,那么这两个区间可以合并。否则,它们应该分开。低卡方值表明它们具有相似的类分布。

卡方检测表格(详解卡方分箱及应用)(1)

  对于卡方值越小分布越相似这一核心理论我也做了个简单的推导:

卡方检测表格(详解卡方分箱及应用)(2)

  可以看到如果需要合并的两箱分布完全一致的话,合并之后的卡方值为0。下面给出卡方分箱的理论及公式:

卡方检测表格(详解卡方分箱及应用)(3)

  上面的步骤只是每一轮需要计算的内容,如果不设置停止条件,算法就会一直运行。当然,我们一般会设置一些停止条件:

  • 卡方停止的阈值
  • 分箱数目的限制

  根据经验值,卡方停止的阈值一般设置置信度为0.9、0.95、0.99,自由度可以设置为4是对应的卡方值,分箱数一般可以设置为5。卡方分箱的自由度是分类变量类型的个数减一。

  下面给一个卡方分箱的代码,建议仔细阅读,有助于代码水平的提高和更好地理解卡方分箱。一定要一次性看完。

## 自写卡方最优分箱过程 def get_chi2(X, col): ''' 计算卡方统计量 ''' # 计算样本期望频率 pos_cnt = X['Defaulter'].sum() all_cnt = X['Defaulter'].count() expected_ratio = float(pos_cnt) / all_cnt # 对变量按属性值从大到小排序 df = X[[col, 'Defaulter']] df = df.dropna() col_value = list(set(df[col])) col_value.sort() # 计算每一个区间的卡方统计量 chi_list = [] pos_list = [] expected_pos_list = [] for value in col_value: df_pos_cnt = df.loc[df[col] == value, 'Defaulter'].sum() df_all_cnt = df.loc[df[col] == value,'Defaulter'].count() expected_pos_cnt = df_all_cnt * expected_ratio chi_square = (df_pos_cnt - expected_pos_cnt)**2 / expected_pos_cnt chi_list.append(chi_square) pos_list.append(df_pos_cnt) expected_pos_list.append(expected_pos_cnt) # 导出结果到dataframe chi_result = pd.DataFrame({col: col_value, 'chi_square':chi_list, 'pos_cnt':pos_list, 'expected_pos_cnt':expected_pos_list}) return chi_result def chiMerge(chi_result, maxInterval=5): ''' 根据最大区间数限制法则,进行区间合并 ''' group_cnt = len(chi_result) # 如果变量区间超过最大分箱限制,则根据合并原则进行合并,直至在maxInterval之内 while(group_cnt > maxInterval): ## 取出卡方值最小的区间 min_index = chi_result[chi_result['chi_square'] == chi_result['chi_square'].min()].index.tolist()[0] # 如果分箱区间在最前,则向下合并 if min_index == 0: chi_result = merge_chiSquare(chi_result, min_index 1, min_index) # 如果分箱区间在最后,则向上合并 elif min_index == group_cnt-1: chi_result = merge_chiSquare(chi_result, min_index-1, min_index) # 如果分箱区间在中间,则判断两边的卡方值,选择最小卡方进行合并 else: if chi_result.loc[min_index-1, 'chi_square'] > chi_result.loc[min_index 1, 'chi_square']: chi_result = merge_chiSquare(chi_result, min_index, min_index 1) else: chi_result = merge_chiSquare(chi_result, min_index-1, min_index) group_cnt = len(chi_result) return chi_result def cal_chisqure_threshold(dfree=4, cf=0.1): ''' 根据给定的自由度和显著性水平, 计算卡方阈值 ''' 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 chiMerge_chisqure(chi_result, dfree=4, cf=0.1, maxInterval=5): threshold = cal_chisqure_threshold(dfree, cf) min_chiSquare = chi_result['chi_square'].min() group_cnt = len(chi_result) # 如果变量区间的最小卡方值小于阈值,则继续合并直到最小值大于等于阈值 while(min_chiSquare < threshold and group_cnt > maxInterval): min_index = chi_result[chi_result['chi_square']==chi_result['chi_square'].min()].index.tolist()[0] # 如果分箱区间在最前,则向下合并 if min_index == 0: chi_result = merge_chiSquare(chi_result, min_index 1, min_index) # 如果分箱区间在最后,则向上合并 elif min_index == group_cnt-1: chi_result = merge_chiSquare(chi_result, min_index-1, min_index) # 如果分箱区间在中间,则判断与其相邻的最小卡方的区间,然后进行合并 else: if chi_result.loc[min_index-1, 'chi_square'] > chi_result.loc[min_index 1, 'chi_square']: chi_result = merge_chiSquare(chi_result, min_index, min_index 1) else: chi_result = merge_chiSquare(chi_result, min_index-1, min_index) min_chiSquare = chi_result['chi_square'].min() group_cnt = len(chi_result) return chi_result def merge_chiSquare(chi_result, index, mergeIndex, a = 'expected_pos_cnt', b = 'pos_cnt', c = 'chi_square'): ''' 按index进行合并,并计算合并后的卡方值 mergeindex 是合并后的序列值 ''' 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] ## 两个区间合并后,新的chi2值如何计算 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]) ## 重置index chi_result = chi_result.reset_index(drop=True) return chi_result import copy chi_train_X = copy.deepcopy(train_X) ## 对数据进行卡方分箱,按照自由度进行分箱 chi_result_all = dict() for col in chi_train_X.columns: print("start get " col " chi2 result") chi2_result = get_chi2(train, col) chi2_merge = chiMerge_chisqure(chi2_result, dfree=4, cf=0.05, maxInterval=5) chi_result_all[col] = chi2_merge

>【作者】:Labryant

>【原创公众号】:风控猎人

>【简介】:某创业公司策略分析师,积极上进,努力提升。乾坤未定,你我都是黑马。

>【转载说明】:转载请说明出处,谢谢合作!~

,
最新文章
儿童视频
推荐文章

关于妃孕岛

Copyright 2022-2026 feiyundao.com 〖妃孕岛〗 版权所有 陕ICP备2022000637号-4

声明: 本站文章均来自互联网,不代表本站观点 如有异议 请与本站联系 本站为非赢利性网站 不接受任何赞助和广告