卡方分布的定义:
若k个独立的随机变量Z1,Z2,…,Zk满足标准正态分布N(0, 1), 则这k个随机变量的平方和:
X = ∑ i = 1 k Z i 2 X = \sum_{i=1}^{k}Z_{i}^2 X=i=1∑kZi2
为服从自由度为k的卡方分布, 记做:
X − χ 2 ( k ) 或 者 记 作 X − χ k 2 X-\chi^{2}(k) 或者记作X-\chi^2_{k} X−χ2(k)或者记作X−χk2
卡方检验是以卡方分布为基础的一种假设检验方法, 主要用于分类变量之间的独立性检验
基本思想: 根据样本数据推断总体的分布与期望分布是否有显著性差异, 或者推断两个分类变量是否相关或者独立
一般原假设为: 观察频数与期望频数没有差异, 或者两个变量相互独立不相关
卡方:表示为观察值与理论值间的偏离程度
卡方值的计算公式为:
X k 2 = ∑ ( A − E ) 2 E X^{2}_{k} = \sum\frac{(A-E)^2}{E} Xk2=∑E(A−E)2
其中A为实际频数, E为期望频数.
卡方值包含以下两个信息:
1.实际值与理论值偏差的绝对大小
2.差异程度与理论值的相对大小
根据卡方分布, 卡方统计量以及自由度, 可以确定在原假设成立的情况下获得当前统计量和更极端情况的概率p.如果p很小, 说明观察值与理论值的偏离程度
卡方分布表
横轴为p值, 纵轴为自由度
自由度k:(行数-1)*(列数-1)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2DmfpQnm-1579400182449)(C:\Users\15743\Pictures\Saved Pictures\卡方分箱算法.jpg)]
算法主要包含两个阶段:
初始化阶段和自底向上合并的阶段
1.初始化阶段: 首先按照属性值的大小进行排序(对于非连续特征, 需要先做数值转化, 比如坏账率, 然后排序), 然后将每个属性值单独作为一组
2.合并阶段:
(1) 对每一对相邻的组, 计算卡方值
(2) 根据计算的卡方值, 对其中最小的一对邻组合并为一组
(3) 不断重复(1), (2)直到计算出的卡方值都不低于事先设定的阈值, 或者分组数达到一定的数量
import numpy as np
import pandas as pd
from scipy.stats import chi2
# 计算卡方值
def chi_value(data):
"""
Calculate Chi-square Value
"""
assert type(data).__name__ == "ndarray" and np.ndim(data) <= 2
# 计算行, 列之和
col_sum = data.sum(axis=0)
row_sum = data.sum(axis=1)
e_sum = data.sum()
# 计算期望频数
# E = np.ones(arr.shape) * C_N / N
# E = (E.T * R_N).T
# square = (arr - E) ** 2 / E
e = np.ones(data.shape) * col_sum / e_sum
e = (e.T * row_sum).T
square = (data - e) ** 2 / e
square[e == 0] = 0
return square.sum()
def chi_merge(df, col, target, max_group, threshold):
"""
ChiMerge 卡方分箱
df: dataframe
col: 需要分箱的变量
target: 类标签
max_group: 最大分组
threshold: 卡方阈值
returns:
cutoffs: 变量分箱列表 list
"""
assert type(df).__name__ == "DataFrame"
cross_tab = pd.crosstab(df[col], df[target])
cross_stat = cross_tab.values
# 分组区间是左闭右开的,如cutoffs = [1,2,3],则表示区间 [1,2) , [2,3) ,[3,3+)。
cutoffs = cross_tab.index.values
print("sorted cutoffs list:", cutoffs, "len cutoffs", len(cutoffs))
if max_group is None:
if threshold is None:
cls_num = cross_stat.shape[-1]
threshold = chi2.isf(0.05, df=cls_num-1)
while True:
min_value = None
minidx = None
# 对每一对相邻的组, 计算卡方值
for i in range(len(cross_stat) - 1):
chi_val = chi_value(cross_stat[i: i+2])
print(chi_val)
if min_value is None or (min_value > chi_val):
min_value = chi_val
minidx = i
if (max_group is not None and max_group < len(cross_stat)) or (threshold is not None and min_value < threshold):
tmp = cross_stat[minidx] + cross_stat[minidx + 1]
cross_stat[minidx] = tmp
cross_stat = np.delete(cross_stat, minidx + 1, minidx)
cutoffs = np.delete(cutoffs, minidx + 1, 0)
print("---", cutoffs)
else:
break
return cutoffs
if __name__ == "__main__":
df = pd.read_csv(r"D:\PycharmProjects\test\toadtest\germancredit.csv")
df.columns = [i.lower() for i in df.columns]
# target = "creditability" col = "duration of credit (month)"
print(chi_merge(df, target="creditability", col='duration of credit (month)', max_group=5, threshold=None))
ion of credit (month)"
print(chi_merge(df, target=“creditability”, col=‘duration of credit (month)’, max_group=5, threshold=None))