数据预处理和构建一个模型
数据处理与特征工程
一、数据预处理
数据预处理的主要目的是为机器学习建模进行数据准备,数据准备的好与坏对模型效果影响比较大。
经常做的一些数据预处理工作有:
> 对缺失值的处理
> 对类别型变量的值进行重新编码
> 把连续型变量进行分箱,然后再按照处理类别型变量的方式重新编码
> 对连续型变量进行标准化和归一化处理
注意事项:
- 以上的预处理工作并不是对所有问题都是必须要做的,不同的算法和问题,对预处理的要求是不一样,如:
> 缺失值对所有算法来说基本上都要处理连续型变量是否要分箱要看具体算法,决策树算法就不需要
>而标准化和归一化在涉及到距离计算的算法中一定要处理,例如 KNN,聚类分析等
1.缺失值处理
常用的三种缺失值处理方法:
- 简单粗暴,直接把有缺失值的整条记录删除。这种方法适合数据样本较大而缺失记录较少的场景,删除缺失记录对整体影响很小。
- 构造一个新变量来标记缺失值:缺失就标记为1,不缺失就标记为0。这种方法认为缺失值本身是一个有意义的信息,不能简单处理掉,必须要标记出来。
- 用一个值替换掉缺失值,具体用什么值来替换这个方法也比较多,例如对数值型变量可以考虑用均值,对类别型变量用频数最大的那个值(众数)。
(1).删除缺失记录 (使用 dropna()方法 ):
# 删除整个数据集中任何一个变量有缺失的记录
dropna = titanic_df.dropna()
dropna.info()
# 对 Age 变量删除缺失值,保存为一个新的变量
Age_dropna = titanic_df[['Age']].dropna()
Age_dropna.info()
删除缺失记录的缺点:
- 删除后只有 183 条记录了,原来可是有891条记录啊,显然这样做不行啊。能不能不要在整个数据集上删除缺失值,而是分析哪个变量就删除该变量的缺失值,当然可以的,你只需要对指定的变量进行dropna()操作就可以了。
(2).构造缺失值标识变量
# 构造一个新的变量 Age_isna,当 Age 是缺失值的时候 Age_isna=1,否则=0
titanic_df['Age_isna']=0
titanic_df.loc[titanic_df['Age'].isnull(), 'Age_isna'] = 1
# 对 Age_isna 进行频数分析,确认 Age_isna 是否构造正确
titanic_df['Age_isna'].value_counts()
(3).替换缺失值
- 以 Age 为例,我们使用均值对它进行替换。先使用mean()计算Age的均值,然后使用fillna()替换掉 Age的缺失值。
## 对年龄缺失值进行均值填充
age_mean = round(titanic_df['Age'].mean())
titanic_df['Age'].fillna(age_mean, inplace=True)
titanic_df.Age.describe()
2.类别变量重新编码
为什么要对类别变量重新编码?
- 一个最简单的理解就是:机器学习算法要求输入的变量值必须是数值
(1).什么是类别型变量
- 类别型变量 的值一般都是标签,一般都是字符串存储,例如Embarked(登船港口)它的取值是{‘C’, ‘Q’, ‘S’},这就需要把它重新编码为数值。但是能简单的使用1,2,3对C,Q,S重新编码吗?显然不能,因为 C,Q,S 之间并没有数量关系,用1,2,3来编码,算法就会认为S的值是C的值的3倍,这显然是错误的。即使有一些类别变量的标签值用数字来存储,但是同样的道理,我们不能认为它已经是数字了就不需要重新编码,不论类别变量的标签值是字符串还是数字,本质上它只是一个 编号
(2).类别变量重新编码 : 独热编码
什么是独热编码:
- 针对类别型变量,我们必须重新编码,把它们转换为数值型变量,但是信息量还不会丢失。最常用的一种类别变量编码方式 哑变量(Dummy Variables)编码,这是统计界的称呼,机器学习界喜欢叫独热编码(one-hot encoding) 。
- 独热编码就是把 1 个类别型变量转化为 N 个 0/1 标识变量。类别型变量有多少个类别值,转化后的 0/1标识变量就有多少个。例如下图示例的“地区”变量是类别型变量,进行独热编码之后,每一个地区对应一个 0/1 标识变量,用户属于哪个地区,在对应地区的 0/1 标识变量上取值为 1,否则就为 0。一条记录只会在一个变量上取值为 1,其他变量上取值为 0,这就是独热这个词的含义。
import pandas as pd
# 把 Pclass 进行独热编码,保存为新的数据对象,名字叫 Pclass_onehot,prefix 参数设置独热编码后的变量名前缀
Pclass_onehot = pd.get_dummies(titanic_df.Pclass,prefix='Pclass')
# 查看前 5 行
Pclass_onehot.head()
结果:
Pclass_1 | Pclass_2 | Pclass_3 | |
---|---|---|---|
0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 |
2 | 0 | 0 | 1 |
3 | 1 | 0 | 0 |
4 | 0 | 0 | 1 |
说明:
- Pclass 有3个标签值(1,2,3),所以独热编码之后有 3 个新的变量,设置了prefix=’Pclass’,所以三个变量名分别为 Pclass_1, Pclass_2, Pclass_3
(3).连续变量分箱
什么是分箱:
- 分箱,英文叫Binning,这是一个有点专业的叫法,其实就是我们常说的变量分段。例如 年龄本来是连续型变量,现在我们把它分成 5 个段: 0-18岁一个段, 18-25 一个段, 25-40 一个段, 40-60 一个段,60+一个段。这就叫分箱,有时候也叫分桶,还可以叫 变量离散化
分箱的好处:
- 好处一、分箱之后连续型变量就可以变成类别型变量来处理了。变成类别型变量又有什么好处呢?那就是分析起来很方便,直接做频数分析或者直方图分析就可以知道变量的分布情况了。
- 好处二、变量分箱之后能让模型变得更稳健,不太容易过拟合。为什么这么说呢,这个也是容易理解的,例如年龄,没分箱之前,不同的年龄(例如 35 岁和 39岁)对模型的预测结果有一定的影响。但是分箱之后,他们都属于 25-40 这个年龄段,预测结果自然就一样了。你可能会说分箱这样做不是降低了变量的精细度吗?对的,目的就是要降低变量的精细度,这样得到的模型结果可能才会更稳健。
有三种常用的分箱方法:
- 自定义分箱:分箱的边界值自定义
- 等宽分箱: 每一个箱的边界呈等差数列。 例如以 10 为间隔对年龄分箱,那就是按照这
样的区间来分箱: 0-10,10-20,20-30…- 等深分箱: 保证每一个箱内记录数一样或者满足指定的比例。 例如如果要分为 10 个箱,并且每个箱内的记录数要相等(即每个箱内都包含 10%的记录)。有时候等深分箱也不一定要求每一个箱内记录数都相等,例如第 1 个箱占 top10%的记录,第 2 个箱占剩余的 top20%,第 3 个箱占剩余的 top30%…
连续变量分箱 – 自定义分箱
#对 Age 进行自定义分箱
cut_points = [0,18,25,40,60,100]
#定义分箱边界值
titanic_df["Age_bin"] = pd.cut(titanic_df.Age, bins=cut_points)
#分箱后的新变量 Age_bin 继续保存在原始的数据对象 titanic_df 中
# 分箱后的频数分析
titanic_df["Age_bin"].value_counts()
连续变量分箱 – 等宽分箱
变量分箱: 等宽分箱
使用 pandas 的 cut()方法,直接指定等宽分箱的数量,例如 10,代码如下:
# 等宽分箱,分箱数为 10
titanic_df["Age_wbin"] = pd.cut(titanic_df.Age,10)
# 分箱后的频数分析
titanic_df["Age_wbin"]