风控建模九:一些特征工程方法及自动化工具小结
建模工作中我们常常面临的一大问题就是,如何在有限的数据下,通过一些特征工程方法,挖掘出数据中更多的信息,来达到提升模型效果的目的。所以本篇文章就简单整理了一些特征工程的思路、方法和一些现成的特征工程python工具包,供大家一起探讨。
一、方法篇
我们下面所做的特征工程工作都是基于这样一种基本思想,即数据孤立地呈现是意义不大的,当我们忽略数字背后的时空背景时,数字背后更丰富的信息就无法表达出来。不同数据之间应该能够通过某种特定方式的有机组合、互相提供场景而衍生出新的有效信息,这是我们特征工程工作基本的出发点。
1、交叉组合,打标有风险区分力的特定人群
举例来看,比如婚姻状态这样一个变量,在我们的业务场景下看到的结果通常是离异的客群比已婚的客群的坏账率稍微高一些,但也没有明显差异。这样的结果能说明婚姻状态这个变量对风险没有区分度,对提升模型效果没有贡献吗?实际并非如此,当我们把年龄和婚姻状态两个变量组合分析后发现,30岁以上离异的客群和已婚的客群坏账率并没有什么差别,但30岁以下就离异的客群坏账率却明显高于其他婚姻状态。这就是一个比较生动的赋予数据场景的例子。当我们单独看婚姻状态这个数据时,我们能获取到的信息非常有限,在风险区分度上也看不到明显效果,但当我们给婚姻状态赋予了年龄这个场景后,我们就进一步获取到了更深层次的信息,30岁不到、离异,仅仅通过两个维度的数据组合,我们就看到了一个似乎不靠谱、不慎重、不负责的形象,而这种组合圈定的人群在风险表现上自然也更高。
基于这样的想法,我们设计的第一种特征工程方法就是所有变量分箱,遍历交叉,找到所有交叉后有明显风险区分力的组合方式,并将这些组合圈定的人群以独热编码的方式打标,作为新的衍生变量。代码如下:
from itertools import product
import multiprocessing
from tqdm import tqdm
def var_cross(df, res_col, target = 'if_overdue_30', bad_thresh=0.2, good_thresh=0.05, num_rate=0.02, lift_rate=1.5):
res = pd.DataFrame()
df_res = pd.DataFrame()
for group_col in res_col:
group_df = df[list(group_col)+[target]].copy()
for num_col in group_df.select_dtypes(include=[np.number]):
if group_df[num_col].value_counts().shape[0]>=10:
group_df[num_col] = pd.qcut(group_df[num_col],10,duplicates='drop').astype(str)
tmp = group_df.groupby(list(group_col) if isinstance(group_col,tuple) else group_col)[target].agg({
'count','mean'})
for item in tmp.iterrows():
if (item[1]['count']>df.shape[0]*num_rate)&((item[1]['mean']>bad_thresh)|(item[1]['mean']<good_thresh)):
if len(group_col)==2:
br1 = group_df.loc[group_df[group_col[0]]==item[0][0],target].mean()
br2 = group_df.loc[group_df[group_col[1]]==item[0][1],target].mean()
if ( (item[1]['mean']>=br1*lift_rate)&(item[1]['mean']>=br2*lift_rate) )|( (item[1]['mean']*lift_rate<=br1)&(item[1]['mean']*lift_rate<=br2) ):
col_name = 'CROSS_'+str(group_col[0])+str(item[0][0])+str(group_col[1])+str(item[0][1])
col_name = col_name.replace(', ','_').replace('(','').replace(']','')
group_df[col_name]=0
group_df.loc[(group_df[group_col[0]]==item[0][0])&(group_df[group_col[1]]==item[0][1]),col_name]=1
df_tmp = pd.DataFrame({
'var':str(group_col),'bin':str(item[0]),'count':item[1]['count'],
'bad_rate':item[1]['mean'],'var_bad_rate':str([br1,br2])},index=[0])
res = pd.concat([res,df_tmp])
df_res = pd.concat([df_res,group_df[col_name]],axis=1)
elif len(group_col)==3:
br1 = group_df.loc[group_df[group_col[0]]==item[0][0],target].mean()
br2 = group_df.loc[group_df[group_col[1]]==item[0][1],target].mean()
br3 = group_df.loc[group_df[group_col[2]]==item[0][2],target].mean()
if ( (item[1]['mean']>=br1*lift_rate)&(item[1]['mean']>=br2*lift_rate)&(item[1]['mean']>=br3*lift_rate