机器学习中如何处理缺失数据?

做监督学习算法,训练数据集中的部分数据缺失,怎么预处理这些数据能够使得训练的结果不受影响,或是影响最小?
关注者
1,127
被浏览
326,541

36 个回答

数据挖掘中遇到缺失值时, 一般有以下几种方式:

这里以一份实战数据为例(个人征信预测比赛的实际数据,保存在文件user_info_train.csv中,有兴趣自己动手实践一遍的可以私信我索取数据表)。

打开这张表,如下图所示:包括用户的基本属性特征(性别、工作类型、教育程度、婚姻状况)和一些用户的记录(产品浏览历史、收入变化、账户数目、信用总额度、平均额度、上期还款金额),结果标签是overdue。

可以看到,表格中有些用户的部分特征属性是存在缺失的,具体各个特征的情况如何我们可以借助pandas中的info()来看具体确实情况了。

#coding:utf-8
import pandas as pd

# 读取user_info_train.csv表 
user_info = pd.read_csv("C:/Users/Administrator/Desktop/user_info_train.csv")

# info()查看每个特征的数据量情况 
print data_train.info()

输出结果如下:

可以看到,该表里面的用户数一共有55594条,其中的gender,job,edu,marriage, family_type 五个特征没有值缺失;而browse_his,salary_change,card_num, total_amoount, av_amount,pre_repay六个属性存在缺失,而且salary_change特征只有2991条,说明其存在严重缺失。


1、缺失值较多的特征处理

一般如果某特征的缺失量过大(如上面的salary_change特征),我们会直接将该特征舍弃掉,否则可能反倒会带入较大的noise,对结果造成不良影响。

但是,这里为了后面讲解数据标准化操作更完善,暂时不采取删除特征的操作,而是换一种方式,将该特征分为两类,一类是非缺失的,我们将其设为“工资收入有变化”,另一类是缺失的,将其设为“工资收入无变化”。

# 定义工资改变特征缺失值处理函数,将有变化设为Yes,缺失设为No 
def set_salary_change(df):
     df.loc[(df.salary_change.notnull()), 'salary_change'] = "Yes"
     df.loc[(df.salary_change.isnull()), 'salary_change'] = "No"
     return df
data_train = set_salary_change(data_train)

2、缺失值较少的特征处理

其余的特征缺失值都在10%以内,我们可以采取很多的方式来处理,下面一一介绍下:


方式1:把NaN直接作为一个特征,假设用0表示,实现如下:

data_train.fillna(0) 

方式2:用均值填充;

# 将所有行用各自的均值填充 
data_train.fillna(data_train.mean())  

# 也可以指定某些行进行填充 
data_train.fillna(data_train.mean()['browse_his':'card_num']) 

有时候还有可能遇到这样一种情况,那就是训练集train中有缺失值,而test中无缺失值。这时候最适合的处理方式应该是对缺失值取条件均值或条件中值,即即根据该用户的label值类别,取所有该label下用户该属性的均值或中值。


方式3:用上下数据进行填充;

# 用前一个数据代替NaN:method='pad'
data_train.fillna(method='pad')  

# 与pad相反,bfill表示用后一个数据代替NaN 
data_train.fillna(method='bfill') 

方式4:用插值法填充;

# 插值法就是通过两点(x0,y0),(x1,y1)估计中间点的值 
data_train.interpolate() 

方式5:用算法拟合进行填充;

# 定义browse_his缺失值预测填充函数
def set_missing_browse_his(df):
     # 把已有的数值型特征取出来输入到RandomForestRegressor中
     process_df = df[[browse_his' , 'gender', 'job', 'edu', 'marriage', 'family_type']]

     # 乘客分成已知该特征和未知该特征两部分
     known = process_df[process_df.browse_his.notnull()].as_matrix()
     unknown = process_df[process_df.browse_his.isnull()].as_matrix()

     # X为特征属性值
     X = known[:, 1:]

     # y为结果标签值
     y = known[:, 0]

     # fit到RandomForestRegressor之中
     rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
     rfr.fit(X,y)

     # 用得到的模型进行未知特征值预测
     predicted = rfr.predict(unknown[:, 1::])

     # 用得到的预测结果填补原缺失数据
     df.loc[(df.browse_his.isnull()), 'browse_his'] = predicted

     return df, rfr

data_train, rfr = set_missing_browse_his(data_train) 

当然,针对我们这里的数据,我们对那些缺失值不是很大的特征都采用方式5来填补其缺失值。即使用随机森林算法,利用数据表中某些没有缺失的特征属性来预测某特征属性的缺失值。将填补后的数据表保存在new_user_info_train.csv中:

data_train.to_csv('C:/Users/Administrator/Desktop/new_user_info_train.csv')

完成缺失值填充后的新数据表如下:

【以上就是我整理的一些基本的缺失值处理方式。 这里顺便说明下,我本人也是自学数据科学的,17年参加校招,一路走来深感不易,因此决定将自己的学习过程和学习内容,以及到时候的求职经验都整理出来进行分享,具体大家可以关注我的知乎专栏和我的微信公众号,名字都是“DT新纪元”,大家有兴趣也可以在关注我的知乎然后私信我,沟通学习和求职方面的问题,主要在力所能及的范围内,我都会尽量给予回答的】

目前有三类处理方法:

1. 用平均值、中值、分位数、众数、随机值等替代。效果一般,因为等于人为增加了噪声。

2. 用其他变量做预测模型来算出缺失变量。效果比方法1略好。有一个根本缺陷,如果其他变量和缺失变量无关,则预测的结果无意义。如果预测结果相当准确,则又说明这个变量是没必要加入建模的。一般情况下,介于两者之间。

3. 最精确的做法,把变量映射到高维空间。比如性别,有男、女、缺失三种情况,则映射成3个变量:是否男、是否女、是否缺失。连续型变量也可以这样处理。比如Google、百度的CTR预估模型,预处理时会把所有变量都这样处理,达到几亿维。这样做的好处是完整保留了原始数据的全部信息、不用考虑缺失值、不用考虑线性不可分之类的问题。缺点是计算量大大提升。

而且只有在样本量非常大的时候效果才好,否则会因为过于稀疏,效果很差。