toad

本文主要记录一下toad有关的学习资料,以及操作过程

先把连接放上:

github主页: https://github.com/amphibian-dev/toad

文档:https://toad.readthedocs.io

演示:https://toad.readthedocs.io/en/latest/tutorial.html

whl 下载地址: https://pypi.org/simple/toad/

报错地址:https://github.com/amphibian-dev/toad/issues

中文教程:https://toad.readthedocs.io/en/dev/tutorial_chinese.html

0.0.60更新内容:https://github.com/amphibian-dev/toad/blob/master/CHANGELOG.md

链接: https://pan.baidu.com/s/1rIaQm0XunlBU_5YXt6CXBw 提取码: fma6

操作过程

# -*- coding: utf-8 -*-
"""
Created on Thu Aug  5 10:43:34 2021

@author: L
"""
#%%导入模块
import pandas as pd
import numpy as np
import toad


#%%导入数据
data = pd.read_csv('D:/xxx/toad/train.csv')
print('Shape:',data.shape)
data.head(10)


#%%数据包含2019年5月 - 7月的数据。其中我们将用3月和4月数据用于训练样本,5月、6月、7月数据作为时间外样本(OOT)
train = data.loc[data.month.isin(['2019-03','2019-04'])==True,:]
OOT = data.loc[data.month.isin(['2019-03','2019-04'])==False,:]

