scorecardpy icon indicating copy to clipboard operation
scorecardpy copied to clipboard

perf.py The calculation of KS

Open sunrisehang opened this issue 5 years ago • 6 comments

当我使用sc.perf_eva(y_train, train_pred , plot_type = ["ks"])计算KS时,KS曲线的定义貌似不是正确的。 正确的KS曲线,应该是以阈值点为x轴,TPR和FPR为y轴,显然源码中计算是错误。这里附上我,参考网上的部分代码,并自己更改后的代码,希望谢老师能参考一下:

def model_score_pro_ks(df_fact_tag, df_expected_score_or_pro, buckets, type_input='score'):
    # 初始等间隔分段区间列表
    breakpoints = np.arange(0, buckets + 1) / (buckets)
    # 将预期得分,按照等距分箱,分成buckets个,返回的是每段的上下界限,array
    # input指的是,初始分箱间隔,组成的列表
    # min和max指的是,df_expected_score_or_pro的最大最小值
    def transform_scale(bin_list, df_expected_score_or_pro_min, df_expected_score_or_pro_max):
        # 将最初间隔变成实际分数间隔
        bin_list /= np.max(bin_list) / (df_expected_score_or_pro_max - df_expected_score_or_pro_min)
        # 加入最小值,即可以实现将分数分隔
        bin_list += df_expected_score_or_pro_min
        return bin_list
    # 针对分数进行等间隔分段
    breakpoints = transform_scale(breakpoints, np.min(df_expected_score_or_pro), np.max(df_expected_score_or_pro))

    # 存在阈值情况下,计算KS值
    def calculate_ecpected_tag(fact_tag, expected_score_pro, bins_point):
        
        # 将真实和预期的组成数据框,便于打标签
        ksdf = pd.DataFrame({'fact_tag': fact_tag, 'expected_score_pro': expected_score_pro})
        # 概率小的是不违约,分数大的也是不违约,但这里计算ks时,大的设定为0,还是小的设定为0,不影响。
        # 因为最后的KS取绝对值,但会影响TPR和FRP曲线
        if type_input == 'score':
            ksdf.loc[:,'expected_tag'] = ksdf.apply(lambda x:1 if x['expected_score_pro'] <= bins_point else 0,axis=1)
        elif type_input == 'pro':
            ksdf.loc[:,'expected_tag'] = ksdf.apply(lambda x:0 if x['expected_score_pro'] <= bins_point else 1,axis=1)
        else:
            raise Exception("Incorrect inputs; the value of type should be choosed between 'score' and 'pro'.")

        #计算TPR和FPR 
        #shilian_tag为真实值,1表示失联,0表示未失联
        #expected_tag为预测值,1表示失联(分数低),0表示未失联(分数高)
        TP = sum([1 if a==b==1 else 0 for a,b in zip(ksdf['fact_tag'],ksdf['expected_tag'])])#正例被预测为正例
        FN = sum([1 if a==1 and b==0 else 0 for a,b in zip(ksdf['fact_tag'],ksdf['expected_tag'])])#正例被预测为反例
        TPR = TP/(TP+FN) 
        TN = sum([1 if a==b==0 else 0 for a,b in zip(ksdf['fact_tag'],ksdf['expected_tag'])])#反例被预测为反例
        FP = sum([1 if a==0 and b==1 else 0 for a,b in zip(ksdf['fact_tag'],ksdf['expected_tag'])])#反例被预测为正例
        FPR = FP/(TN+FP)

        KS = TPR - FPR

        return pd.DataFrame({'bins_point':[bins_point],'TPR':[TPR], 'FPR':[FPR],'KS':[abs(KS)]})

    df1 = pd.DataFrame()
    
    for i in breakpoints:
        # 不断组成数据框,直至所有KS计算完成
        df1 = pd.concat([df1,calculate_ecpected_tag(df_fact_tag, df_expected_score_or_pro, i)])
    return df1

sunrisehang avatar Apr 03 '19 06:04 sunrisehang

目前这个版本的计算方式是没问题的,只是一个是good在上,一个bad在上面。

你如果有时间的话可以把R版本的这个翻译成python的。 https://github.com/ShichenXie/scorecard/blob/master/R/perf.R

ShichenXie avatar Apr 03 '19 10:04 ShichenXie

48B5A4133D2241E0972378A7BA7F2C99 哦哦,我们用的KS是图里这个方法计算的。。。那谢老师的KS该怎么解释呢😂

sunrisehang avatar Apr 04 '19 02:04 sunrisehang

请问K-S曲线横坐标究竟应该是什么呀?我在网上发现了有阈值、总体百分比两个版本

lihaicheng7003 avatar Apr 28 '22 02:04 lihaicheng7003

两个版本好像都有用的,得出的开始值应该是一致的,不可以验证一下。如果有问题,可以这边反馈一下。

ShichenXie avatar Apr 28 '22 06:04 ShichenXie

张建行:您的邮件我已收到。

sunrisehang avatar Apr 28 '22 06:04 sunrisehang

张建行:您的邮件我已收到。

sunrisehang avatar Oct 11 '22 09:10 sunrisehang