当前位置: 首页 >  技术分享 >  使用PySpark计算AUC,KS与PSI

使用PySpark计算AUC,KS与PSI

导读:当特征数量或者模型数量很多的时候,使用PySpark去计算相关风控指标会节省很多的时间。网上关于使用PySpark计算相关风控指标的资料较少,尤其是PSI计算不管是国内还是国外相关的代码都没有正确的,这里抛砖引玉,写了三个风控常用的指标AUC,KS和PSI相关的计算方法,供参考。

当特征数量或者模型数量很多的时候,使用PySpark去计算相关风控指标会节省很多的时间。网上关于使用PySpark计算相关风控指标的资料较少,尤其是PSI计算不管是国内还是国外相关的代码都没有正确的,这里抛砖引玉,写了三个风控常用的指标AUC,KS和PSI相关的计算方法,供参考。

AUC

AUC的相关概念网上已经有很多的很好的文章,这里不在赘述,AUC使用的到的计算公式如下:

\[AUC=\frac{\sum_{i\in positiveClass}rank_i-{\displaystyle\frac{M(1+M)}2}}{M\times N} \]

其中M为负类样本的数目,N为正类样本的数目

使用PySpark计算代码如下:

from pyspark.sql import functions as F
from pyspark.sql.window import Window

true_y_col = 'y'
pred_y_col = 'pred_y'
date_col = 'day'


auc_df = df.filter(F.col(true_y_col)>=0).filter(F.col(pred_y_col)>=0)\
           .select(true_y_col, pred_y_col, date_col, 'model_name')\
           .withColumn('totalbad', F.sum(F.col(true_y_col)).over(Window.patitonBy(date_col, 'model_name').orderBy(F.lit(1))))\
           .withColumn('totalgood', F.sum(1-F.col(true_y_col)).over(Window.patitonBy(date_col, 'model_name').orderBy(F.lit(1))))\
           .withColumn('rnk2', F.row_number().over(Window.partitionBy(date_col, 'model_name').orderBy(F.col(pred_y_col).asc())))\
           .filter(F.col(true_y_col)==1)\
           .groupBy(date_col, 'model_name')\
           .agg(((F.sum(F.col('rnk2'))-0.5*(F.max(F.col('totalbad')))*(1+F.max(F.col('totalbad'))))/(F.max(F.col('totalbad'))*F.max(F.col('totalgood')))).alias('AUC'))\
           .orderBy('model_name', date_col)

KS

KS统计量是基于经验累积分布函数(Empirical Cumulative Distribution Function,ECDF)
建立的,一般定义为:

\[KS=\max\left\{\left|cum\left(bad\_rate\right)-cum\left(good\_rate\right)\right|\right\} \]

即为TPRFPR差值绝对值的最大值。

\[KS=max\left(\left|TPR-FPR\right|\right) \]

KS计算方法有很多种,这里使用的是分箱法分别计算TPRFPR,然后得到KS。
使用PySpark计算代码如下:

from pyspark.sql import functions as F
from pyspark.sql.window import Window

true_y_col = 'y'
pred_y_col = 'pred_y'
date_col = 'day'
nBins = 10

ks_df = df.filter(F.col(true_y_col)>=0).filter(F.col(pred_y_col)>=0)\
          .select(true_y_col, pred_y_col, date_col, 'model_name')\
          .withColumn('Bin', F.ntile(nBins).over(Window.partitionBy(date_col, 'model_name').orderBy(pred_y_col)))\
          .groupBy(date_col, 'model_name', 'Bin').agg(F.sum(true_y_col).alias('N_1'), F.sum(1-F.col(true_y_col)).alias('N-0'))\
          .withColumn('ALL_1', F.sum('N_1').over(Window.partitionBy(date_col, 'model_name')))\
          .withColumn('ALL_0', F.sum('N_0').over(Window.partitionBy(date_col, 'model_name')))\
          .withColumn('SUM_1', F.sum('N_1').over(Window.partitionBy(date_col, 'model_name').orderBy('Bin')))\
          .withColumn('ALL_0', F.sum('N_0').over(Window.partitionBy(date_col, 'model_name').orderBy('Bin')))\
          .withColumn('KSn', F.expr('round(abs(SUM_1/ALL_1-SUM_0/ALL_0),6)'))\
          .withColumn('KS', F.round(F.max('KSn').over(Window.partitionBy(date_col, 'model_name')),6))

