4 特征构造
学习目标
- 知道未来信息的概念,及处理未来信息的方法
- 掌握从原始数据构造出新特征的方法
- 掌握特征变换的方法
- 掌握缺失值处理的方法
1 数据准备
1.1 梳理数据的内在逻辑
关系种类
一对一:一个用户有一个注册手机号
一对多:一个用户多笔借款
多对多:一个用户可以登录多个设备,一个设备可以有多个用户登录
- 举例
下图中,蓝色框为二月当期账单,红色框为订单
梳理类ER图
-
任务:分析厚数据常登陆首单用户的逾期情况
-
可以将表结构展示到特征文档当中,说明取数逻辑
1.2 样本设计和特征框架
- 定义观察期样本
- 确定观察期(定X时间切面)和表现期(定Y的标签)
- 确认样本数目是否合理
- 数据EDA
- 看数据总体分布
- data.shape
- data.isnull()
- data.info()
- data.describe()
- 看好坏样本分布差异
- data[data[label]==0].describe() 好用户
- data[data[label]==1].describe() 坏用户
- 看单个数据
- data.sample(n=10,random_state=1)
- 梳理特征框架
- RFM生成新特征
举例 行为评分卡中的用户账单还款特征
-
用户账单关键信息:时间,金额,还款,额度
-
小结:在构建特征前,要完成
-
类ER图
-
样本设计表
-
特征框架表
2 特征构造
2.1 静态信息特征和时间截面特征
用户静态信息特征
- 用户的基本信息(半年内不会变化)
用户时间截面特征
- 未来信息当前时间截面之后的数据
- 时间截面数据在取数的时候要小心,**避免使用未来信息 **
- 产生未来信息最直接的原因:缺少快照表
- 金融相关数据原则上都需要快照表记录所有痕迹(额度变化情况,多次申请的通过和拒绝情况…)
缺少快照表的可能原因
- 快照表消耗资源比较大,为了性能不做
- 原有数据表设计人员疏忽,没做
- 借用其他业务数据(如电商)做信贷
举例
首次借贷 二次借贷 爬虫授权 三次借贷
——————————————————————→
用户 | 借款 | 授权爬虫 | 逾期 |
---|---|---|---|
u1 | l11 | N | 0 |
u1 | l12 | N | 0 |
u1 | l13 | Y | 0 |
u2 | l21 | N | 0 |
u2 | l22 | N | 0 |
u2 | l23 | Y | 1 |
u3 | l11 | N | 0 |
u3 | l12 | N | 0 |
u3 | l13 | Y | 0 |
实际存储
用户 | 授权 |
---|---|
u1 | Y |
u2 | Y |
u3 | Y |
用户 | 借款 | 逾期 |
---|---|---|
u1 | l11 | 0 |
u1 | l12 | 0 |
u1 | l13 | 0 |
u2 | l21 | 0 |
u2 | l22 | 0 |
u2 | l23 | 1 |
u3 | l11 | 0 |
u3 | l12 | 0 |
u3 | l13 | 0 |
join 结果
用户 | 借款 | 授权爬虫 | 逾期 |
---|---|---|---|
u1 | l11 | Y | 0 |
u1 | l12 | Y | 0 |
u1 | l13 | Y | 0 |
u2 | l21 | Y | 0 |
u2 | l22 | Y | 0 |
u2 | l23 | Y | 1 |
u3 | l11 | Y | 0 |
u3 | l12 | Y | 0 |
u3 | l13 | Y | 0 |
解决方案: 加入快照表
用户 | 授权 | 时间 |
---|---|---|
u1 | Y | t3 |
u2 | Y | t3 |
u3 | Y | t3 |
2.2 时间序列特征
用户时间序列特征
- 从观察点往前回溯一段时间的数据
时间序列特征衍生
特征聚合:将单个特征的多个时间节点取值进行聚合。特征聚合是传统评分卡建模的主要特征构造方法。
- 举例,计算每个用户的额度使用率,记为特征ft,按照时间轴以月份为切片展开
- 申请前30天内的额度使用率ft1
- 申请前30天至60天内的额度使用率ft2
- 申请前60天至90天内的额度使用率ft3
- 申请前330天至360天内的额度使用率ft12
- 得到一个用户的12个特征
import pandas as pd
import numpy as np
data = pd.read_excel('data/textdata.xlsx')
data.head()
显示结果
customer_id ft1 ft2 ft3 ft4 ft5 ft6 ft7 ft8 ft9 … gt3 gt4 gt5 gt6 gt7 gt8 gt9 gt10 gt11 gt12 0 111 9 11.0 12 13 18 10 12 NaN NaN … 10 0 18 10 12 NaN NaN NaN NaN NaN 1 112 11 -11.0 10 10 13 13 10 NaN NaN … 10 10 13 13 10 NaN NaN NaN NaN NaN 2 113 0 11.0 10 12 6 10 0 25.0 10.0 … 10 12 6 10 0 25.0 10.0 NaN NaN NaN 3 114 -7 -1.0 9 8 7 0 -19 10.0 11.0 … 10 10 12 0 -19 10.0 11.0 NaN NaN NaN 4 115 11 NaN 6 10 0 17 19 10.0 30.0 … 6 10 0 17 19 10.0 30.0 15.0 NaN NaN 5 rows × 26 columns
- 可以根据这个时间序列进行基于经验的人工特征衍生,例如计算最近P个月特征大于0的月份数
#最近p个月,ft>0的月份数
def Num(ft,p): #ft 特征名字 p特征大于0的月份数
df=data.loc[:,ft+'1':ft+str(p)]
# np.where df>0 向量计算 会拿着每一个数跟0做比较, 返回的也是一个数组
# 条件为true 返回1 条件为False 返回0
auto_value=np.where(df>0,1,0).sum(axis=1)
return ft+'_num'+str(p),auto_value
- 计算最近P个月特征ft等于0的月份数
#最近p个月,ft=0的月份数
def zero_cnt(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
auto_value=np.where(df==0,1,0).sum(axis=1)
return ft+'_zero_cnt'+str(p),auto_value
- 计算近p个月特征ft大于0的月份数是否大于等于1
#最近p个月,ft>0的月份数是否>=1
def Evr(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
arr=np.where(df>0,1,0).sum(axis=1)
auto_value = np.where(arr,1,0)
return ft+'_evr'+str(p),auto_value
- 计算最近p个月特征ft的均值
#最近p个月,ft均值
def Avg(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
auto_value=np.nanmean(df,axis = 1 )
return ft+'_avg'+str(p),auto_value
- 计算最近p个月特征ft的和,最大值,最小值
#最近p个月,ft和
def Tot(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
auto_value=np.nansum(df,axis = 1)
return ft+'_tot'+str(p),auto_value
#最近(2,p+1)个月,ft和
def Tot2T(ft,p):
df=data.loc[:,ft+'2':ft+str(p+1)]
auto_value=df.sum(1)
return ft+'_tot2t'+str(p),auto_value
#最近p个月,ft最大值
def Max(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
auto_value=np.nanmax(df,axis = 1)
return ft+'_max'+str(p),auto_value
#最近p个月,ft最小值
def Min(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
auto_value=np.nanmin(df,axis = 1)
return ft+'_min'+str(p),auto_value
- 其余衍生方法
#最近p个月,最近一次ft>0到现在的月份数
def Msg(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
df_value=np.where(df>0,1,0)
auto_value=[]
for i in range(len(df_value)):
row_value=df_value[i,:]
if row_value.max()<=0:
indexs='0'
auto_value.append(indexs)
else:
indexs=1
for j in row_value:
if j>0:
break
indexs+=1
auto_value.append(indexs)
return ft+'_msg'+str(p),auto_value
#最近p个月,最近一次ft=0到现在的月份数
def Msz(ft,p):
df=data.loc[:,ft+'1':ft+str(p)]
df_value=np.