本文共 5179 字,大约阅读时间需要 17 分钟。
我们从现实中收集的数据,几乎不可能是完美的,往往都会有一些缺失值,很多人选择的是直接将含有缺失值的样本直接删除,这是一种方式,但是有时候填补缺失值会比直接丢弃样本效果更好,即使我们不知道缺失值的真实数据。
在sklearn.impute.SimpleImputer
模块中可以轻松地将均值、中值、或者其它常用的数值来对空值进行填补。下面我们将对波士顿房价数据集进行均值
、0
、随机森林回归
来进行缺失值填补,并验证各种情况下的拟合效果,找出最佳的缺失值填补方式。
import numpy as npimport pandas as pdfrom matplotlib import pyplot as pltfrom sklearn.datasets import load_bostonfrom sklearn.impute import SimpleImputer # 对空值进行from sklearn.ensemble import RandomForestRegressorfrom sklearn.model_selection import cross_val_score
# 获取数据集 --- 共有 506*13=6578 个数据boston = load_boston()x_full = boston.data # 数据集y_full = boston.target # 标签列n_samples = x_full.shape[0] # 506行n_features = x_full.shape[1] # 13列 --- 特征名称
50%
,也就是共有3289
个数据缺失rng = np.random.RandomState(0) # 随机种子missing_rate = 0.5 # 缺失值比例n_missing_samples = int(np.floor(n_samples*n_features*missing_rate)) # np.floor()向下取整,返回.0格式的浮点数n_missing_samples # 3289
# randint(下限,上限,取出数的个数)missing_samples = rng.randint(0,n_samples,n_missing_samples) # 行中随机取出3289个数据missing_features= rng.randint(0,n_features,n_missing_samples) # 列中随机取出3289个数据# 使用上述的方式进行抽样,会使得数据远超样本量506(这里的样本量只按照行来计算)# 我们还可以使用np.random.choice()来进行抽象,可以抽取不重复的随机数,确保数据不会集中在同一行中,某种程度上也保证了数据的分散度
x_missing = x_full.copy() # 对源数据集进行拷贝x_missing[missing_samples,missing_features] = np.nan # 通过行、列索引随机定位生成缺失值x_missing = pd.DataFrame(x_missing)x_missing
sklearn.impute
中的SimpleImputer类
进行填补,missing_values
=np.nan
代表当前所需填补值(空值)的类型;strategy
='mean'
表示填补空值所使用的策略,就是用均值mean来进行填补。# 4.使用均值进行填补imp_mean = SimpleImputer(missing_values=np.nan,strategy='mean')x_missing_mean = imp_mean.fit_transform(x_missing) # 训练fit() + 导出predict() ==> fit_transform()x_missing_mean = pd.DataFrame(x_missing_mean) x_missing_mean
填补之后,效果如下图所示
strategy
=’constant
’,fill_value
=0
表示使用常量进行填补,fill_value指明所使用的常数为0。imp_0 = SimpleImputer(missing_values=np.nan,strategy='constant',fill_value=0)x_missing_0 = imp_mean.fit_transform(x_missing) # 训练fit() + 导出predict() ==> fit_transform()x_missing_0 = pd.DataFrame(x_missing_0) x_missing_0
任何回归都是从特征矩阵中学习,然后求解连续型标签y的过程,之所以能够实现这个过程,是因为回归算法认为特征矩阵和标签之前存在着某种联系。实际上,标签和特征是可以相互转换的
,比如说,在一个“用地区,环境,附近学校数量预测“房价”的问题中,我们既可以用“地区,“环境”,“附近学校数量”的数据来预测“房价”,也可以反过来用“环境”,“附近学校数量”和“房价”来预测“地区”(有点类似"y=kx+b"方程中的知三求一)。而回归填补缺失值,正是利用了这种思想。
对于一个有n
个特征的数据来说,其中特征T
有缺失值,我们就把特征T
当作标签,其他的 n-1
个特征和原本的标签组成新的特征矩阵。那对于T
来说,它没有缺失的部分,就是我们的ytrain
,这部分数据既有标签也有特征,而它缺失的部分,只有特征没有标签,就是我们需要预测的部分
。
特征T不缺失的值对应的其他 n-1
个特征+本来的标签:xtrain
特征T不缺失的值:ytrain
特征缺失的值对应的其他 n-1
个特征+本来的标签: xtest
特征缺失的值:未知,我们需要预测的ytest
这种做法,对于某一个特征大量缺失,其他特征却很完整的情况,非常适用!
那如果数据中除了特征T之外,其他特征也有缺失值怎么办?
x_missing_reg = x_missing.copy()# 找出数据集中,缺失值从小到大排序的特征们的顺序# np.argsort() --- 返回从小到大排序的顺序所对应的索引sort_columns_index = np.argsort(x_missing_reg.isnull().sum()).valuessort_columns_index
for i in sort_columns_index: # 构建新的特征矩阵(没有选中填充的特征 + 原始的标签)和新标签(被选中填充的特种) df = x_missing_reg fillc = df.iloc[:,i] # 当前要填值的一列特征 --- 新标签 df = pd.concat([df.iloc[:,df.columns != i],pd.DataFrame(y_full)],axis=1) # 其余n-1列和完整标签 # 在新的特征矩阵中对含有缺失值的列进行空值填补 df_0 = SimpleImputer(missing_values=np.nan,strategy='constant',fill_value=0).fit_transform(df) # 提取出测试集、训练集 ytrain = fillc[fillc.notnull()] # 被选出来要填充的特征列中非空的数据 --- 训练标签 ytest = fillc[fillc.isnull()] # 被选出来要填充的特征列中为空的数据 --- 测试标签 xtrain = df_0[ytrain.index,:] # 在新特征矩阵中,被选出来要填充的特征的非空值所对应的记录 xtest = df_0[ytest.index,:] # 在新特征矩阵中,被选出来要填充的特征空值所对应的记录 # 使用随机森林回归来填补缺失值 rfc = RandomForestRegressor(n_estimators=100).fit(xtrain,ytrain) y_predict = rfc.predict(xtest) # 将填补好的特征返回到我们的原始特征矩阵中 x_missing_reg.loc[x_missing_reg.iloc[:,i].isnull(),i] = y_predict
原始数据集
、均值填补数据集
、0值填补数据集
、随机森林回归填补数据集
进行打分。# 对空值填补进行评估X = [x_full,x_missing_mean,x_missing_0,x_missing_reg]mse = [] # 使用均方误差进行评估for x in X: estimator = RandomForestRegressor(n_estimators=100,random_state=0) scores = cross_val_score(estimator,x,y_full,scoring='neg_mean_squared_error',cv=5).mean() mse.append(scores * -1)mse [21.62860460743544, 43.20737719157445, 47.40551717161716, 17.55283253410987]
通过评估,可以发现,利用均值
、0值
进行空值填补均方误差评分达到40以上
,而利用随机森林回归填补
竟然比原始数据集的拟合效果还要好,均方误差评分低至17.5
,当然不排除具有过拟合情况的出现。
# 可视化plt.figure(figsize=(12,8)) # 画布colors = ['r','g','b','orange'] # 颜色x_labels = ["x_full","x_missing_mean","x_missing_0","x_missing_reg"] # 标签ax = plt.subplot(111) # 添加子图for i in range(len(mse)): ax.barh(i,mse[i],color=colors[i],alpha=0.6,align='center') ax.set_title("Imputation Technique with Boston Data",color='white') # 设置标题ax.set_xlim(left=np.min(mse)*0.9,right=np.max(mse)*1.1) # 设置x轴的范围ax.set_yticks(range(len(mse)))ax.set_xlabel("MSE",color='white') # 设置x轴标签ax.set_yticklabels(x_labels) # 设置y轴刻度# 设置图形坐标轴颜色为白色plt.tick_params(axis='x',colors='white') plt.tick_params(axis='y',colors='white')plt.show()
转载地址:http://lfeq.baihongyu.com/