print('train size:',train.shape,'
OOT size:',OOT.shape)
'''
train size: (43576, 167) 
OOT size: (65364, 167)
'''



#%%EDA

#%%1.描述性统计,可展示数值型也可以展示类别型,类别型的分位数使用频数替代
toad.detect(train)

#展示的字段有
toad.detect(train).columns
'''
['type', 'size', 'missing', 'unique', 'mean_or_top1', 'std_or_top2',
       'min_or_top3', '1%_or_top4', '10%_or_top5', '50%_or_bottom5',
       '75%_or_bottom4', '90%_or_bottom3', '99%_or_bottom2', 'max_or_bottom1']
'''

#%%2.计算变量iV , toad.quality

'''
toad.quality(dataframe, target=’target’, iv_only=False)

输出每个变量的iv值,gini,entropy,和unique values,结果以iv值排序。”target”为目标列,”iv_only”决定是否只输出iv值。

注意:
1. 对于数据量大或高维度数据,建议使用iv_only=True 
2. 要去掉主键,日期等高unique values且不用于建模的特征
'''
to_drop = ['APP_ID_C','month'] # 去掉ID列和month列
toad.quality(data.drop(to_drop,axis=1),'target',iv_only=True)[:15]


#%%特征筛选

'''
3. toad.selection.select(dataframe, target=’target’, empty=0.9, iv=0.02, corr=0.7, return_drop=False, exclude=None):
根据缺失值占比,iv值,和高相关性进行变量筛选,赋值为:

(1)empyt=0.9: 若变量的缺失值大于0.9被删除

(2)iv=0.02: 若变量的iv值小于0.02被删除

(3)corr=0.7: 若两个相关性高于0.7时,iv值低的变量被删除

(4)return_drop=False: 若为True,function将返回被删去的变量列

(5)exclude=None: 明确不被删去的列名,输入为list格式

如下面的cell,没有变量由于缺失值高被删除,大量变量因为低iv值被删除,部分相关性高的变量被删除。从165个特征中选出了32个变量。
'''

train_selected, dropped = toad.selection.select(train,target = 'target', empty = 0.5, iv = 0.05, corr = 0.7, return_drop=True, exclude=['APP_ID_C','month'])
print(dropped)
print(train_selected.shape)  #(43576, 34)


#%%分箱

'''
toad的分箱功能支持数值型数据和离散型分箱,默认分箱方法使用 卡方分箱。

toad.transform.Combiner 是用来分箱的class,分箱步骤如下:

(1) c = toad.transform.Combiner()
(2) *训练分箱*: c.fit(dataframe, y = ‘target’, method = ‘chi’, min_samples = None, n_bins = None, empty_separate = False)
    y: 目标列
    method: 分箱方法,支持’chi’ (卡方分箱), ‘dt’ (决策树分箱), ‘kmean’ , ‘quantile’ (等频分箱), ‘step’ (等步长分箱)
    min_samples: 每箱至少包含样本量,可以是数字或者占比
    n_bins: 箱数,若无法分出这么多箱数,则会分出最多的箱数
    empty_separate: 是否将空箱单独分开
(3) *查看分箱节点*:c.export()
(4) *手动调整分箱*: c.load(dict)
(5) *apply分箱结果*: c.transform(dataframe, labels=False):
    labels: 是否将分箱结果转化成箱标签。False时输出0,1,2…(离散变量根据占比高低排序),True输出(-inf, 0], (0,10], (10, inf)。

注意:1. 注意删去不需要分箱的列,特别是ID列和时间列
'''


c = toad.transform.Combiner()

# 使用特征筛选后的数据进行训练:使用稳定的卡方分箱,规定每箱至少有5%数据, 空值将自动被归到最佳箱。
c.fit(train_selected.drop(to_drop, axis=1), y = 'target', method = 'chi', min_samples = 0.05) #empty_separate = False

# 为了演示,仅展示部分分箱
# 注意c.export() 的结果是一个字典
print('var_d2:',c.export()['var_d2'])
print('var_d5:',c.export()['var_d5'])
print('var_d6:',c.export()['var_d6'])


#%%观察分箱
#toad.plot的module提供了一部分的可视化功能,帮助调整分箱节点

'''
1.*时间内观察*: toad.plot.bin_plot(dataframe, x = None, target = ‘target),bar代表了样本量占比,红线代表了正样本占比(e.g. 坏账率)
  x: 需要观察的特征
  target: 目标列

'''
from toad.plot import bin_plot

# 看'var_d2'在时间内的分箱
col = 'var_d2'

bin_plot(c.transform(train_selected[[col,'target']], labels=True), x=col, target='target')


'''
2. *跨时间观察:* toad.plot.badrate_plot(dataframe, target = ‘target’, x = None, by = None)
   输出不同时间段中每箱的正样本占比

- target: 目标列

- x: 时间列, string格式

- by: 需要观察的特征

注意:时间列需要预先分好并设成string,不支持timestampe

'''
from toad.plot import badrate_plot

col = 'var_d2'

# 观察 'var_d2' 分别在时间内和OOT中的稳定性
badrate_plot(c.transform(train[[col,'target','month']], labels=True), target='target', x='month', by=col)
badrate_plot(c.transform(OOT[[col,'target','month']], labels=True), target='target', x='month', by=col)

badrate_plot(c.transform(data[[col,'target','month']], labels=True), target='target', x='month', by=col)
'''
敞口随时间变化而增大为优,代表了变量在更新的时间区分度更强。线之前没有交叉为优,代表分箱稳定。
'''

# 看'var_d5'在时间内的分箱
col = 'var_d5'

#观察单个变量分箱结果时,建议设置'labels = True'
bin_plot(c.transform(train_selected[[col,'target']], labels=True), x=col, target='target')


#%%调整分箱  c.set_rules(dict)

#set_rules 后会更新被修改的箱

# iv值较低,假设我们要 'F' 淡出分出一组来提高iv

#设置分组
rule = {'var_d5':[['O', 'nan'],['F'], ['M']]}

#调整分箱
c.set_rules(rule)

#查看手动分箱稳定性
bin_plot(c.transform(train_selected[['var_d5','target']], labels=True), x='var_d5', target='target')
badrate_plot(c.transform(OOT[['var_d5','target','month']], labels=True), target='target', x='month', by='var_d5')


#%%WOE转化

'''
WOE转化在分箱调整好之后进行,步骤如下:

1. *用调整好的Combiner转化数据:* c.transform(dataframe, labels=False)只会转化被分箱的变量

2. *初始化woe transer:* transer = toad.transform.WOETransformer()

3. *fit_transform:* transer.fit_transform(dataframe, target, exclude = None)

   训练并输出woe转化的数据,用于转化train/时间内数据

   target:目标列数据(非列名)
   exclude: 不需要被WOE转化的列 注意:会转化所有列,包括未被分箱transform的列,通过 ‘exclude’ 删去不要WOE转化的列,特别是target列
4. *根据训练好的transer,转化test/OOT数据:*transer.transform(dataframe)

'''

#根据训练好的transer输出woe转化的数据,用于转化test/OOT数据。
# 初始化
transer = toad.transform.WOETransformer()

# combiner.transform() & transer.fit_transform() 转化训练数据,并去掉target列
train_woe = transer.fit_transform(c.transform(train_selected), train_selected['target'], exclude=to_drop+['target'])
OOT_woe = transer.transform(c.transform(OOT))

print(train_woe.head(3))


#%%逐步回归特征刷选 

toad.selection.stepwise(dataframe, target=’target’, estimator=’ols’, direction=’both’, criterion=’aic’, max_iter=None, return_drop=False, exclude=None): *
'''
逐步回归特征筛选,支持向前,向后和双向(推荐)
- estimator: 用于拟合的模型,支持'ols', 'lr', 'lasso', 'ridge'

- direction: 逐步回归的方向,支持'forward', 'backward', 'both' (推荐)

- criterion: 评判标准,支持'aic', 'bic', 'ks', 'auc'

- max_iter: 最大循环次数

- return_drop: 是否返回被剔除的列名

- exclude: 不需要被训练的列名,比如ID列和时间列

*tip: 经验证,direction = ‘both’效果最好。
estimator = ‘ols’以及criterion = ‘aic’运行速度快且结果对逻辑回归建模有较好的代表性*
'''
# 将woe转化后的数据做逐步回归
final_data = toad.selection.stepwise(train_woe,target = 'target', estimator='ols', direction = 'both', criterion = 'aic', exclude = to_drop)

# 将选出的变量应用于test/OOT数据
final_OOT = OOT_woe[final_data.columns]

print(final_data.shape) # 逐步回归从31个变量中选出了10个

# 确定建模要用的变量
col = list(final_data.drop(to_drop+['target'],axis=1).columns)


#%%计算PSI  *toad.metrics.PSI(df_train, df_test):*

#输出每列特征的PSI,可以用于检验WOE转化后的特征稳定性

toad.metrics.PSI(final_data[col], final_OOT[col])


#%%常用模型评分标准  toad. metrics. KS, F1, AUC

# 用逻辑回归建模
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(final_data[col], final_data['target'])

# 预测训练和隔月的OOT
pred_train = lr.predict_proba(final_data[col])[:,1]

pred_OOT_may =lr.predict_proba(final_OOT.loc[final_OOT.month == '2019-05',col])[:,1]
pred_OOT_june =lr.predict_proba(final_OOT.loc[final_OOT.month == '2019-06',col])[:,1]
pred_OOT_july =lr.predict_proba(final_OOT.loc[final_OOT.month == '2019-07',col])[:,1]


from toad.metrics import KS, AUC
print('train KS',KS(pred_train, final_data['target']))
print('train AUC',AUC(pred_train, final_data['target']))
print('OOT结果')
print('5月 KS',KS(pred_OOT_may, final_OOT.loc[final_OOT.month == '2019-05','target']))
print('6月 KS',KS(pred_OOT_june, final_OOT.loc[final_OOT.month == '2019-06','target']))
print('7月 KS',KS(pred_OOT_july, final_OOT.loc[final_OOT.month == '2019-07','target']))

'''
train KS 0.37042196076166667
train AUC 0.7506045637676994
OOT结果
5月 KS 0.36857480763524153
6月 KS 0.34764230892681874
7月 KS 0.3786493041720077
'''
#*PSI 同样可以用于验证分数的稳定性*
print(toad.metrics.PSI(pred_train,pred_OOT_may))
print(toad.metrics.PSI(pred_train,pred_OOT_june))
print(toad.metrics.PSI(pred_train,pred_OOT_june))


#%%计算ks 

'''
*toad.metrics.KS_bucket(predicted_proba, y_true, bucket=10, method = ‘quantile’):*
KS bucket输出模型预测分箱后评判信息,包括每组的分数区间,样本量,坏账率,KS等
   - bucket:分箱的数量

   - method:分箱方法,建议用'quantile'(等人数),或'step' (等分数步长)

bad\_rate为每组坏账率:
(1)组之间的坏账率差距越大越好
(2)可以用于观察是否有跳点
(3)可以用与找最佳切点
(4)可以对比
'''
# 将预测等频分箱,观测每组的区别
toad.metrics.KS_bucket(pred_train, final_data['target'], bucket=10, method = 'quantile')


#%%转为评分卡

'''
toad.ScoreCard( combiner = {}, transer = None, pdo = 60, rate = 2, base_odds = 20, base_score = 750, card = None, C=0.1,kwargs):
逻辑回归模型转标准评分卡,支持传入逻辑回归参数,进行调参。

- combiner: 传入训练好的 toad.Combiner 对象

- transer: 传入先前训练的 toad.WOETransformer 对象

- pdo、rate、base_odds、base_score:
  e.g. pdo=60, rate=2, base_odds=20,base_score=750
      实际意义为当比率为1/20,输出基准评分750,当比率为基准比率2倍时,基准分下降60分

- card: 支持传入专家评分卡

- **kwargs: 支持传入逻辑回归参数(参数详见 sklearn.linear_model.LogisticRegression)
'''

card = toad.ScoreCard(
    combiner = c,
    transer = transer,
    #class_weight = 'balanced',
    #C=0.1,
    #base_score = 600,
    #base_odds = 35 ,
    #pdo = 60,
    #rate = 2
)

card.fit(final_data[col], final_data['target'])

#评分卡在 fit 时使用 WOE 转换后的数据来计算最终的分数,分数一旦计算完成,便无需 WOE 值,可以直接使用 原始数据 进行评分。

# 直接使用原始数据进行评分
card.predict(train)

#输出标准评分卡
card.export()
View Code
原文地址:https://www.cnblogs.com/cgmcoding/p/15117263.html