本文我们使用加州住房价格数据集,从零开始,一步一步建立模型,预测每个区域的房价中位数。目的是完整实现一个机器学习的流程。
问题分析
获取数据
为了方便,我已经事先将数据下载到本地,后台回复“房价”,即可获取。该数据集以每一个街区为单位,包含街区的经纬度,居民年龄中位数,总房间数,总卧室数,人口数量,家庭数量,收入中位数,房价中位数,距离海边的描述等信息。街区在后文中也被称为区域,我们最终是要根据每个区域的其他特征预测该区域的房价中位数。
查看数据的结构
1.使用pandas读取数据,并用head()
方法查看前5行样例。可以看到一共有10个属性(特征)
2.使用info()
方法查看数据集的整体描述信息
我们可以获得以下信息:数据集一共包含20640个实例,其中total_bedroom有一些的缺失值。一共有9个float类型的属性,一个object类型的属性,ocean_proximity。
3.对于唯一一个分类属性,使用value_counts()
方法查看它的分布情况
可以看到,该属性共有5个取值,每个取值都是字符串类型,后面我们需要对其进行一定的转换。
4.对于数值属性,使用describe()
方法查看摘要,该方法默认处理数值类型的属性
结果输出了每个属性的个数count,平均值mean,标准差std,最小值min,最大值max,还有25%,50%,75%三个百分位数。需要注意这里的空值会被忽略,所以total_bedroom的count值是20433。
5.用直方图观察每个属性,了解数据的分布情况
(点击查看大图,黑色的坐标轴标签不太容易看)
以其中一个属性median_income为例,从第二行第三列的子图中我们可以看到,大多数的区域收入中位数约为2-4(单位未知)。收入超过10的区域很少。类似的可以对其他属性有一个认识。
划分训练集和测试集
以上我们对整个数据集进行了熟悉,包括数据类型,缺失情况。每个属性单独的取值等等。接下来我们首先将数据划分为训练集和测试集,为后续建立模型做铺垫。建立模型要在训练集上进行分析,而不关注任何测试集的信息,这样有助于提升模型的泛化能力。需要注意的是,在进行划分时,有纯随机抽样的方式和分层抽样的方式。
1.纯随机抽样
2.分层抽样
这里使用分层抽样需要结合实际的情境。我们从某渠道了解到,收入中位数对于预测房价的中位数有较大的影响。所以我们考虑在测试集中,该属性各取值的比例和整个数据集中接近。
由于收入是数值型的,我们首先需要将其离散化。
上面的代码将收入离散化为5类,输出了每一类的比例。接下来进行分层抽样。
分层抽样得到的start_test_set的不同收入类别的比例与原数据集几乎一致。而随机抽样的各收入类别的比例则与它们有一些差别。
划分完训练和测试集之后,可以把income_cat属性删掉(代码略)。
数据探索和和可视化
这里我们需要把测试集放在一边,只探索训练集,首先创建了一个副本。
1.探索不同的经纬度区域的数量
从图中可以看出某些经纬度(需要结合地理知识)的区域较多,某些地域较少,这样对于区域的分布有了一个直观的认识。
2.探索房价与人口和地理位置的关系规律
圆点越大代表人口越多,颜色越深代表房价越高。可以看到沿海地区房价高,人口多的地方房价高。
3.探究不同属性与房价中位数的相关性
- 相关系数法
- 分布矩阵
由于属性较多,因此只选取了与房价中位数最相关的几个。在上面的图中,对角线上展示了每个属性的直方图,其他位置展示了两两属性间的散点图。
可以发现收入的中位数与房价中位数相关性最强。
我们从图中能够发现二者的相关性,但也有几条水平的直线是”异常“值。例如房价50w,45w,28w处的直线。后续可能需要对其进行删除。
4.试验不同属性的组合
这一步我们可以从实际出发,通过现有的特征进行一些相互组合,衍生出新的特征。例如:
每个家庭的房间数=总房间数(totals_rooms)/总家庭数(housholds)
每个房间的卧室数=总的卧室数(total_bedrooms)/总的房间数(total_rooms)
每个家庭的人口数=总的人口数/总的家庭数(housholds)
验证一下我们新构造的三个特征与目标值之间的相关性系数:
可以看到新构造的属性比原来的两个属性与房价中位数的相关性更高。这一步可以帮助我们更深入的思考与预测目标相关的影响因素,帮助建立更深刻的理解。
数据准备
在开始之前,需要把预测目标和基本属性分开。
1.数据清理
- 缺失值的处理
前面我们提到total_bedrooms属性有缺失。我们可以以下方法来处理:
Scikit-Learn中提供了Imputer类来处理缺失值。使用中位数填充缺失值的代码如下:
- 删除缺失的行,可以使用pandas中的
dropna()
方法 - 删除该列,可以使用pandas中的
drop()
方法 - 用平均值或中位数填充该值,可以使用pandas中的fillna()方法
- 删除缺失的行,可以使用pandas中的
Scikit-Learn中提供了Imputer类来处理缺失值。使用中位数填充缺失值的代码如下:
由于中位数只能针对数值型属性计算,我们需要先创建一个只有数值型属性的数据副本。
imputer计算好的缺失值存储在imputer.statistics中。通过上面的步骤,我们就把total_bedrooms的缺失值用中位数进行了填充。
- 处理文本和分类属性 这里我们来处理上一步中删掉的ocean_proximity属性,它是一个类别型变量。需要将其转换为数字才能输入模型。Scikit-Learn有两种方式处理这种情况。 ① 使用LabelEncoder
encoder的classes_属性中对应的值依次被编码为0, 1, 2, 3, 4。 ② 使用 OneHotEncoder
以上两种方法最终可以得到onehot形式的矩阵。但第一种方法首先得到一个scipy的稀疏矩阵,仅存储非0元素的位置,但仍然可以像使用二维数组来使用它。在调用toarray才能得到numpy的数组。第二种方法是直接得到最终的结果。更快捷。但当特征很多的时候,numpy数组的存储会比较占空间。
- 特征缩放 为了消除数据中量纲的影响,通常有两种方式对数据进行缩放:最大最小缩放和标准化。在scikitlearn中都提供了相应的方法。 最大最小缩放是将值减去最小值并除以最大值和最小值的差,将值最终归于0-1之间。标准化缩放则是首先减去平均值然后除以方差,最终范围不一定是0-1之间。
数据准备小结
上面我们对数据进行了缺失值处理,分类变量onehot处理,特征缩放处理等。这里进行一下统一的总结。如下面代码所示。最终得到的训练集有16个特征。
对于这一环节的数据处理,sklearn中提供了pipline的方式,可以将这一系列过程流水线化。看起来更清晰。准备好了数据之后,接下来如何进行模型选取,评估,参数调整呢。这些详细的内容我们下一篇文章再来系统学习~
小结
用下面的思维导图对本文的内容串联一下,可以在后台回复“房价”获取本文的数据,代码,思维导图和PDF版本。
reference:
《机器学习实战:基于Scikit-Learn和Tensorflow》第二章