风控决策引擎 技术架构 风控决策树_数据

本部分内容基于<oil_data_for_tree.xlsx>这份数据,进行决策树构造,决策树的结果用于策略制定

1. 数据预处理

1.1 数据大小 (50609, 19)

1.2 数据的维度


Index(['uid', 'oil_actv_dt', 'create_dt', 'total_oil_cnt', 'pay_amount_total',
       'class_new', 'bad_ind', 'oil_amount', 'discount_amount', 'sale_amount',
       'amount', 'pay_amount', 'coupon_amount', 'payment_coupon_amount',
       'channel_code', 'oil_code', 'scene', 'source_app', 'call_source'],
      dtype='object')


其中,uid是主键,oil_actv_dt为首次贷款的日期,create_dt为每条数据产生的业务时间(我猜的)。

1.3 数据分类

将数据分为三类:直接去重的、需要进行聚合的数值型变量、需要进行计数(去重)的。

org_lst = ['uid','create_dt','oil_actv_dt','class_new','bad_ind']
 agg_lst = ['oil_amount','discount_amount','sale_amount','amount','pay_amount','coupon_amount','payment_coupon_amount']
dstc_lst = ['channel_code','oil_code','scene','source_app','call_source']

后续将基于uid对三类数据的处理结果进行连接,形成用于训练模型的一张宽表。

1.4 不同类别数据进行不同处理

对数据进行加工之前,需要先做两个预处理的操作。

1. 针对create_dt存在部分的缺失, 用oil_actv_dt进行补全。

2. 只保留oil_actv_dt的180天内的数据。

remark:构造变量的时候不能直接对历史所有数据做累加。  否则随着时间推移,变量分布会有很大的变化。

1.4.1 直接去重

去重,每个uid保留一条记录,保留远的一条记录(这个根据业务来确定吧,这里的保留方式我也不确定)。

def time_isna(x,y):
    if str(x) == 'NaT':
        x = y
    else:
        x = x
    return x
df2 = df.sort_values(['uid','create_dt'],ascending = False) 
# 按照uid和create_dt降序排列->感觉这一步没什么意义?
df2['create_dt'] = df2.apply(lambda x: time_isna(x.create_dt,x.oil_actv_dt),axis = 1)
# axis = 1就是对行进行循环,对每一行使用apply里面的函数

# 做补全的时候,也可以考虑下面的方法:
# df2_test = df.sort_values(['uid','create_dt'],ascending = False) 
# df2_test.loc[df2_test.create_dt.isna(), 'create_dt'] = df2_test.loc[df2_test.create_dt.isna(), 'oil_actv_dt']

df2['dtn'] = (df2.oil_actv_dt - df2.create_dt).apply(lambda x :x.days)
df = df2[df2['dtn']<180]
df.head()
# 保留申请贷款前180天内的数据

base = df[org_lst]
base['dtn'] = df['dtn']
base = base.sort_values(['uid','create_dt'],ascending = False)
# 按create_dt进行降序排列
# base = base.drop_duplicates(['uid'],keep = 'first')
base = base.drop_duplicates(['uid'],keep = 'last')
# 私以为,求最大间隔天数的话,应该keep last(因为sort_values用的依据的使create_dt)
base.shape

1.4.2 变量衍生-数值型进行聚合操作

风控决策引擎 技术架构 风控决策树_最小值_02

gn = pd.DataFrame()
for i in agg_lst:
#     计数
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:len(df[i])).reset_index())
    tp.columns = ['uid',i + '_cnt']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
    
#     大于零的个数
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.where(df[i]>0,1,0).sum()).reset_index())
    tp.columns = ['uid',i + '_num']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
        
#     非空元素的和
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nansum(df[i])).reset_index())
    tp.columns = ['uid',i + '_tot']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
        
#     非空元素的平均值
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanmean(df[i])).reset_index())
    tp.columns = ['uid',i + '_avg']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
        
#     非空元素的最大值
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanmax(df[i])).reset_index())
    tp.columns = ['uid',i + '_max']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
        
#     最小值
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanmin(df[i])).reset_index())
    tp.columns = ['uid',i + '_min']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
        
