1.数据源文件
链接: https://pan.baidu.com/s/1ndgWRcPoPH8DKdkQ-HCrFg
提取码: 2s3k
2.处理连续没有正样本或负样本的区间
import pandas as pd
import numpy as np
data = pd.read_csv(u'1.csv', header=0)
# 根据自由度和显著性水平,查找卡方分布表临界值,即置信度水平
# 定义一个卡方分箱(可设置参数置信度水平与箱的个数)停止条件为大于置信水平且小于bin的数目
def ChiMerge( df, variable, flag, confidenceVal,bin):
"""
运行前需要 import pandas as pd 和 import numpy as np
df:data传入一个数据框仅包含一个需要卡方分箱的变量与正负样本标识(正样本为1,负样本为0)
variable:需要卡方分箱的变量名称(字符串)
flag:正负样本标识的名称(字符串)
confidenceVal:置信度水平(默认是不进行抽样95%)
bin:最多箱的数目
sample: 为抽样的数目(默认是不进行抽样),因为如果观测值过多运行会较慢
"""
# 进行数据格式化录入
total_num = df.groupby([variable])[flag].count() # 统计需分箱变量每个值数目
total_num = pd.DataFrame({'total_num': total_num}) # 创建一个数据框保存之前的结果
positive_class = df.groupby([variable])[flag].sum() # 统计需分箱变量每个值正样本数
positive_class = pd.DataFrame({'positive_class': positive_class}) # 创建一个数据框保存之前的结果
regroup = pd.merge(total_num, positive_class, left_index=True, right_index=True,
how='inner') # 组合total_num与positive_class
regroup.reset_index(inplace=True)
regroup['negative_class'] = regroup['total_num'] - regroup['positive_class'] # 统计需分箱变量每个值负样本数
regroup = regroup.drop('total_num', axis=1)
np_regroup = np.array(regroup) # 把数据框转化为numpy(提高运行效率)
print(np_regroup)
print('已完成数据读入,正在计算数据初处理')
# print(np_regroup.shape) # np_regroup 数组数据 (661,3)
# print(np_regroup.shape[0]) # np_regroup 元素个数 661
# 处理连续没有正样本或负样本的区间,并进行区间的合并(以免卡方值计算报错)
print(np_regroup[0, 0]) # 2
print(np_regroup[0, 1]) # 0 positive_class值
print(np_regroup[0, 2]) # 1 negative_class值
print(np_regroup[1, 0]) # 3
print(np_regroup[1, 1]) # 0 positive_class值
print(np_regroup[1, 2]) # 1 negative_class值
i = 0
while i <= np_regroup.shape[0] - 2: # i <= 661-2
if (np_regroup[i, 1] == 0 and np_regroup[i + 1, 1] == 0) or (np_regroup[i, 2] == 0 and np_regroup[i + 1, 2] == 0):
np_regroup[i, 0] = np_regroup[i + 1, 0] # 3
np_regroup[i, 1] = np_regroup[i, 1] + np_regroup[i + 1, 1] # 正样本0
np_regroup[i, 2] = np_regroup[i, 2] + np_regroup[i + 1, 2] # 负样本2
# print(str(i) + '-->[' + str(np_regroup[i, 0]) + ','+str(np_regroup[i, 1]) + ','+str(np_regroup[i, 2])+']')
np_regroup = np.delete(np_regroup, i + 1, 0)
i = i - 1
i = i + 1
# print('-->'+str(i))
# print(11111111111111111111111111)
# print(np_regroup[0, 0])
# print(np_regroup[0, 1])
# print(np_regroup[0, 2])
print(np_regroup)
print(np_regroup.shape[0])
# 对相邻两个区间进行卡方值计算
chi_table = np.array([]) # 创建一个数组保存相邻两个区间的卡方值
for i in np.arange(np_regroup.shape[0] - 1):
chi = (np_regroup[i, 1] * np_regroup[i + 1, 2] - np_regroup[i, 2] * np_regroup[i + 1, 1]) ** 2 \
* (np_regroup[i, 1] + np_regroup[i, 2] + np_regroup[i + 1, 1] + np_regroup[i + 1, 2]) / \
((np_regroup[i, 1] + np_regroup[i, 2]) * (np_regroup[i + 1, 1] + np_regroup[i + 1, 2]) * (
np_regroup[i, 1] + np_regroup[i + 1, 1]) * (np_regroup[i, 2] + np_regroup[i + 1, 2]))
chi_table = np.append(chi_table, chi)
print('已完成数据初处理,正在进行卡方分箱核心操作')
print(chi_table)
# 把卡方值最小的两个区间进行合并(卡方分箱核心)
while min(chi_table) < confidenceVal and len(chi_table) > (bin - 1): # 最小值小于置信度水平 and 长度大于最大箱数-1
chi_min_index = np.argwhere(chi_table == min(chi_table))[0] # 找出卡方值最小的位置索引
print(chi_min_index)
np_regroup[chi_min_index, 1] = np_regroup[chi_min_index, 1] + np_regroup[chi_min_index + 1, 1]
np_regroup[chi_min_index, 2] = np_regroup[chi_min_index, 2] + np_regroup[chi_min_index + 1, 2]
np_regroup[chi_min_index, 0] = np_regroup[chi_min_index + 1, 0]
np_regroup = np.delete(np_regroup, chi_min_index + 1, 0)
print(np_regroup)
if chi_min_index == np_regroup.shape[0] - 1: # 最小值是最后两个区间的时候
# 计算合并后当前区间与前一个区间的卡方值并替换
chi_table[chi_min_index - 1] = (np_regroup[chi_min_index - 1, 1] * np_regroup[chi_min_index, 2] - np_regroup[
chi_min_index - 1, 2] * np_regroup[chi_min_index, 1]) ** 2 \
* (np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index - 1, 2] +
np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2]) / \
((np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index - 1, 2]) * (
np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2]) * (
np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index, 1]) * (
np_regroup[chi_min_index - 1, 2] + np_regroup[chi_min_index, 2]))
# 删除替换前的卡方值
chi_table = np.delete(chi_table, chi_min_index, axis=0)
print(chi_table)
else:
# 计算合并后当前区间与前一个区间的卡方值并替换
chi_table[chi_min_index - 1] = (np_regroup[chi_min_index - 1, 1] * np_regroup[chi_min_index, 2] - np_regroup[
chi_min_index - 1, 2] * np_regroup[chi_min_index, 1]) ** 2 \
* (np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index - 1, 2] +
np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2]) / \
((np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index - 1, 2]) * (
np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2]) * (
np_regroup[chi_min_index - 1, 1] + np_regroup[chi_min_index, 1]) * (
np_regroup[chi_min_index - 1, 2] + np_regroup[chi_min_index, 2]))
# 计算合并后当前区间与后一个区间的卡方值并替换
chi_table[chi_min_index] = (np_regroup[chi_min_index, 1] * np_regroup[chi_min_index + 1, 2] - np_regroup[
chi_min_index, 2] * np_regroup[chi_min_index + 1, 1]) ** 2 \
* (np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2] + np_regroup[
chi_min_index + 1, 1] + np_regroup[chi_min_index + 1, 2]) / \
((np_regroup[chi_min_index, 1] + np_regroup[chi_min_index, 2]) * (
np_regroup[chi_min_index + 1, 1] + np_regroup[chi_min_index + 1, 2]) * (
np_regroup[chi_min_index, 1] + np_regroup[chi_min_index + 1, 1]) * (
np_regroup[chi_min_index, 2] + np_regroup[chi_min_index + 1, 2]))
# 删除替换前的卡方值
chi_table = np.delete(chi_table, chi_min_index + 1, axis=0)
print(chi_table)
print('已完成卡方分箱核心操作,正在保存结果')
print("--------------------------------")
print(np_regroup)
# 把结果保存成一个数据框
result_data = pd.DataFrame() # 创建一个保存结果的数据框
result_data['variable'] = [variable] * np_regroup.shape[0] # 结果表第一列:变量名
list_temp = []
for i in np.arange(np_regroup.shape[0]):
if i == 0:
x = '0' + ',' + str(np_regroup[i, 0])
elif i == np_regroup.shape[0] - 1:
x = str(np_regroup[i - 1, 0]) + '+'
else:
x = str(np_regroup[i - 1, 0]) + ',' + str(np_regroup[i, 0])
list_temp.append(x)
result_data['interval'] = list_temp # 结果表第二列:区间,前开后闭
result_data['flag_0'] = np_regroup[:, 2] # 结果表第三列:负样本数目
result_data['flag_1'] = np_regroup[:, 1] # 结果表第四列:正样本数目
return result_data
# 调用函数参数示例
if __name__ == '__main__':
bins = ChiMerge(data, 'value', 'badorgood', 12.59, 6)
total = bins['flag_0'].sum()+bins['flag_1'].sum()
bins['bad_rate'] = bins['flag_1'] / total # 计算每个箱体坏样本(原为1)所占总样本比例
bins['badattr'] = (bins['flag_1'] + 1) / (bins['flag_1'].sum()+1) # 计算每个箱体坏样本所占坏样本总数的比例 加1避免分子为0
bins['goodattr'] = (bins['flag_0']+1) / (bins['flag_0'].sum()+1) # 计算每个箱体好样本所占好样本总数的比例 加1避免分子为0
print(bins)
bins['woe'] = np.log(bins['badattr'] / bins['goodattr']) # 计算每个箱体的woe值
bins['iv'] = (bins['badattr'] - bins['goodattr']) * bins['woe'] # 计算每个变量的iv值
bins.to_csv('3.csv', sep=',', header=True, index=False)
print(bins)