视频作者:菜菜TsaiTsai
链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili
接下来,我们总算可以开始处理我们的缺失值了。首先我们要注意到,由于我们的特征矩阵由两种类型的数据组成:分类型和连续型,因此我们必须对两种数据采用不同的填补缺失值策略。传统地,如果是分类型特征,我们则采用众数进行填补。如果是连续型特征,我们则采用均值来填补。
此时,由于我们已经分了训练集和测试集,我们需要使用训练集上的众数对训练集和测试集都进行填补。为什么会这样呢?按道理说就算用测试集上的众数对测试集进行填补,也不会使测试集数据进入我们建好的模型,不会给模型透露一些信息。然而,在现实中,我们的测试集未必是很多条数据,也许我们的测试集只有一条数据,而某个特征上是空值,此时此刻测试集本身的众数根本不存在。因此为了避免这种尴尬的情况发生,我们假设测试集和训练集的数据分布和性质都是相似的,因此我们统一使用训练集的众数和均值来对测试集进行填补。
在sklearn当中,即便是我们的填补缺失值的类也需要由实例化,fit和接口调用执行填补三个步骤来进行,而这种分割其实一部分也是为了满足我们使用训练集的建模结果来填补测试集的需求。我们只需要实例化后,使用训练集进行fit,然后在调用接口执行填补时用训练集fit后的结果分别来填补测试集和训练集就可以了。
# 首先找出分类型特征都有哪些
Xtrain.dtypes # 一般来说分类型特征的类型都是object
---
Month int64
Climate object
MinTemp float64
MaxTemp float64
Rainfall float64
……
Raintoday object
dtype: object
cate = Xtrain.columns[Xtrain.dtypes == "object"].tolist()
cate # category
---
['Climate', 'WindGustDir', 'WindDir9am', 'WindDir3pm', 'Raintoday']
# 除了特征类型为‘object’的特征们,还有虽然用数字表示,但本质为分类型特征的云层遮蔽程度
cloud = ['Cloud9am','Cloud3pm']
cate = cate + cloud
cate
---
['Climate',
'WindGustDir',
'WindDir9am',
'WindDir3pm',
'Raintoday',
'Cloud9am',
'Cloud3pm']
# 对于分类型特征,使用众数来进行填补
from sklearn.impute import SimpleImputer
si = SimpleImputer(missing_values=np.nan,strategy="most_frequent")
# 注意,我们使用训练集数据来训练我们的填补器,本质是生成训练集中的众数
si.fit(Xtrain.loc[:,cate])
# 然后我们用训练集中的众数同时填补训练集和测试集
Xtrain.loc[:,cate] = si.transform(Xtrain.loc[:,cate])
Xtest.loc[:,cate] = si.transform(Xtest.loc[:,cate])
Xtrain.loc[:,cate].isnull().mean()
---
Climate 0.0
WindGustDir 0.0
WindDir9am 0.0
WindDir3pm 0.0
Raintoday 0.0
Cloud9am 0.0
Cloud3pm 0.0
dtype: float64
在编码中,和我们的填补缺失值一样,我们也是需要先用训练集fit模型,本质是将训练集中已经存在的类别转换成是数字,然后我们再使用接口transform分别在测试集和训练集上来编码我们的特征矩阵。
当我们使用接口在测试集上进行编码的时候,如果测试集上出现了训练集中从未出现过的类别,那代码就会报错,表示说“我没有见过这个类别,我无法对这个类别进行编码”,此时此刻你就要思考,你的测试集上或许存在异常值,错误值,或者的确有一个新的类别出现了,而你曾经的训练数据中并没有这个类别。以此为基础,你需要调整你的模型。
# 只有分类型变量才能编码,连续型的不能编码
# 所有的分裂型编码为数字,一个类别是一个数字
from sklearn.preprocessing import OrdinalEncoder # 只允许二维以上的数据进行输入
# 可以选择哑变量也可以选择普通的编码
# 如果哑变量会带来太多的特征,就先试试普通的编码,效果不好,再换
# 这里为了方便就是用普通的编码
oe = OrdinalEncoder()
# 利用训练集进行fit
oe = oe.fit(Xtrain.loc[:,cate])
# 用训练集的编码结果来编码训练和测试特征矩阵
# 在这里如果测试特征矩阵报错,就说明测试集中出现了训练集中从未见过的类别
Xtrain.loc[:,cate] = oe.transform(Xtrain.loc[:,cate])
Xtest.loc[:,cate] = oe.transform(Xtest.loc[:,cate])
Xtrain.loc[:,cate].head()
---
Climate WindGustDir WindDir9am WindDir3pm Raintoday Cloud9am Cloud3pm
0 1.0 2.0 6.0 0.0 0.0 0.0 7.0
1 0.0 6.0 4.0 6.0 0.0 7.0 7.0
2 4.0 13.0 4.0 0.0 0.0 1.0 3.0
连续型变量的缺失值由均值来进行填补。连续型变量往往已经是数字,无需进行编码转换。与分类型变量中一样,我们也是使用训练集上的均值对测试集进行填补。
当然了,在算法比赛中,我们可以穷尽一切我们能够想到的办法来填补缺失值以追求让模型的效果更好,不过现实中,除了模型效果之外,我们还要追求可解释性。
col = Xtrain.columns.tolist()
for i in cate:
col.remove(i)
col
---
['Month',
'MinTemp',
'MaxTemp',
……
'Temp3pm']
# 均值填补连续型变量
impmean = SimpleImputer(missing_values=np.nan,strategy="mean")
impmean = impmean.fit(Xtrain.loc[:,col])
Xtrain.loc[:,col] = impmean.transform(Xtrain.loc[:,col])
Xtest.loc[:,col] = impmean.transform(Xtest.loc[:,col])
Xtrain.isnull().mean() # 缺失值填补完毕
---
Month 0.0
Climate 0.0
MinTemp 0.0
……
dtype: float64
数据的无量纲化是SVM执行前的重要步骤,因此我们需要对数据进行无量纲化。但注意,这个操作我们不对分类型变量进行。
# 连续型变量需要无量纲化,但注意,分类型变量不需要进行无量纲化,这个操作没有意义
col.remove("Month") # 其实Month也是分类型变量,但由于没有空值,所以之前不在cate里也不影响,现在要去掉
col
---
['MinTemp',
'MaxTemp',
'Rainfall',
'Evaporation',
'Sunshine',
'WindGustSpeed',
'WindSpeed9am',
'WindSpeed3pm',
'Humidity9am',
'Humidity3pm',
'Pressure9am',
'Pressure3pm',
'Temp9am',
'Temp3pm']
from sklearn.preprocessing import StandardScaler # 数据转换为均值为0,方差为1的数据
# 标准化不会改变数据分布,不会把数据变成正态分布
ss = StandardScaler()
ss = ss.fit(Xtrain.loc[:,col])
Xtrain.loc[:,col] = ss.transform(Xtrain.loc[:,col])
Xtest.loc[:,col] = ss.transform(Xtest.loc[:,col])
特征工程到这里就全部结束了。将数据处理完毕之后,建议大家都使用to_csv来保存我们已经处理好的数据集,避免我们在后续建模过程中出现覆盖了原有的数据集的失误后,需要从头开始做数据预处理。在开始建模之前,务必保存好处理好的数据,然后在建模的时候,重新将数据导入。
剩了一点结尾,会在KMeans之后补上