ks_df = ks_df.select(date_col, 'model_name', 'KS').filter(col('KS').isNotNull()).dropDuplicates()

PSI

群体稳定性指标(Population Stability Index,PSI)是风控场景常用的验证样本 在各分数段的分布与建模样本 分布的稳定性。在建模中,常用来筛选特征变量、评估模型稳定性

计算公式如下:

\[psi=\sum_{i=1}^n\left(A_i-E_i\right)\ast\ln\left(A_i/E_i\right) \]

其中\(A_i\)代表的是第i个分箱中实际分布(actual)样本占比 ,同理\(E_i\)代表的是第i个分箱中预期分布(excepted)样本占比

使用PySpark计算代码如下:

from pyspark.sql import functions as F
from pyspark.sql.window import Window
from pyspark.sql.functions import when


date_col = 'day'
nBins = 10
feature_list = ['fea_1', 'fea_2', 'fea_3']

df = df.withColumn('flag', when(F.col(date_col) == 'actual_date', 0).when(F.col(date_col) == 'excepted_date', 1).otherwise(None)

quantitles = df.filter(F.col('flag') == 0)\
               .approxQuantile(feature_list, [i/nBins for i in range(1, nBins)], 0.001) # 基准样本分箱

quantitles_dict = {col: quantitles[idx] for idx, col in enumerate(feature_list)}
f_quantitles_dict = F.create_map([F.lit(x) if isinstance(x, str) else F.array(*[F.lit(xx) for xx in x]) for i in quantitles_dict.items() for x in i])

unpivotExpr = "stack(3, 'fea_1', fea_1, 'fea_2', fea_2, 'fea_3', fea_3)"

psi_df = df.filter(F.col('flag').isNotNull()).select('flag', F.expr(unpivotExpr))\
           .withColumn('Bin', when(F.col('value').isNull(), 'Missing').otherwise(
            when(F.col('value') < f_quantitles_dict[F.col('varname')][0], 'bin_0')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][1], 'bin_1')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][2], 'bin_2')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][3], 'bin_3')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][4], 'bin_4')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][5], 'bin_5')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][6], 'bin_6')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][7], 'bin_7')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][8], 'bin_8')
            .when(F.col('value') < f_quantitles_dict[F.col('varname')][8], 'bin_9')))\
           .groupBy('varname', 'Bin').agg(F.sum('flag').alias('N_1'), F.sum(1-F.col('flag')).alias('N_0'))\
           .withColumn('ALL_1', F.sum('N_1').over(Window.partitionBy('varname')))\
           .withColumn('ALL_0', F.sum('N_0').over(Window.partitionBy('varname')))\
           .withColumn('actual', F.expr('round(N_0/ALL_0, 6)'))\
           .withColumn('excepted', F.expr('round(N_1/ALL_1, 6)'))\
           .withColumn('PSIn', F.expr('round((actual-excepted)*ln(actual/excepted), 6'))\
           .withColumn('PSI', F.round(F.sum('PSIn').over(Window.partitionBy('varname')), 6))

Reference

  • 【风控算法】二、SQL->Python->PySpark计算KS,AUC及PSI
  • 风控模型—区分度评估指标(KS)深入理解应用
  • 风控模型—群体稳定性指标(PSI)深入理解应用
