• 计算通过率逾期率并绘制通过率逾期率曲线


    在信贷领域AUC&KS指标能提现模型的学习效果,但是在评估模型的相对好坏,以及制定使用方案的时候还是需要通过率&逾期率曲线进行评估模型的。

    横坐标为累计通过率,纵坐标为累计逾期率,此时比较相同的通过率情况下,逾期率越高,曲线位置就越靠近上方。

    1、可以理解曲线下的面积越小越好,跟AUC曲线是相反的逻辑。

    2、要看细节部分,在某个局部区间通过率下是否局部更好,是否可以交叉使用

    3、是否整个曲线都是缠绕的,如果是的话,考虑选择AUC评估指标吧。

    本文代码分为两个部分

    首先计算通过率&逾期率指标,然后绘制通过率逾期率曲线。

    1. # 简化版的通过率逾期率
    2. def get_pass_and_overdue_with_list(pred_result_and_real_label: List[Tuple[float, float]]) -> Tuple[List, List]:
    3. """
    4. 输入预测概率和实际label,输出通过率和逾期率序列
    5. pred_result_and_real_label = data[['ruleset_111_mexico_loan_xgboost_score','first1_overdue3']].values.tolist()
    6. :param pred_result_and_real_label: 包含预测概率和实际label的元组组成的列表,注意元组的第一个元素为预测概率
    7. :return: 通过率和逾期率序列
    8. """
    9. # 按照元组中的第一个元素,也就是预测值进行升序排列,可变对象已更改
    10. pred_result_and_real_label.sort(key=lambda x: x[0])
    11. # 获取序列的长度
    12. the_length = len(pred_result_and_real_label)
    13. print(f"the_length {the_length} ")
    14. # pass_rate
    15. pass_rate = []
    16. # overdue_rate
    17. overdue_rate = []
    18. # 遍历过程中每个节点的总逾期数目
    19. overdue_sum = 0.0
    20. for i in range(the_length):
    21. # 实时计算通过率并保存
    22. pass_rate.append((i + 1.0) / the_length)
    23. # 实时计算总逾期数目
    24. overdue_sum += pred_result_and_real_label[i][1]
    25. # 使用实时计算的总逾期数目计算当前在总样本上的逾期率并保存
    26. overdue_rate.append(overdue_sum / (i + 1.0))
    27. return pass_rate, overdue_rate
    28. def get_auc_ks(data, prob_col='prob', label_col='overdue'):
    29. """
    30. :param data:
    31. :param prob_col:
    32. :param label_col:
    33. :return:
    34. """
    35. from sklearn.metrics import roc_curve, auc
    36. fpr, tpr, thresholds = roc_curve(data[label_col], data[prob_col])
    37. auc_value = auc(fpr, tpr)
    38. ks = max(tpr - fpr)
    39. return auc_value, ks
    40. def get_pass_and_overdue_by_dataframe(data, prob_col='prob', label_col='overdue', float_round=5):
    41. """
    42. :param data:
    43. :param prob_col:
    44. :param label_col:
    45. :param float_round:
    46. :return:
    47. """
    48. from tqdm import tqdm
    49. data[prob_col] = data[prob_col].apply(lambda x: float(str(x)[0:float_round]))
    50. data.sort_values(by=prob_col, ascending=True, inplace=True)
    51. data['flag'] = 1
    52. data_agg = data.groupby(prob_col).agg({'flag': 'sum', label_col: 'sum'})
    53. data_agg.reset_index(drop=False, inplace=True)
    54. probs = list(set(data_agg[prob_col]))
    55. probs.sort()
    56. ttl_cnt = data.shape[0]
    57. pass_rats = []
    58. overdue_rats = []
    59. for prob_ in tqdm(probs):
    60. pass_rat = data_agg[data_agg[prob_col] <= prob_]['flag'].sum() / ttl_cnt
    61. pass_rats.append(pass_rat)
    62. cum_overdue = data_agg[data_agg[prob_col] <= prob_][label_col].sum()
    63. overdue_rat = cum_overdue / data[data[prob_col] <= prob_].shape[0]
    64. overdue_rats.append(overdue_rat)
    65. return pass_rats, overdue_rats
    66. def get_auc_ks_pass_overdue_rat(data, prob_col='prob', label_col='overdue', float_round=5, plot=False):
    67. """
    68. :param data:
    69. :param prob_col:
    70. :param label_col:
    71. :param float_round:
    72. :param plot:
    73. :return:
    74. """
    75. print(f'get_auc_ks_pass_overdue_rat prob_col:{prob_col} label_col:{label_col}')
    76. data = data[[prob_col, label_col]].copy()
    77. num = data.shape[0]
    78. auc_value, ks = get_auc_ks(data, prob_col=prob_col, label_col=label_col)
    79. pass_rats, overdue_rats = get_pass_and_overdue_by_dataframe(data, prob_col=prob_col, label_col=label_col,
    80. float_round=float_round)
    81. if plot == True:
    82. plt.figure(figsize=(6, 6))
    83. # 开始画图 modes_metrics oot_dstx_num,oot_zcfl_num
    84. plt.title(f'{label_col}|num:{num}')
    85. plt.plot(pass_rats, overdue_rats, color='green', label=f'auc:{auc_value:.4f}_ks:{ks:.4f}')
    86. plt.legend() # 显示图例
    87. plt.xlabel('pass_rat')
    88. plt.ylabel('cum_overdue')
    89. plt.grid()
    90. plt.show()
    91. return auc_value, ks, pass_rats, overdue_rats, num
    92. def get_auc_ks_pass_overdue_rat_with_data_type(train_data
    93. , data_type='data_type'
    94. , dstx='dstx'
    95. , zcfl='zcfl'
    96. , prob_col='prob'
    97. , label_col='overdue'
    98. , float_round=5
    99. , plot=False):
    100. """
    101. 计算正常分流&大赦天下的模型指标
    102. :param train_data: dataframe
    103. :param data_type: dstx&zcfl
    104. :param dstx: dstx的标记
    105. :param zcfl: zcfl的标记
    106. :param prob_col: 预测概率值列
    107. :param label_col: 标签列
    108. :param float_round: 保留精度
    109. :param plot: 是否绘制通过预期曲线
    110. :return:
    111. """
    112. data = train_data[train_data[data_type] == dstx][[prob_col, label_col]].copy()
    113. oot_dstx_auc_value, oot_dstx_ks, oot_dstx_pass_rats, oot_dstx_overdue_rats, oot_dstx_num = get_auc_ks_pass_overdue_rat(
    114. data\
    115. , prob_col=prob_col\
    116. , label_col=label_col\
    117. , float_round=5\
    118. , plot=False)
    119. data = train_data[train_data[data_type] == zcfl][[prob_col, label_col]].copy()
    120. oot_zcfl_auc_value, oot_zcfl_ks, oot_zcfl_pass_rats, oot_zcfl_overdue_rats, oot_zcfl_num = get_auc_ks_pass_overdue_rat(
    121. data \
    122. , prob_col=prob_col \
    123. , label_col=label_col \
    124. , float_round=5 \
    125. , plot=False)
    126. if plot:
    127. fig, axs = plt.subplots(2, 1)
    128. plt.rcParams['figure.figsize'] = (9, 12.0)
    129. axs[0].plot(oot_dstx_pass_rats, oot_dstx_overdue_rats)
    130. axs[0].set_ylim(0, 0.5)
    131. axs[0].set_xlim(0, 1)
    132. axs[0].set_title(f'oot dstx nocv fpd3|num:{oot_dstx_num}')
    133. axs[0].set_xlabel('pass_rat')
    134. axs[0].set_ylabel('cum_overdue') # ,fontproperties = font
    135. axs[0].grid(True)
    136. axs[0].legend(["auc:%.4f ks:%.4f" % (oot_dstx_auc_value, oot_dstx_ks)], loc="lower left")
    137. axs[1].plot(oot_zcfl_pass_rats, oot_zcfl_overdue_rats)
    138. axs[1].set_ylim(0, 0.5)
    139. axs[1].set_xlim(0, 1)
    140. axs[1].set_title(f'oot zcfl nocv fpd3|num:{oot_zcfl_num}')
    141. axs[1].set_xlabel('pass_rat')
    142. axs[1].set_ylabel('cum_overdue') # ,fontproperties = font
    143. axs[1].grid(True)
    144. axs[1].legend(["auc:%.4f ks:%.4f" % (oot_zcfl_auc_value, oot_zcfl_ks)], loc="lower left")
    145. return oot_dstx_auc_value, oot_dstx_ks, oot_dstx_pass_rats, oot_dstx_overdue_rats, oot_dstx_num, \
    146. oot_zcfl_auc_value, oot_zcfl_ks, oot_zcfl_pass_rats, oot_zcfl_overdue_rats, oot_zcfl_num

    绘制通过率逾期率曲线

    def plot_dstx_zcfl_pass_overdue(modes_metrics, label='fpd3', line_desc=None,
                                    colors=['skyblue', 'green', 'blue', 'y', 'r']):
        """
        一张图绘制两个子图,分别绘制zcfl & dstx的通过率逾期率
        :param modes_metrics: each row contains oot_dstx_auc_value, oot_dstx_ks, oot_dstx_pass_rats,  oot_dstx_overdue_rats,
         oot_dstx_num, oot_zcfl_auc_value, oot_zcfl_ks, oot_zcfl_pass_rats, oot_zcfl_overdue_rats, oot_zcfl_num
        :param label: overdue label
        :param line_desc: 图例描述
        :param colors: 图例颜色
        :return: None
        """
        import matplotlib.font_manager as fm
        font_size=12
        # 设置family、size 
        font = fm.FontProperties(fname='/data/simhei.ttf', size=font_size)
        plt.rcParams['figure.figsize'] = (9, 12.0)
        fig, axs = plt.subplots(2, 1)
        if line_desc is None:
            line_desc = ['']*len(modes_metrics)
            
        for i in range(len(modes_metrics)):
            oot_dstx_auc_value, oot_dstx_ks, oot_dstx_pass_rats, oot_dstx_overdue_rats, oot_dstx_num = modes_metrics[i][0:5]
            axs[0].plot(oot_dstx_pass_rats, oot_dstx_overdue_rats, color=colors[i],
                        label=f'auc:{oot_dstx_auc_value:.4f}|ks:{oot_dstx_ks:.4f}_{line_desc[i]}' )
        axs[0].set_ylim(0, 0.4)
        axs[0].set_xlim(0, 1)
        axs[0].set_title(f'oot dstx nocv {label}|num:{oot_dstx_num}')
        axs[0].set_xlabel('pass_rat')
        axs[0].set_ylabel('cum_overdue', fontproperties=font)
        axs[0].legend(loc="lower right",prop=font)  # 显示图例
        axs[0].grid(True)

        for i in range(len(modes_metrics)):
            oot_dstx_auc_value, oot_dstx_ks, oot_dstx_pass_rats, oot_dstx_overdue_rats, oot_dstx_num = modes_metrics[i][5:]
            axs[1].plot(oot_dstx_pass_rats, oot_dstx_overdue_rats, color=colors[i],
                        label=f'auc:{oot_dstx_auc_value:.4f}|ks:{oot_dstx_ks:.4f}_{line_desc[i]}' )
        axs[1].set_ylim(0, 0.4)
        axs[1].set_xlim(0, 1)
        axs[1].set_title(f'oot zcfl nocv {label}|num:{oot_dstx_num}')
        axs[1].set_xlabel('pass_rat')
        axs[1].set_ylabel('cum_overdue' , fontproperties = font)
        axs[1].legend(loc="lower right",prop=font)  # 显示图例
        axs[1].grid(True)
     

     绘图结果如上图

  • 相关阅读:
    Neo4j数据库(二)
    Halcon Variable Inspect 安装失败
    springboot整合redis
    2核4G游戏服务器推荐(阿里云/腾讯云/华为云)
    house of storm+堆SROP+orw
    堆叠注入 [GYCTF2020]Blacklist1
    4.2冰达机器人:视觉实例-机器人视觉循线、视觉实例-调整循线颜色
    估算总体标准差的极差均值估计法sigma = R/d2
    简易基本MyBatis语句书写模板-续更中
    windows和linux中Nginx常用命令
  • 原文地址:https://blog.csdn.net/mtj66/article/details/127686302