使用Python建立市民个人收入评分卡(二)

5 连续型变量自动分箱

在评分卡建模过程中,数据中的连续型变量需要进行分箱,用于计算woe值。
这里使用卡方分箱进行分箱

# 卡方分箱
def Chi_merge(X, y, columns, k=6):
    item = dict()
    pinf = float('inf')  # 正无穷大
    ninf = float('-inf')  # 负无穷大
    # 需要选取连续变量,以及分箱的个数
    # 1.制作数据结构
        # [feature, label] --> [feature, label, count] --> [feature, [count1, count2, count3]]
    split_points = pd.DataFrame()
    for col in columns:
        df = pd.concat([X[col],y], axis=1)
        # print(df.head())

        # 统计重复特征个数
        df_gby = df.groupby(df.columns.values.tolist()).size().reset_index()
        df_gby.columns = ['feature', 'label', 'count']
        zeros = np.zeros(len(df_gby['label'].unique()),dtype=int)
        label_cnt = []
        for i in range(df_gby.shape[0]):
            label_cnt.append(copy.deepcopy(zeros))
        df_gby['label_cnt'] = label_cnt

        # 计数频数写进数据结构
        for _, loc in df_gby.iterrows():
            if loc[1] in range(len(df_gby['label'].unique())):
                index = loc[1]
                loc[3][index] = loc[2]
            else:
                raise TypeError("Data Excption")
        df_cnt = df_gby[['feature','label_cnt']]

        # 重复项求和
        for i in range(df_cnt.shape[0]):
            if df_cnt.iloc[i][0] == df_cnt.iloc[i-1][0]:
                for j in range(len(df_gby['label'].unique())):
                    df_cnt.iloc[i][1][j] += df_cnt.iloc[i-1][1][j]

        # 丢掉重复行
        df_cnt.drop_duplicates(subset=['feature'], keep='last', inplace=True)
        df_cnt.reset_index(drop=True, inplace=True)

        # 数据结构存入tuple
        tuple = []
        for i in range(df_cnt.shape[0]):
            tuple.append((str(df_cnt.iloc[i][0]),list(df_cnt.iloc[i][1])))

        # 2.卡方计算与合并
        # 卡方值计算
        def chi_square(A):
            m = len(A)
            k = len(A[0])
            R = []
            for i in range(m):
                sum = 0
                for j in range(k):
                    sum += A[i][j]
                R.append(sum)
            C = []
            for j in range(k):
                sum = 0
                for i in range(m):
                    sum += A[i][j]
                C.append(sum)
            N = 0
            for ele in C:
                N += ele
            res = 0
            for i in range(m):
                for j in range(k):
                    Eij = R[i] * C[j] / N
                    if Eij != 0:
                        res = res + (A[i][j] - Eij) ** 2 / Eij
            return res

        def combine(a, b):
            '''  a=('4.4', [3, 1, 0]), b=('4.5', [1, 0, 2])
                 combine(a,b)=('4.4', [4, 1, 2])  '''
            c = a[:]  # c[0]=a[0]
            for i in range(len(a[1])):
                c[1][i] += b[1][i]
            return (c)
        # 区间合并
        num_interval = len(tuple)
        while num_interval > k:
            num_pair = num_interval - 1
            chi_values = []
            for i in range(num_pair):
                arr = [tuple[i][1],tuple[i+1][1]]
                chi_values.append(chi_square(arr))

            min_chi = min(chi_values)  # get the minimum chi value
            for i in range(num_pair - 1, -1, -1):
                if chi_values[i] == min_chi:
                    tuple[i] = combine(tuple[i], tuple[i + 1])
                    tuple[i + 1] = 'Merged'
            while 'Merged' in tuple:
                tuple.remove('Merged')
            num_interval = len(tuple)
        interval = [float(record[0]) for record in tuple]
        del(interval[0])
        # split_points[col] = interval
        # print(interval)
        interval.insert(0, ninf)
        interval.append(pinf)
        item[col] = interval
    return item

分箱:

# 连续型变量自动分箱
X, y = split_column(df, "Y")
result = Chi_merge(X, y, col_values, k=8)
print("======================连续变量分箱==========================")
for r in result.items():
    print(r)
for k, v in result.items():
    col_item[k] = dict()
    col_item[k]["cut"] = v
    col_item[k]["type"] = "cut"

分箱结果:

======================连续变量分箱==========================
('年龄', [-inf, 21.0, 24.0, 28.0, 30.0, 34.0, 42.0, 60.0, inf])
('投资损失', [-inf, 1573.0, 1825.0, 1887.0, 1944.0, 1977.0, 1980.0, 2392.0, inf])
('教育时间', [-inf, 3.0, 9.0, 10.0, 11.0, 13.0, 14.0, 15.0, inf])
('工作天数', [-inf, 3.0, 35.0, 40.0, 42.0, 50.0, 51.0, 62.0, inf])
('投资收入', [-inf, 114.0, 3103.0, 3137.0, 4386.0, 4416.0, 5178.0, 99999.0, inf])

6 计算woe值

由于连续变量和离散变量两者分段不一致,连续变量和离散变量这里分开计算

# 计算woe值和iv值
# 连续变量的woe值
for k, v in col_item.items():
    # print("=== {}".format(k))
    col = k
    bin_list = v["cut"]
    count = len(bin_list)

    col_item[col]["woe"] = []
    for index in range(count):
        if index != count - 1:
            bin_count = len(df.loc[(df[col] > bin_list[index]) & (df[col] <= bin_list[index + 1]), col])
            bad_count = len(df.loc[(df[col] > bin_list[index]) & (df[col] <= bin_list[index + 1]) & (df["Y"] == 0), col])
            good_count = bin_count - bad_count
            # print("[{}, {}], good_count: {} bad_count: {}".format(bin_list[index], bin_list[index + 1], bin_count, good_count, bad_count))
            if (bad_count == 0) or (good_count == 0):
                woe = 0
            else:
                woe = (bad_count / total_bad) / (good_count / total_good)
                woe = math.log(woe)
            col_item[col]["woe"].append(woe)
            # print("woe {}".format(woe))

# 离散变量的woe值
for col in col_labels:
    col_item[col] = dict()

    result = df[col].value_counts()
    cols = result.index
    result_dict = {k: v for k, v in zip(cols, list(result))}
    # print(result_dict)
    types_list = result_dict.keys()
    col_item[col]["cut"] = list(types_list)
    col_item[col]["type"] = "type"

    col_item[col]["woe"] = []
    for k in types_list:
        bad_count = len(df.loc[(df[col] == k) & (df["Y"] == 0), col])
        good_count = len(df.loc[(df[col] == k) & (df["Y"] == 1), col])
        # print("[{}] total: {}, good: {}, bad: {}".format(k, v, bad_count, good_count))

        if (bad_count == 0) or (good_count == 0):
            woe = 0
        else:
            woe = (bad_count / total_bad) / (good_count / total_good)
            woe = math.log(woe)
        col_item[col]["woe"].append(woe)
        # print("woe {}".format(woe))

7 原始数据替换为woe值

将原始数据替换为woe值然后进行逻辑回归训练。

# 替换为woe值
for k, v in col_item.items():
    # 连续变量
    if v["type"] == "cut":
        col = k
        bin_list, woex = v["cut"], v["woe"]
        count = len(bin_list)
        for index in range(count):
            if index != count - 1:
                # print("{}, [ {} < {} ]".format(woex[index], bin_list[index], bin_list[index + 1]))
                df.loc[(df[col] > bin_list[index]) & (df[col] <= bin_list[index + 1]), col] = woex[index]
    # 离散变量
    else:
        col = k
        bin_list, woex = v["cut"], v["woe"]
        for index, type_name in enumerate(bin_list):
            df.loc[(df[col] == bin_list[index]), col] = woex[index]

8 逻辑回归

使用转换后的woe数据训练高收入预测二分类模型,计算的时候增加一个配置项,用于评分卡的计算

# 逻辑回归计算系数
X, y = split_column(df, y="Y")
drop_list = []
X = X.drop(drop_list, axis=1)
X['bia'] = 1
logic = LogisticRegression(penalty="l2", class_weight=None, C=1)
lg = logic.fit(X, y)
coef_list = lg.coef_[0]
coef_dict = {k: v for k, v in zip(X.columns.values, coef_list)}
print("======================coef 系数==========================")
print(coef_dict)

逻辑回归系数

======================coef 系数==========================
('工作情况', -0.08107345111649898)
('性别', -0.12778166055899795)
('bia', 3.3405391750781788)
('家庭角色', -0.09961242632590088)
('教育', -0.3154451804440804)
('教育时间', -0.5790788213669904)
('婚姻状况', -0.3732197877225377)
('年龄', -0.1772763593749565)
('投资收入', -2.5629206586528372)
('投资损失', -1.3755660231932954)
('职业类型', -0.25562556502783673)
('工作天数', -0.3618937334098486)

9 建立评分卡

评分卡公式介绍:https://blog.csdn.net/lll1528238733/article/details/76601897

这里就不重复介绍评分卡的公式和原理了,我们先计算出公式的系数,然后计算不同分段的得分权值,最后加总。
直接上代码:

# 计算分数
P = 600  # 指定比例时的分值
PD0 = 20  # 两倍比例的的分差值
default_ratio = 10  # 非高收入/高收入
A = P - (PD0 * math.log(default_ratio)) / math.log(2)
B = PD0 / math.log(2)
print("公式参数  A: {} B: {}".format(A, B))
base_score = round(A + B * coef_dict["bia"])
print("基础分值: {}".format(base_score))
print("======================coef 系数==========================")
for c in coef_dict.items():
    print(c)

items = []
for col in list(X.columns.values):
    if col != "bia":
        item = dict()
        item["col"] = col
        item["coef"] = coef_dict[col]
        item["cut"] = col_item[col]["cut"]
        item["woe"] = col_item[col]["woe"]
        items.append(item)

# print("===dict")
# for i in items:
#     print(i)

# 评分卡
print("======================评分标准==========================")
for item in items:
    print("====== {}".format(item["col"]))
    score = []
    for index, w in enumerate(item["woe"]):
        score = round(item["coef"] * w * B)
        if item['col'] in col_values:
            print("{}~{} => {}".format(item["cut"][index], item["cut"][index + 1], score))
        else:
            print("{} => {}".format(item["cut"][index], score))

得到的评分标准如下:

======================评分标准==========================
基础分值: 630.0
====== 年龄
-inf~21.0 => -90.0
21.0~24.0 => -45.0
24.0~28.0 => -19.0
28.0~30.0 => -6.0
30.0~34.0 => -0.0
34.0~42.0 => 7.0
42.0~60.0 => 12.0
60.0~inf => -0.0
====== 工作天数
-inf~3.0 => -10.0
3.0~35.0 => -18.0
35.0~40.0 => -2.0
40.0~42.0 => 4.0
42.0~50.0 => 11.0
50.0~51.0 => 3.0
51.0~62.0 => 12.0
62.0~inf => 8.0
====== 职业类型
保安 => 6.0
运输 => -3.0
机械操作 => -12.0
执行主管 => 15.0
管理文书 => -10.0
农业捕捞 => -13.0
其他职业 => -28.0
手工艺维修 => -1.0
销售 => 2.0
劳工保洁 => -21.0
家政服务 => -49.0
军人 => 12.0
技术支持 => 4.0
专业技术 => 14.0
未知 => -15.0
====== 投资收入
-inf~114.0 => -6.0
114.0~3103.0 => -11.0
3103.0~3137.0 => -0.0
3137.0~4386.0 => -7.0
4386.0~4416.0 => -0.0
4416.0~5178.0 => 28.0
5178.0~99999.0 => 114.0
99999.0~inf => -0.0
====== 投资损失
-inf~1573.0 => -2.0
1573.0~1825.0 => -90.0
1825.0~1887.0 => 71.0
1887.0~1944.0 => 103.0
1944.0~1977.0 => 83.0
1977.0~1980.0 => -0.0
1980.0~2392.0 => -3.0
2392.0~inf => 72.0
====== 教育
专科 => 2.0
职高 => 52.0
小学 => -46.0
高中 => -16.0
幼儿园 => -70.0
本科 => 20.0
研究生 => 33.0
初中 => -38.0
本科肄业 => -7.0
博士 => 51.0
====== 家庭角色
未婚 => -21.0
其他关系 => -30.0
离家 => -14.0
妻子 => 14.0
丈夫 => 13.0
孩子 => -41.0
====== 婚姻状况
未婚 => -28.0
已婚配偶异地 => -19.0
已婚 => 14.0
分居 => -22.0
离婚 => -16.0
丧偶 => -18.0
====== 教育时间
-inf~3.0 => 7.0
3.0~9.0 => 3.0
9.0~10.0 => 1.0
10.0~11.0 => -0.0
11.0~13.0 => -3.0
13.0~14.0 => -5.0
14.0~15.0 => -8.0
15.0~inf => -8.0
====== 工作情况
省政府 => 1.0
中央部委 => 0.0
非有限责任公司 => 0.0
有限责任公司 => 3.0
地方政府 => 1.0
个体 => -0.0
未知 => -2.0
====== 性别
男 => 0.0
女 => -1.0