内容
  • 计算机网络 基础面试第一弹
    计算机网络 基础面试第一弹
    2023-12-04
    1. DNS解析过程.DNS解析(Domain Name System Resolution)是将域名转换为IP地址的过
  • UE构建基础和实践:一、概述
    UE构建基础和实践:一、概述
    2023-12-04
    序言.构建这个概念是和应用程序包及资源等相关的,它的特点是使用脚本控制一系列的任务的处理,以减少人力操作带来的成本和操作
  • 五年经验的前端社招被问:CPU 和 GPU 到底有啥区别?
    五年经验的前端社招被问:CPU
    2023-12-01
    首先来看 CPU 和 GPU 的百科解释:.CPU(Central ProcessingUnit,中央处理器) :功能主
  • CPU 和 CPU Core 有啥区别?多核 CPU?多个 CPU?
    CPU 和 CPU Core 有
    2023-12-01
    CPU 全称 Central Processing Unit,中央处理器,计算机的大脑,长这个样子:.CPU 通过一个插
  • 时尚个性针织毛衣
    时尚个性针织毛衣
    2023-12-11
    时尚个性针织毛衣.时尚个性针织毛衣一直是秋冬季节的必备单品,不仅可以很好地保暖,还能展现出个性与时尚。无论是女性还是男性
  • 休闲简约短袖衬衫
    休闲简约短袖衬衫
    2023-12-21
    休闲简约短袖衬衫.现代人生活节奏快,休闲简约的穿着成为时尚潮流。短袖衬衫作为经典的休闲单品,一直备受时尚人士的青睐。它舒
  • 经典款皮鞋
    经典款皮鞋
    2023-12-06
    经典款皮鞋.经典款皮鞋一直是时尚界的永恒之选,不论是商务场合、休闲聚会还是正式场合,都能展现出绅士淑女的气质和优雅。今天
  • 修身弹力牛仔裤
    修身弹力牛仔裤
    2023-12-26
    修身弹力牛仔裤:展现你的魅力.一、时尚的必备单品.修身弹力牛仔裤一直都是时尚界的必备单品,它不仅可以展现出个人的魅力,还
  • 可爱儿童内衣套装,优质棉质,柔软透气,呵护宝宝肌肤
    可爱儿童内衣套装,优质棉质,柔软
    2024-01-05
    可爱儿童内衣套装,优质棉质,柔软透气,呵护宝宝肌肤.宝宝的皮肤是非常娇嫩的,所以选择合适的内衣套装对于宝宝的健康和舒适至
  • 优雅复古半身裙,散发优雅复古气息
    优雅复古半身裙,散发优雅复古气息
    2024-01-15
    优雅复古半身裙,散发优雅复古气息.复古是一种永不过时的时尚趋势,它总能让人们联想到过去的美好时光。而半身裙则是女性衣橱里
  • 时尚修身连衣裙,展现优雅女性魅力
    时尚修身连衣裙,展现优雅女性魅力
    2023-12-06
    时尚修身连衣裙,展现优雅女性魅力.时尚修身连衣裙一直是女性衣橱里的必备单品,不仅款式多样,而且能够展现出女性的优雅魅力。
  • 潮流风衣大衣,彰显都市时尚风采
    潮流风衣大衣,彰显都市时尚风采
    2023-12-16
    潮流风衣大衣,彰显都市时尚风采.潮流风衣大衣一直是时尚界备受追捧的单品之一。它既能为我们遮风挡雨,又能为我们穿出时尚感,
  • 暖心家居服套装,柔软舒适,可爱**形象,让宝宝安心入睡
    暖心家居服套装,柔软舒适,可爱*
    2023-12-16
    暖心家居服套装,让宝宝安心入睡.宝宝的睡眠质量对成长发育至关重要,而穿着舒适的家居服对宝宝的睡眠质量有着直接的影响。为了
  • 时尚儿童牛仔裤,经典款式,耐穿耐磨,让宝宝更有个性
    时尚儿童牛仔裤,经典款式,耐穿耐
    2024-01-10
    时尚儿童牛仔裤引领潮流.时尚儿童牛仔裤一直是儿童服装中的经典款式,不仅经典耐穿,而且可以展现宝宝的个性。随着时尚的发展,