使用实际情况中的数据进行机器学习时,通常会遇到如下两个方面的问题:
通过数据预处理使得数据适应模型的需求。sklearn
中进行数据预处理的模块包括如下两种:
preprocessing
:几乎包含数据预处理的所需要的所有函数;Impute
:专用的缺失值填充工具。数据的无量纲化是将不同规格的数据转换为同一规格,或不同分布的数据转换为特定分布的过程。对于以梯度和矩阵为核心的算法(比如,逻辑回归、支持向量机、神经网络等)中,数据的无量纲化可以加快模型的收敛速度;而对于基于距离的模型(比如,KNN、K-means聚类等),数据的无量纲化可以提高模型的预测精度。
最常用的无量纲化处理方法包括数据归一化处理与标准化处理两种:
preprocessing.MinMaxScaler
:数据归一化处理数据归一化处理(Normalization
,又称为Min-Max Scaling
):即对数据按照最小值中心化后,再按照极差(最大值-最小值)缩放后,数据就会缩放到[0 1]
区间内。
在sklearn
中我们可以使用preprocessing.MinMaxScaler
方法来实现数据的归一化处理。MinMaxScaler
有一个和总要的参数feature_range
,其用来控制数据要所的区间。
下面给出一个简单示例以直观地了解preprocessing.MinMaxScaler
归一化函数的使用方法:
from sklearn.preprocessing import MinMaxScaler
data = [[-3, 4], [-1, 7], [-0.3, 5], [0, 4], [4, 6]]
print("data数据归一化之前的结果:\n{}".format(data))
# 数据归一化处理
scaler = MinMaxScaler() # 实例化归一化方法
scaler = scaler.fit(data) # 对数据data进行归一化处理
result = scaler.transform(data) # 归一化结果
print("\ndata数据归一化之后的结果:\n{}".format(result))
# 由归一化后的数据转换为原始数据
result_inverse = scaler.inverse_transform(result)
print("\n由数据归一化之后的结果转换为原始数据:\n{}".format(result_inverse))
代码执行结果如下图所示:
preprocessing.StandardScaler
:标准化处理数据标准化处理(Standardization
,又称为Z-score normalization
):即对数据按照均值中心化后,再按照标准差缩放,将数据转换为服从标准正态分布的数据。
在sklearn
中我们可以使用preprocessing.StandardScaler
方法来实现数据的标准化处理。
下面给出其简单的示例:
from sklearn.preprocessing import StandardScaler
data = [[-3, 4], [-1, 7], [-0.3, 5], [0, 4], [4, 6]]
print("data数据标准化之前的结果:\n{}".format(data))
# 数据标准化处理
scaler = StandardScaler() # 实例化标准化方法
scaler = scaler.fit(data) # 对数据data进行标准化处理
result = scaler.transform(data) # 标准化后的结果
print("\ndata数据标准化之后的结果:\n{}".format(result))
# 由标准化后的数据转换为原始数据
result_inverse = scaler.inverse_transform(result)
print("\n由数据标准化之后的结果转换为原始数据:\n{}".format(result_inverse))
代码执行结果如下图所示:
!! ✨ 注意:
StandardScaler
默认的是对列操作,我们可以通过下面的命令查看需要标准化数据的列均值与列方差:
print("标准化的列平均值:", scaler.mean_)
print("\n标准化的列方差值:", scaler.var_)
输出如下所示:
在实际的数据处理中,缺失值处理是最为重要的内容之一。
这里使用的数据集可以通过百度网盘链接下载,链接为:🗃️ 链接: https://pan.baidu.com/s/1oD4eZ_mnUj6eeoZhIaiNbQ?pwd=ixx8,提取码: ixx8。
下载好数据集将数据集放入到当前目录下,另外为了简化内容的介绍,首先通过下面的代码对数据集进行简化处理:
import pandas as pd
# 数据集简化
# (1) 导入数据集并查看数据集信息
train_data = pd.read_csv(
'./data_prepro/train.csv', # 数据集的位置
index_col=0,
)
train_data.head()
此时读取的原始数据如下图所示:
下面通过下面的代码只留下Age
、Sex
、Embarked
与Survived
列,并将其顺序修改为[Age, Sex, Embarked, Survived]
:
# (2) 只留下Age、Sex、Embarked与Survived列
train_data.drop(
[
"PassengerId", "Pclass", "Name", "SibSp",
"Parch", "Ticket", "Fare", "Cabin"
], # 需要删除的列
inplace=True, # 替换原始数据train_data
axis=1 # 对列进行操作
)
# (3) 对列进行重新排序:[Survived, Sex, Age, Embarked] --> [Age, Sex, Embarked, Survived]
train_data = train_data.reindex(
columns=['Age', 'Sex', 'Embarked', 'Survived']
)
train_data.head()
代码执行结果如下图所示:
至此,我们就得到了下面两个部分需要使用的数据集train_data
。
impute.SimpleImputer
方法的缺失值处理SimpleImputer
的调用方法如下所示:
class sklearn.impute.SimpleImputer(
missing_values=nan,
strategy='mean',
fill_value=None,
copy=True
)
其主要的四个参数的含义如下表所示:
参数 | 功能 |
---|---|
missing_values | 告诉SimpleImputer缺失值为什么类型,默认为np.nan |
strategy | 默认填充策略为均值填充 输入mean使用均值填充(仅对数值型特征可用) 输入median使用中值填充(仅对数值型特征可用) 输入most_frequent使用众数填充(对数值型与字符型特征均可用) 输入constant表示请参考参数fill_value中的值(对数值型与字符型特征均可用) |
fill_value | 当参数strategy为constant时,可输入字符串或数字表示要填充的值,通常使用 |
copy | 默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原来的特征矩阵中去 |
首先查看train_data
数据的统计信息:
train_data.info()
由上图的data_train
统计信息可以看出,共有891行数据,其中Age
属性只有714行具有真实数据,大概有200行缺失值;而Embarked
属性具有889行真实数据,只有两行缺失值。
下面使用SimpleImputer
来处理Age
与Embarked
的缺失值。首先对Age
属性进行中位数填充:
代码如下所示:
# (1) 年龄属性Age的缺失值中位数填充
# 由于sklearn中能够处理的数据为矩阵,下面得到年龄矩阵
Age = train_data.loc[:, "Age"].values.reshape(-1,1)
# 下面使用SimpleImputer来对Age属性的缺失值进行处理
from sklearn.impute import SimpleImputer
# 实例化一个缺失值处理的对象,其填充方法使用特征的中位数填充策略
imp_median = SimpleImputer(strategy='median')
# 对年龄属性进行缺失值的中位数填充
Age_imp = imp_median.fit_transform(Age)
# 将填充后的Age_imp替换到原始的数据中
train_data.loc[:, "Age"] = Age_imp
# 此时再查看Age的属性信息
train_data.loc[:, "Age"].info()
由此可以看出年龄属性Age
已经填充为891行数值型数据了。
接着,使用SimpleImputer
函数的众数填充方法most_frequent
,对登船港口属性Embarked
的缺失值进行填充,代码如下所示:
# (2) 登船港口属性Embarked的缺失值众数填充
# 由于sklearn中能够处理的数据为矩阵,下面得到Embarked矩阵
Embarked = train_data.loc[:, "Embarked"].values.reshape(-1,1)
# 下面使用SimpleImputer来对Embarked属性的缺失值进行处理
from sklearn.impute import SimpleImputer
# 实例化一个缺失值处理的对象,其填充方法使用特征的众数填充策略
imp_freq = SimpleImputer(strategy='most_frequent')
# 对年龄属性进行缺失值的众数填充
Embarked_imp = imp_freq.fit_transform(Embarked)
# 将填充后的Embarked_imp替换到原始的数据中
train_data.loc[:, "Embarked"] = Embarked_imp
# 此时再查看Embarked的属性信息
train_data.loc[:, "Embarked"].info()
由图可以看出缺失值已经填充完成。我们还可以使用如下命令查看train_data
数据中是否具有缺失值:
train_data.isnull().any()
由此可以看出train_data
中已经没有缺失值了。
对于大多数机器学习算法,比如逻辑回归、SVM、KNN登算法,它们只能处理数值型数据,而不能处理文字。且在sklearn
中除了专门处理文字的算法,在使用fit
时需要导入数值型数据。
因此,在使用sklearn
的机器学习算法时,通常需要对非数值型数据进行编码,以实现将文字型数据转换为数值型数据。sklearn
中常用的编码函数包括:
preprocessing.LabelEncoder
:标签专用,用于将分类标签转换为分类数值;preprocessing.OneHotEncoder
:特征常用,用于将分类特征转换为分类数值。1. preprocessing.LabelEncoder
:标签专用(目标值),用于将分类标签转换为分类数值
sklearn
中的preprocessing.LabelEncoder
方法可以十分方便地将文字型标签转换为分类数值。如下图所示,train_data
数据的标签Survived
为bool
类型:
下面使用LabelEncoder
方法将其转换为分类数值型数据,代码如下所示:
from sklearn.preprocessing import LabelEncoder
# sklearn中对于标签的处理可以是列表或者Series,所以这里不需要转换为矩阵
y = train_data.iloc[:, -1]
le = LabelEncoder() # 实例化一个标签编码对象
le = le.fit(y) # 导入需要处理的标签
label = le.transform(y) # 获取编码后的数值分类标签
# 查看转换后的数值分类标签label结果
print("转换后的数值分类标签结果为:\n{}\n".format(label))
# 我们可以通过标签编码对象le的classes_属性查看标签中具有多少类别
print("原始标签中具有的类别:\n{}.\n".format(le.classes_))
# 将转换后的label标签替换到train_data中
train_data.loc[:, "Survived"] = label
train_data.head()
由上图可以看出,标签数据Survived
已经转换为分类数值型数据。
2. preprocessing.OneHotEncoder
:特征常用,用于将分类特征转换为分类数值
比如train_data
数据中的Sex
与Embarked
属性均为文本型特征数据,下面使用OneHotEncoder
方法将其转换为分类数值特征,代码如下所示:
from sklearn.preprocessing import OneHotEncoder
import numpy as np
# 获取需要转换的特征数据,并将其转换为二维矩阵
Sex = np.array(train_data.loc[:, "Sex"]).reshape(-1,1)
Embarked = np.array(train_data.loc[:, "Embarked"]).reshape(-1,1)
# 导入需要进行编码处理的特征数据
enc_sex = OneHotEncoder(categories='auto').fit(Sex)
enc_embark = OneHotEncoder(categories='auto').fit(Embarked)
# 得到独热码的编码结果
Sex_result = enc_sex.transform(Sex).toarray()
Embarked_result = enc_embark.transform(Embarked).toarray()
# 将两者横向拼接,进而将其拼接到train_data中
Sex_Embarked_New = pd.concat(
[pd.DataFrame(Sex_result), pd.DataFrame(Embarked_result)], axis=1
)
train_data = pd.concat(
[train_data, Sex_Embarked_New],
axis=1
)
train_data.drop(["Sex", "Embarked"], axis=1, inplace=True)
# 查看性别属性与登船港口的类别名,将其作为列名
Sex_col_names = enc_sex.get_feature_names()
Embarked_col_names = enc_embark.get_feature_names()
# 修改新添加的列的列名
train_data.columns = [
"Age", "Survived", *Sex_col_names.tolist(), *Embarked_col_names.tolist()
]
train_data.head()
编码后的结果如下图所示: