小白金融风控TASK3


此部分是金融风控的特征工程部分,要实现的目标是:

  1. 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
  2. 学习特征交互、编码、选择的相应方法

3.1数据预处理

3.1.1 导入包并读取数据

导入需要的包,查找出数据中的对象特征和数值特征

// An highlighted block
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
from tqdm import tqdm
from sklearn.preprocessing import LabelEncoder
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb
import lightgbm as lgb
from catboost import CatBoostRegressor
import warnings
from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss
warnings.filterwarnings('ignore')

#训练集测试集导入
train = pd.read_csv('train.csv')
testA = pd.read_csv('testA.csv')

3.2异常值处理

3.2.1 特征预处理

// An highlighted block
#分离出数值型特征和类别型特征
numerical_fea = list(train.select_dtypes(exclude = ['object']).columns)
category_fea = list(train.select_dtypes(include = ['object']).columns)
label = 'isDefault'
numerical_fea.remove(label)

3.2.2 缺失值填充

使用train.isnull().sun() 查看缺失值的情况如下;

// An highlighted block
id                        0
loanAmnt                  0
term                      0
interestRate              0
installment               0
grade                     0
subGrade                  0
employmentTitle           1
employmentLength      46799
homeOwnership             0
annualIncome              0
verificationStatus        0
issueDate                 0
isDefault                 0
purpose                   0
postCode                  1
regionCode                0
dti                     239
delinquency_2years        0
ficoRangeLow              0
ficoRangeHigh             0
openAcc                   0
pubRec                    0
pubRecBankruptcies      405
revolBal                  0
revolUtil               531
totalAcc                  0
initialListStatus         0
applicationType           0
earliesCreditLine         0
title                     1
policyCode                0
n0                    40270
n1                    40270
n2                    40270
n3                    40270
n4                    33239
n5                    40270
n6                    40270
n7                    40270
n8                    40271
n9                    40270
n10                   33239
n11                   69752
n12                   40270
n13                   40270
n14                   40270

根据结果我们发现0-n14以及employLength特征缺失值较多,employmentTitle,postCode,dti,pubRecBankruptcies,revolUtil,title有较少的缺失,我们这里采用的方法是对于数值型变量,我们取中位数,对于类别型变量,我们使用众数来填充缺失值

// An highlighted block

train[numerical_fea] = train[numerical_fea].fillna(train[numerical_fea].median())
train[category_fea] = train[category_fea].fillna(train[category_fea].mode())

#重新查看一下缺失值的情况
train.isnull().sum()

3.2.3时间格式处理

观察数据发现isissueDate(贷款发放的月份)这个特征是一个时间特征,我们处理的方式是计算借款日与最文件中最小的日期的距离的天数来构造一个新的特征:

// An highlighted block
#最早的日期
startdate = datetime.datetime.strptime('2007-06-01','%Y-%m-%d')
#先转换格式再用日期-最早的日期得出天数为新的特征issueDateDT
for data in [train,testA]:
   data['issueDate'] =pd.to_datetime(data['issueDate'],format = '%Y-%m-%d')
   #构造时间特征
   data['issueDateDT'] = data['issueDate'].apply(lambda x: x - startdate).dt.days

对employmentLength的数据做一些预处理:空值依然返回空值,>10年的全部分到10这个类别,<1年的分到0这个类别。

// An highlighted block
def employmentLength_to_int(s):
   if pd.isnull(s):
       return s
   else:
       return np.int8(s.split(' ')[0])
   
for data in [train,testA]:
   data['employmentLength'].replace(to_replace='< 1 year',value='0 year',inplace=True)
   data['employmentLength'].replace(to_replace='10+ years',value='10 years',inplace=True)
   data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)

对earliesCreditLine(借款人最早报告的信用额度开立的月份)进行预处理:我们直接提取出年份

// An highlighted block
for data in [train,testA]:
    data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda x : x[-4:])

3.2.4 纯类别型特征处理

纯类别一般有两种思路:
1.若这个变量是有大小关系的,比如该数据中的grade,可以使用数值映射
(‘A’:1,‘B’:2,‘C’:3,‘D’:4,‘E’:5,‘F’:6,‘G’:7)
2.若变量之间是没有什么关系的,我们可以用独热编码(One-hot-encode)

// An highlighted block
# 部分类别特征
cate_features = ['grade', 'subGrade', 'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \
                 'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:
    print(f, '类型数:', data[f].nunique())

#对于grade这种有大小,优劣区分的特征
for data in [data, data_test_a]:
    data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7})

#对于这些没有大小之分的特征,类型数在2之上,又不是高维稀疏的,且纯分类特征,可以使用独热编码
for data in [train,testA]:
    data = pd.get_dummies(data,columns=['subGrade', 'homeOwnership', 'verificationStatus','regionCode'],drop_first= True)


3.2.5 异常处理

首先,当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。
其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据勒说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
最后,test数据不能删。
1.检测异常的一种方法:3 σ \sigma σ法
在统计学中,如果一个总体近似正态分布,那么大约68%的数据会落在一个标准差范围内,大约95%的数据会落在两个标准差范围内,大约99.7%的数据会落在三个标准差范围内,所以,我们可以把超过3 σ \sigma σ 的数据标为异常值。
2.检测异常的方法二:箱型图
总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR;

// An highlighted block
#写一个判断异常/正常值的函数,并生成一个新的列fea_outliers
def find_outliers_by_3sigma(data,fea):
    data_mean = np.mean(data[fea])
    data_std = np.std(data[fea])
    lower_rule = data_mean - 3 * data_std
    upper_rule = data_mean + 3 * data_std
    data[fea + '_outliers'] = data[fea].apply(lambda x:str('异常值') if x < lower_rule and x > upper_rule else '正常值' )
    return data
#得到特征的异常值后可以进一步分析变量异常值和目标变量的关系
for fea in numerical_fea:
    train = find_outliers_by_3sigma(train,fea)
    print(train[fea + '_outliers'].value_counts())
    print(train.groupby(fea+'_outliers')['isDefault'].sum())
    print('*' * 20)

#删除异常值
for fea in numerical_fea:
    train = data[train[fea+'_outliers']=='正常值']
    train = data.reset_index(drop=True) 


3.3 数据分箱

1.特征分箱的目的:从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
2.数据分桶的对象:
(1).将连续变量离散化
(2).将多状态的离散变量并成少状态
3.分箱的原因:数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据分桶也叫做数据分箱,然后使用量化后的结果。
4.分箱的优点:
(1).处理缺失值上,可以将null单独作为一个分箱
(2).处理异常值上,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
(3)业务解释性上,我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在着非线性关系,此时可经过WOE(Weight of Evidence)变换。
5.分箱的基本原则:
(1).最小分箱占比不低于5%
(2)箱内不能全是好客户
(3).连续箱单调

-固定宽度分箱
当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。

// An highlighted block
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000)
## 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))
# 分位数分箱
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 1 10, labels=False)

3.4 特征交互

线性模型中包含有交互特征对,那它的训练时间和评分时间就会从 O(n) 增加到 O(n2),其中 n 是单一特征的数量。

// An highlighted block

#特征交互
    for col in ['grade', 'subGrade']: 
    temp_dict = train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})
    temp_dict.index = temp_dict[col].values
    temp_dict = temp_dict[col + '_target_mean'].to_dict()

    train_data[col + '_target_mean'] = train[col].map(temp_dict)
    train_data[col + '_target_mean'] = testA[col].map(temp_dict)
    
    
# 其他衍生变量 mean 和 std
for df in [train, testA]:
    for item in ['n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:
        df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')
        df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')


3.5 特征编码

labelEncoder 直接放入模型中

// An highlighted block
##label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
from sklearn.preprocessing import LabelEncoder
#tqdm是一个看程序进程的函数
for col in tqdm(['subGrade','postCode','title','employmentTitle']):
    le = LabelEncoder()
    # astype()函数可用于转化dateframe某一列的数据类型,values方法返回结果是数组
    le.fit(list(train[col].astype(str).values) + list(testA[col].astype(str).values))
    print(le.classes_)
    train[col] = le.transform(list(train[col].astype(str).values))
    testA[col] = le.transform(list(testA[col].astype(str).values))
print('Label Encoding Finished!')

3.6 特征选择

特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。
目前特征选择方法共有以下三个大类:
1.filter(过滤法)
(1)方差选择法
(2)相关系数法(pearson 相关系数)
(3)卡方检验
(4)互信息法

2.Wrapper(包裹法)
(1).RFE(递归特征消除)
(2)RFECV(递归特征消除交叉验证)

3.Embedded
(1).基于惩罚项的特征选择法
(2).基于树模型的特征选择
1.filter方法

// fiter方法
#1.方差选择法
#该方法要计算各个特征的方差,设置一个阈值,仅选择大于阈值的特征(方差小说明数据之间变化不大所以可能对变量影响不大)
from sklearn.feature_selection import VarianceThreshold
#threshold就是阈值
VarianceThreshold(threshold=3).fit_transform(train,target_train)


#2.相关系数法
#根据Pearson相关系数计算每个特征与目标的线性相关性
#结果的取值区间为 [-11]-1 表示完全的负相关, +1表示完全的正相关,0 表示没有线性相关。
#结合Pearson相关系数选取最好的K个特征
#注意:0只代表没有线性关系,并不代表独立。
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,
#输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数
SelectKBest(k=5).fit_transform(train,target_train)


#3.chi2检验
#同样是用于检验自变量与因变量之间的相关性
#注:卡方只能运用在正定矩阵上,否则会报错Input X must be non-negative
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#参数k为选择的特征个数
SelectKBest(chi2, k=5).fit_transform(train,target_train)

#4.互信息法
#也是检验自变量与因变量之间的相关性
from sklearn.feature_selection import SelectKBest
from minepy import MINE
#由于MINE的设计不是函数式的,定义mic方法将其为函数式的,
#返回一个二元组,二元组的第2项设置成固定的P0.5
def mic(x, y):
   m = MINE()
   m.compute_score(x, y)
   return (m.mic(), 0.5)
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(train,target_train)

.2.Wrapper(RFE)
递归特征消除法(RFE):递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,一逻辑回归为例。


from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数

RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(train,target_train)

3.Embedded(惩罚法)
基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维

// An highlighted block
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#带L1惩罚项的逻辑回归作为基模型的特征选择

SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(train,target_train)

基于树模型的特征选择 树模型中GBDT也可用来作为基模型进行特征选择。 在feature_selection库的SelectFromModel类结合GBDT模型可以用于选择特征

// An highlighted block
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值