#     方差
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanvar(df[i])).reset_index())
    tp.columns = ['uid',i + '_var']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
    
#     最大值-最小值
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanmax(df[i]) -np.nanmin(df[i]) ).reset_index())
    tp.columns = ['uid',i + '_var']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')
    
#     变异系数
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df:np.nanvar(df[i])/max(np.nanmean(df[i]),1)).reset_index())
    tp.columns = ['uid',i + '_var']
    if gn.empty == True:
        gn = tp
    else:
        gn = pd.merge(gn,tp,on = 'uid',how = 'left')

1.4.3 变量衍生-求distinct数量

gc = pd.DataFrame()
for i in dstc_lst:
    tp = pd.DataFrame(df.groupby('uid').apply(lambda df: len(set(df[i]))).reset_index())
    tp.columns = ['uid',i + '_dstc']
    if gc.empty == True:
        gc = tp
    else:
        gc = pd.merge(gc,tp,on = 'uid',how = 'left')

1.5 将变量组合在一起

fn = pd.merge(base,gn,on= 'uid')
fn = pd.merge(fn,gc,on= 'uid') 
fn.shape

fn = fn.fillna(0)

2. 模型训练

x = fn.drop(['uid','oil_actv_dt','create_dt','bad_ind','class_new'],axis = 1)
# 删去一些列
y = fn.bad_ind.copy()
from sklearn import tree

dtree = tree.DecisionTreeRegressor(max_depth = 2,min_samples_leaf = 500,min_samples_split = 5000)
dtree = dtree.fit(x,y)

使用回归树模型。

3. 输出决策树图像,并作出决策

import pydotplus 
from IPython.display import Image
from sklearn.externals.six import StringIO
import os
os.environ["PATH"] += os.pathsep + 'D:\setup\Graphviz\bin' # 为了找到graphviz
with open( "dt.dot", "w") as f:
    tree.export_graphviz(dtree, out_file=f) # 可视化函数
dot_data = StringIO() # 在内存中读写str
tree.export_graphviz(dtree, out_file=dot_data,  
                         feature_names=x.columns,
                         class_names=['bad_ind'],
                         filled=True, rounded=True,
                         special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())  # getvalue()方法用于获得写入的str
Image(graph.create_png())

风控决策引擎 技术架构 风控决策树_数据_03

 解读:

1. 图中的value可看作每个叶节点中坏样本出现的概率。

value是指叶节点的预测值,由于令损失函数(mse)最小的预测值为平均值,故途中的value也可以理解为叶节点中样本为坏样本的概率。

2. 对不同叶子节点中的样本进行分类。

2.1 oil_a :pay_amount_tot <= 240387的样本。

2.2 oil_b :pay_amount_tot >240387但discount_amount_cn<=3.5的样本。

2.3 oil_c :discount_amount_cn>3.5的样本。

然后结合原始数据中的类别(class_new),对样本进一步细分,选出bad_rate较小的类别,作为可放宽群体。

1)坏账率分布

坏账率分布

贷前分类

A

B

C

D

E

F

总计

油品分类

oil_A

0.9%

0.7%

1.6%

1.7%

2.9%

5.5%

1.2%

oil_B

1.8%

2.2%

2.7%

5.3%

6.2%

13.1%

3.0%

oil_C

5.1%

6.7%

6.3%

5.9%

15.2%

19.9%

7.4%

总计

2.9%

3.9%

4.2%

4.9%

10.6%

16.1%

4.7%

2)人数分布






人数分布

贷前分类

A

B

C

D

E

F

总计

油品分类

oil_A

4.9%

12.6%

3.9%

2.6%

0.9%

0.7%

25.6%

oil_B

5.0%

12.5%

4.1%

3.2%

0.9%

0.8%

26.4%

oil_C

7.3%

21.6%

7.5%

6.8%

2.4%

2.4%

48.0%

总计

17.1%

46.7%

15.5%

12.6%

4.3%

3.8%

100.0%

3)结果比对


可放款人数

可放款人数占比

坏账率

现计划

5052

45.5%

1.6%

原计划

1901

17.1%

2.9%

remark:

原计划为对class_new值为A的客户进行放款。