在之前的两节中,我们从特征工程基本理解讲起,逐步介绍了如何使用特征工程优化机器学习流水线,对数据集进行实际操作,以及评估和理解实际应用中出现的不同数据类型。
在分清楚数据的类型之后,我们就要着手于开始清洗和增强数据,即如何在数据集中删除和添加新的列,以及如何发现填充缺失值,所有这些操作的目标都是优化机器学习流水线。
本文主要从下面四个方面来做说明:
- 识别数据中的缺失值;
- 删除有害数据;
- 输入(填充)缺失值;
- 对数据进行归一化/标准化。
1、识别数据中的缺失值
笔者依旧是沿用了上节使用的数据集:
#我们先导入一些特征工程常用的包:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
导入数据集:
tag = pd.read_csv('F:\\招行fintech\\test_A榜.csv')
首先进行探索性数据分析(EDA, exploration data analysis)来识别缺失的值。先使用head方法观察数据的前几行。
tag.head()
上述代码输出:
现在我们来看看数据集中是否有数据点是空的(缺失值)。用pandas DataFrame内置的isnull()的方法:
可以很明显的看到,有四列是存在缺失值的。当然,这是非常理想的情况,我这边使用另一个数据集来说明一下一种特殊情况问题:
导入数据集:
tag = pd.read_csv('F:\\招行fintech\\附件1.csv')
我们使用head来观察数据,输入结果如下:
可以明显看到,此数据集中出现了若干如‘0’,‘?’的数据,这些数据按照惯例应该是问题数据。然而当我们再使用isnull语句时:
我们发现,系统并没有将‘0’以及‘?’识别为缺失值,但在实际情况中,这些数据明显是错误的,依旧需要进行更正!
其实这些都是我们在拿到数据集时可能会遇到的问题,如果数据集没有文档说明,缺失值常见的填充方法有如下几种:
- 0(数值型)
- unknown 或 Unknown (类别型)
- ?(类别型)
当我们知道数据集中出现了这些缺失值之后,我们就可以深入研究如何去解决这些缺失值。
2、处理数据集中的缺失值
我们接下来就要讨论如何处理这些缺失值。最主要的办法就以下两种:
- 删除缺少值的行;
- 填充缺失值。
这两种办法都会清洗我们的数据集,让算法可以处理,但是每种办法都各有优缺点。
我们可以先用python中的None填充所有的数字‘0’和‘?’,这样pandas的fillna和fropna方法就可以正常工作,我们沿用第二个数据集做处理,操作如下:
#直接对所有列进行操作
col_0 = ['X1', 'X2', 'X3', 'X4',
'X5', 'X6', 'X7']
for col in col_0:
tag[col].replace(to_replace=['0'],value=[None],inplace=True)
tag[col].replace(to_replace=['?'], value=[None], inplace=True)
现在我们使用isnull方法计算一下缺失值数量,应该可以看到正确的结果:
现在的数据前几行应该如下图所示:
现在数据就有意义多了,我们已经将缺失值正确插入了数据集,确实数据不再是原来的占位符0。
2.1、删除有害的行
在处理数据的两种办法中,我们最常用的方法大概就是直接删除掉存在缺失值的行。可以在pandas中利用dropna方法,接下来我们使用比较常见的数据集,即皮马印第安人糖尿病数据集:
删除存在缺失值的行:
tag_dropped = tag.dropna()
我们来检查一下我们丢失了多少行:
num_rows_lost = round(100*(tag.shape[0]- tag_dropped.shape[0])/float(tag.shape[0]))
print("retained {}% of rows".format(num_rows_lost))
结果显示:
我们丢失了原始数据大约44%的行!
我们针对此数据集在做进一步的探索性数据分析:
在转换之后,二元响应并没有什么变化,那我们再来看看数据的形状:
每列的均值(不算缺失值):
每列的均值(删除缺失值):
为了更好的看到这些数的变化,我们利用均值变化的百分比做可视化:
ax = ((tag_dropped.mean() - tag.mean()) / tag.mean()).plot(kind='bar', title = '%change in average column values')
ax.set_ylabel('% change')
plt.show()
结果图如下:
可以从上看到,times_pregnant(怀孕次数)的均值在删除缺失值后下降了14%,变化很大!如此可知,删除行对于数据的形状改变还是极大的,所以我们应该尽可能保留更多的数据。
2.2、填充缺失值
填充数据是处理缺失数据的一种更复杂的方法。最常用的便是均值填充,我们先来看看缺失总数:
我们选取Glucose来看看这些缺失值:
我们现在就选用fillna方法,将这一列所有的None填充为该列其余值均值:
tag['Glucose'].fillna(tag['Glucose'].mean(), inplace=True)
这样一来该列应该不存在缺失值,我们再用isnull方法来查看一下:
果然已经没有了缺失项,我们再来查看一下:
果然!缺失值已经被均值取代了!当然我们也可以用0,以及其他值来填充,我们可以使用KNN交叉验证准确率方法来验证一下用各种值取代缺失项的准确率:
knn_params = {'classify__n_neighbors':[1,2,3,4,5,6,7]}
knn = KNeighborsClassifier()
mean_impute = Pipeline([('impute', SimpleImputer(strategy='mean')),('classify', knn)])
X = tag.drop('Outcome', axis = 1)
Y = tag['Outcome']
grid = GridSearchCV(mean_impute, knn_params)
grid.fit(X,Y)
print(grid.best_score_, grid.best_params_)
最后结果统计:
3、标准化和归一化
到目前为止,我们已经知道了如何识别数据类型,如何识别缺失值,以及如何处理缺失值。那么接下来我们将继续讨论,如何进一步优化我们的数据集,以进一步增强机器学习流水线结果,我们接下来看一看数据的分布情况:
我们可以注意到,每列的均值、最小值、最大值和标准差差值都很大,这就关系到机器学习模型中数据尺度的影响问题了,只有当所有特征的数值变化区间差别不大,才能达到机器学习算法模型的最优状态。
所以我们必须对数据做归一化,下列有三种方法:
- z分数标准化
- min-max标准化
- 行归一化
3.1、z分数标准化
z分数标准化公式:
在这个公式里:
- z是新的值
- x是单元格原来的值
- μ是该列的均值
- 是列的标准差
我们继续以Glucose列为例,我们先来看看未z分数标准化前的均值和标准差:
tag['Glucose'].mean()
tag['Glucose'].std()
结果为:
可视化:
现在我们应用z分数标准化,代码如下:
scaler = StandardScaler()
glucose_z_score_standardized = scaler.fit_transform(tag[['Glucose']])
print(glucose_z_score_standardized.mean())
print(glucose_z_score_standardized.std())
结果如下:
需要注意的是,在这一步我们需要先把缺失值填上。可视化:
ax = pd.Series(glucose_z_score_standardized.reshape(-1,)).hist()
ax.set_title('Distribution of plasma_glucose_concentration after Z Score Scaling')
结果如下:
我们观察到x轴更紧密了,y轴则没有变化,如此,我们就完成了z分数标准化。
3.2、min-max标准化
接下来的两种标准化方式我就不做太多赘述了,直接上代码:
min_max = MinMaxScaler()
tag_min_max = pd.DataFrame(min_max.fit_transform(tag_imputed),
columns='Glucose')
3.3、行归一化
normalize = Normalizer()
tag_normalized = pd.DataFrame(normalize.fit_transform(tag_imputed),
columns='Glucose')
最后我们做个总结: