金融风控 Task3-特征工程

学习目标

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

内容介绍

  • 数据预处理:
  • 缺失值的填充
  • 时间格式处理
  • 对象类型特征转换到数值
  • 异常值处理:
  • 基于3segama原则
  • 基于箱型图
  • 数据分箱:
  • 固定宽度分箱
  • 分位数分箱
  • 离散数值型数据分箱
  • 连续数值型数据分箱
  • 卡方分箱
  • 特征交互:
  • 特征和特征之间组合
  • 特征和特征之间衍生
  • 其他特征衍生的尝试
  • 特征编码:
  • one-hot编码
  • label-encode编码
  • 特征选择:
  • Filter
  • Wrapper
  • Embedded

代码示例

导包并读取数据

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('./data/train.csv')
testA = pd.read_csv('./data/testA.csv')

特征预处理

numerical_fea = list(train.select_dtypes(exclude=['object']).columns)
category_fea = list(filter(lambda x: x not in numerical_fea,list(train.columns)))
label = 'isDefault'
numerical_fea.remove(label)
缺失值填充
# 查看缺失值
train.isnull().sum()

在这里插入图片描述

# 按照平均数填充数值特征
train[numerical_fea] = train[numerical_fea].fillna(train[numerical_fea].median())
testA[numerical_fea] = testA[numerical_fea].fillna(train[numerical_fea].median())

# #按照众数填充类别型特征
train[category_fea] = train[category_fea].fillna(train[category_fea].mode())
testA[category_fea] = testA[category_fea].fillna(train[category_fea].mode())
train.isnull().sum()

在这里插入图片描述

时间格式处理

for data in [train, testA]:
    data['issueDate'] = pd.to_datetime(data['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01','%Y-%m-%d')
    data['issueDateDT'] = data['issueDate'].apply(lambda x: x-startdate).dt.days

train['employmentLength'].value_counts(dropna=False).sort_index()

在这里插入图片描述

对象类型特征转换到数值特征

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('10+ years','10 years', inplace=True)
    data['employmentLength'].replace('< 1 year','0 year', inplace=True)
    data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)
data['employmentLength'].value_counts(dropna=False).sort_index()

在这里插入图片描述

train['earliesCreditLine'].sample(5)

在这里插入图片描述

for data in [train,testA]:
    data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:]))

类别特征处理

cate_features = ['grade', 'subGrade', 'employmentTitle', 'homeOwnership','verificationStatus', 'purpose', 'postCode', 'regionCode', 'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:
    print(f, '类型数:', data[f].nunique())

在这里插入图片描述

异常值处理

检测异常的方法:均方差
def find_outliers_by_3segama(data,fea):
    data_std = np.std(data[fea])
    data_mean = np.mean(data[fea])
    outliers_cut_off = data_std*3
    lower_rule = data_mean - outliers_cut_off
    upper_rule = data_mean + outliers_cut_off
    data[fea+'_outliers'] = data[fea].apply(lambda x:str('异常值') if x > upper_rule or x < lower_rule else '正常值')
    return data
data_train = data_train.copy()
for fea in numerical_fea:
    data_train = find_outliers_by_3segama(data_train,fea)
    print(data_train[fea+'_outliers'].value_counts())
    print(data_train.groupby(fea+'_outliers')['isDefault'].sum())
    print('*'*10)

在这里插入图片描述

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

数据分桶

  • 特征分箱的目的:
  • 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
  • 数据分桶的对象:
  • 将连续变量离散化
  • 将多状态的离散变量合并成少状态
  • 分箱的原因:
  • 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间化即数据分桶也叫做数据分箱,然后使用量化后的结果。
  • 分箱的优点:
  • 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
  • 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
  • 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但际x与y之间经常存在着非线性关系,此时可经过WOE变换。
  • 特别要注意一下分箱的基本原则:
  • 最小分箱占比不低于5%
  • 箱内不能全部是好客户
  • 连续箱单调
固定宽度分箱
当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:09、1099、100999、10009999,等等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。
# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是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'], 10, labels=False)
特征交互
for col in ['grad','subGrade']:
    temp_dict = data_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()
    data_train[col + '_target_mean'] = data_train[col].map(temp_dict)
    data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)
# 其他衍生变量 mean 和 std
for df in [data_train, data_test_a]:
for item in
['n0','n1','n2','n2.1','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')

特征编码

labelEncode 直接放入树模型中
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']):
le = LabelEncoder()
le.fit(list(data_train[col].astype(str).values) +
list(data_test_a[col].astype(str).values))
data_train[col] = le.transform(list(data_train[col].astype(str).values))
data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values))
print('Label Encoding 完成')

特征选择

  • Filter
    a. 方差选择法
    b. 相关系数法(pearson 相关系数)
    c. 卡方检验
    d. 互信息法
  • Wrapper (RFE)
    a. 递归特征消除法
  • Embedded
    a. 基于惩罚项的特征选择法
    b. 基于树模型的特征选择

猜你喜欢

转载自blog.csdn.net/BigCabbageFy/article/details/108719488