模型训练通过pandas一次分组计算多个标签的KS

模型训练通过pandas一次分组计算多个标签的KS

  1. 模型训练通常是一堆特征X和一个标签y,但是模型经常需要再多个不同的y标签上去看效果,进行综合评估,如信贷领域中需要看在不同的mob的表现
  2. 下面会使用pandas计算多个y标签效果

准备数据

  1. 生成模拟数据

    python 复制代码
    import numpy as np
    import pandas as pd
    from sklearn import metrics
    
    np.random.seed(2025)
    def gen_data(labels=['y1', 'y2', 'y3'], nums=1000):
        data = dict(
            # 模拟分组月份
            month=np.random.choice(range(1, 6), nums),
            pred=np.random.rand(nums)
        )
        df = pd.DataFrame(data)
        for y in labels:
            df[y] = np.random.choice([0, 1, np.nan], nums)
        return df
    df=gen_data(nums=10)
    print(df)
    # 输出如下
    # 
    #    month      pred   y1   y2   y3
    # 0      3  0.964238  1.0  1.0  1.0
    # 1      5  0.800984  1.0  NaN  NaN
    # 2      1  0.455205  NaN  0.0  0.0
    # 3      4  0.801058  1.0  0.0  0.0
    # 4      4  0.041718  NaN  1.0  NaN
    # 5      5  0.769458  0.0  1.0  0.0
    # 6      1  0.003171  0.0  1.0  1.0
    # 7      1  0.292809  NaN  0.0  0.0
    # 8      3  0.610914  NaN  0.0  NaN
    # 9      2  0.913027  1.0  1.0  0.0
  2. 创建计算单列ks的函数,注意处理模型打分或者标签为空的情况

    python 复制代码
    # 计算单个标签的ks
    def calc_one_ks(y_true, y_pred):
        # 去掉标签为空的
        y_true = y_true.reset_index(drop=True)
        idx = y_true[y_true.notna()].index
        # 去掉打分为空的
        y_pred = pd.Series(y_pred).reset_index(drop=True)
        idx2 = y_pred[y_pred.notna()].index
        # 取模型打分和标签都不为空的
        idx_uni = list(set(idx) & set(idx2))
        y_true = y_true[idx_uni]
        y_pred = y_pred[idx_uni]
        if len(y_true) == 0:
            return None
        tpr, fpr, _ = metrics.roc_curve(y_true, y_pred)
        ks = max(abs(tpr - fpr))
        return ks
  3. 使用pandas的groupBy和apply分组计算多个标签的ks值,同时输出主标签的数量和正样本率

    python 复制代码
        def get_ks(s: pd.Series, labels, y_main=None, score='pred'):
            cnt = len(s)
            y_main = y_main or labels[0]
            pos_cnt = s[y_main].sum()
            pos_pct = round(pos_cnt / cnt, 4)
            res = dict(cnt=cnt, pos_cnt=pos_cnt, pos_pct=pos_pct)
            for y in labels:
                res[f'{y}_ks'] = calc_one_ks(s[y], s[score])
            return pd.Series(res)
        

完整代码

python 复制代码
import numpy as np
import pandas as pd
from sklearn import metrics

np.random.seed(2025)


def gen_data(labels=['y1', 'y2', 'y3'], nums=1000):
    data = dict(
        # 模拟分组月份
        month=np.random.choice(range(1, 6), nums),
        pred=np.random.rand(nums)
    )
    df = pd.DataFrame(data)
    for y in labels:
        df[y] = np.random.choice([0, 1, np.nan], nums)
    return df


# 计算单个标签的ks
def calc_one_ks(y_true, y_pred):
    # 去掉标签为空的
    y_true = y_true.reset_index(drop=True)
    idx = y_true[y_true.notna()].index
    # 去掉打分为空的
    y_pred = pd.Series(y_pred).reset_index(drop=True)
    idx2 = y_pred[y_pred.notna()].index
    # 取模型打分和标签都不为空的
    idx_uni = list(set(idx) & set(idx2))
    y_true = y_true[idx_uni]
    y_pred = y_pred[idx_uni]
    if len(y_true) == 0:
        return None
    tpr, fpr, _ = metrics.roc_curve(y_true, y_pred)
    ks = max(abs(tpr - fpr))
    return ks


def get_ks(s: pd.Series, labels, y_main=None, score='pred'):
    cnt = len(s)
    y_main = y_main or labels[0]
    pos_cnt = s[y_main].sum()
    pos_pct = round(pos_cnt / cnt, 4)
    res = dict(cnt=cnt, pos_cnt=pos_cnt, pos_pct=pos_pct)
    for y in labels:
        res[f'{y}_ks'] = calc_one_ks(s[y], s[score])
    return pd.Series(res)


if __name__ == '__main__':
    labels = ['y1', 'y2', 'y3']
    df = gen_data(labels)
    g_cols = ['month']
    df_res = df.groupby(g_cols).apply(lambda s: get_ks(s, labels))
    print(df_res)
# 输出
#          cnt  pos_cnt  pos_pct     y1_ks     y2_ks     y3_ks
# month                                                       
# 1      212.0     55.0   0.2594  0.206674  0.270109  0.101449
# 2      183.0     66.0   0.3607  0.138675  0.088418  0.135717
# 3      188.0     63.0   0.3351  0.139406  0.120635  0.125490
# 4      196.0     63.0   0.3214  0.104762  0.192693  0.148323
# 5      221.0     80.0   0.3620  0.120833  0.092691  0.073430

注意,使用pandas直接计算的方式仅适用于样本量较小的情况下,如果样本量较大可以使用sql或者spark进行计算,后续会再进行更新

相关推荐
张欣-男14 分钟前
5分钟理解线性代数v2
人工智能·线性代数·机器学习
是翎1 小时前
Vibe Coding零基础入门:六步工作流拆解
大数据·人工智能·学习·机器学习·数据挖掘
恒知学术1 小时前
锂离子电池 SOC 估计英文论文怎么找?卡尔曼滤波与数据驱动方法检索
机器学习·参考文献·谷歌学术·sci论文·免费论文检索工具·英文文献
桃西西呀2 小时前
9月1日起AI图要被标记了,机器怎么一眼认出哪张是 AI 画的?
人工智能·机器学习·llm
Zzj_tju2 小时前
Prompt Injection 防御:隔离不可信上下文的最小复现
人工智能·深度学习·机器学习·自然语言处理·prompt
陈年老古董2 小时前
矿物分类实战:从传统机器学习到深度学习(含PyTorch实现)
笔记·深度学习·学习·机器学习·分类
词却2 小时前
机器学习入门:手写数字识别与算法对比
人工智能·算法·机器学习
小白说大模型3 小时前
Hermes 全配置指南:从裸版到 AI Agent 天花板
大数据·人工智能·学习·算法·机器学习·数据挖掘
在所不辞兄3 小时前
【神经网络干货】可穿戴电子皮肤正在从“单点传感”走向“多通道、多模态、连续监测”
人工智能·神经网络·机器学习·数据挖掘
来让爷抱一个15 小时前
2026 智能体安全实战:把护栏写进SPEC,MonkeyCode 云端跑通
人工智能·安全·机器学习