本学习笔记为阿里云天池龙珠计划Docker训练营的学习内容,学习链接为:https://github.com/datawhalechina/team-learning-data-mining/tree/master/FinancialRiskControl
学习目标:
- 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
- 学习特征交互、编码、选择的相应方法
首先,我们思考为什么要学习特征工程?
答:特征工程,就是对原始数据进行一系列工程处理,将其提炼为特征,做为输入供算法和模型使用。特征工程的目的是去除原始数据中的杂质和冗余,设计更高效的特征以刻画求解的问题与预测模型之间的关系。[^1]
举一个例子,现在出一个二分类问题,请你使用逻辑回归,设计一个身材分类器。输入数据X:身高和体重 ,标签为Y:身材等级(胖,不胖)。显然,不能单纯的根据体重来判断一个人胖不胖,姚明很重,他胖吗?显然不是。针对这个问题,一个非常经典的特征工程是,BMI指数,BMI=体重/(身高^2)。 这样,通过BMI指数,就能非常显然地帮助我们,刻画一个人身材如何。甚至,你可以抛弃原始的体重和身高数据。[^2]
一、学习知识概要
- 决策树一些介绍
- 数据预处理
- 缺失值的填充
- 时间格式处理
- 对象类型特征转换到数值
- 异常值处理
- 基于3σ原则
- 基于箱型图
- 数据分箱
- 固定宽度分箱
- 分位数分箱
- 离散数值型数据分箱
- 连续数值型数据分箱
- 卡方分箱(选做作业)
- 特征交互
- 特征和特征之间组合
- 特征和特征之间衍生
- 其他特征衍生的尝试(选做作业)
- 特征编码
- one-hot编码
- label-encoder编码
- 特征选择
- 1 Filter
- 2 Wrapper (RFE)
- 3 Embedded
二、学习内容
2.1 决策树一些介绍
因为该节内容涉及决策树,所以各位可以先了解一下决策树,它是一个非常常见并且优秀的机器学习算法,它易于理解、可解释性强,其可作为分类算法,也可用于回归模型。
先介绍该节内容主要用到的包:catboost
本质上是决策树算法
适用于:需要特别多的前期数据处理和特征数值化的任务
Catboost 一个超级简单实用的boost算法:https://www.jianshu.com/p/49ab87122562
- 它自动采用特殊的方式处理类别型特征(categorical features)。首先对categorical features做一些统计,计算某个类别特征(category)出现的频率,之后加上超参数,生成新的数值型特征(numerical features)。这也是我在这里介绍这个算法最大的motivtion,有了catboost,再也不用手动处理类别型特征了。
- catboost还使用了组合类别特征,可以利用到特征之间的联系,这极大的丰富了特征维度。
- catboost的基模型采用的是对称树,同时计算leaf-value方式和传统的boosting算法也不一样,传统的boosting算法计算的是平均数,而catboost在这方面做了优化采用了其他的算法,这些改进都能防止模型过拟合。
其中对称树与普通的决策树有些许区别,大部分的GBM模型用的都是普通的决策树,对称树在GBM中主要有三点优势:
1、拟合模式相对简单,因为每一层都是一个判断条件
2、可以提高预测速度
3、对称树的结构本身比普通决策树自由度小,可以看作是加入了penalty,或者看作regularization
2.2 数据预处理
先导入包和数据集,并查找出数据中的对象特征和数值特征
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')
data_train =pd.read_csv('train.csv')
data_test_a = pd.read_csv('testA.csv')
数据大概长这样
id loanAmnt term interestRate installment grade subGrade employmentTitle employmentLength homeOwnership ... n5 n6 n7 n8 n9 n10 n11 n12 n13 n14
0 35000.0 5 19.52 917.97 E E2 320.0 2 years 2 ... 9.0 8.0 4.0 12.0 2.0 7.0 0.0 0.0 0.0 2.0
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) #数值列
category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns))) #类别特征列
label = 'isDefault'
numerical_fea.remove(label)
2.21 缺失值的填充
在比赛中数据预处理是必不可少的一部分,对于缺失值的填充往往会影响比赛的结果,在比赛中不妨尝试多种填充然后比较结果选择结果最优的一种;
比赛数据相比真实场景的数据相对要“干净”一些,但是还是会有一定的“脏”数据存在,清洗一些异常值往往会获得意想不到的效果。
-
把所有缺失值替换为指定的值0
data_train = data_train.fillna(0)
-
用缺失值上面的值填充缺失值
data_train = data_train.fillna(axis=0,method=‘ffill’)
bfill是用缺失值下面的值填充缺失值
二维中axis=0表示行,1表示列 -
纵向用缺失值下面的值填充缺失值,且设置最多只填充两个连续的缺失值
data_train = data_train.fillna(axis=0,method=‘bfill’,limit=2)
#查看缺失值情况
data_train.isnull().sum() #查看每列有多少缺失值
#以下展示部分结果
#按照平均数填充数值型特征
data_train[numerical_fea] = data_train[numerical_fea].fillna(data_train[numerical_fea].median())
data_test_a[numerical_fea] = data_test_a[numerical_fea].fillna(data_train[numerical_fea].median())
#按照众数填充对象型特征
data_train[category_fea] = data_train[category_fea].fillna(data_train[category_fea].mode())
data_test_a[category_fea] = data_test_a[category_fea].fillna(data_train[category_fea].mode())
#以下展示部分结果
值得注意的是结果中只有object变量‘employmentLength’没有被填充,下面的方法是将object对象转化成int或float(取出或替换数值),再进行填充,但还有一种方法是采用下面的代码:
data_train[‘employmentLength’].replace(np.nan,‘10 years’,inplace=True)
data_test_a[‘employmentLength’].replace(np.nan,‘10 years’,inplace=True)
它会将缺失值直接填用别的进行替换,方便后续一并处理
#查看类别特征
category_fea
-----------------------------------
['grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine']
category_fea:对象型类别特征需要进行预处理,其中[‘issueDate’]为时间格式特征。
2.22 时间格式处理
#转化成时间格式 将表中的时间转化成python可以处理的时间格式
for data in [data_train, data_test_a]: #因为这两个表有一样的列特征,可以一起处理
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 #返回的是每个时间距开始时间的间隔天数
data_train['employmentLength'].value_counts(dropna=False).sort_index()
# sort_index(axis=0,ascending=True)按索引行排升序,sort_values()按值行排升序
#employmentLength 的样式如:2 years,是object类型,在上面没有被填充,所以我们应该把数字取出来,将他转化为int或float类型再进行填充
#要将其中的数字取出来
def employmentLength_to_int(s):
if pd.isnull(s):
return s
else:
return np.int8(s.split()[0]) #将数据转化为int8类型,节省内存
for data in [data_train, data_test_a]:
data['employmentLength'].replace(to_replace='10+ years', value='10 years', inplace=True) #数据规整
data['employmentLength'].replace('< 1 year', '0 years', inplace=True) #replace是替换函数
data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int) #apply()是应用函数
data['employmentLength'].value_counts(dropna=False).sort_index()
- 对earliesCreditLine进行预处理
data_train['earliesCreditLine'].