下一步我们编写代码对测试集数据进行评分的计算和评分卡监控。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 评分是银行、金融机构以及其他信贷相关企业用来评估借款申请人信用风险的一种工具。而Python作为一种高级编程语言,可以应用于数据处理和建模方面,在评分的探索中也找到了应用。 首先,Python语言具有丰富的数据处理库,如pandas和numpy。通过这些库,我们可以方便地读取和处理大量的申请人信息数据,例如姓名、年龄、性别、收入、负债情况等。同时,Python还提供了强大的数据清洗和特征工程的功能,可以帮助我们对数据进行清洗和加工,以便于后续的建模析工作。 其次,Python在建模方面也有很多优势。例如,通过scikit-learn这个机器学习库,我们可以方便地进行特征选择、建模和评估。通过使用逻辑回归、决策树等算法,我们可以构建评分模型,并通过交叉验证等方法对模型进行评估和优化。此外,Python还提供了其他一些用于建模的库,如XGBoost和LightGBM,它们在梯度提升树方面表现出色,可以提高模型的预测能力。 最后,Python还有一些可视化库,如matplotlib和seaborn,它们可以帮助我们对评分的结果进行可视化展示。通过绘制相关的图表和图像,我们可以更直观地理解和解释评分的作用和效果。 总的来说,Python评分的探索中具有丰富的数据处理、建模和可视化功能,可以帮助我们更高效地进行评分的研究和应用。通过Python,我们可以更好地理解申请人的信用风险,并为银行等机构出更准确、科学的决策。 ### 回答2: 评分是一种广泛应用于信用评估、风险评估等领域的工具,用于对个体特征进行评分类。Python是一种强大的编程语言,具有丰富的数据处理和析库,非常适合用于评分的制作和探索。 在Python中,可以使用numpy和pandas等库进行数据处理和清洗,以准备评分建模所需的数据集。可以使用pandas读取和处理原始数据,使用numpy进行数据转换和预处理,例如缺失值处理、数据标准化等,以便后续建模析。 接下来,可以使用scikit-learn等机器学习库来构建评分模型。可以使用逻辑回归、决策树、随机森林等算法进行模型训练和调优。使用交叉验证等技术,评估模型的稳定性和性能表现。 在建模过程中,可以使用matplotlib和seaborn等可视化库对数据特征进行探索和可视化析。可以绘制柱状图、箱线图、散点图等,以便更好地理解数据特征之间的关系和布情况。 此外,Python还提供了丰富的统计析工具,例如statsmodels和scipy等库,可以用于对模型结果进行统计学析和检验,以确保模型的有效性和可解释性。 综上所述,Python评分探索中具有广泛的应用价值。通过使用Python的数据处理、机器学习和可视化库,可以对评分的建模过程进行全面的探索和析,从而提高模型的准确性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值