以房价预测为例对数据进行探究
原题是:House Prices - Advanced Regression Techniques。
本文主要关注挖掘数据中的信息,不涉及神经网络模型的构建。译改自:Comprehensive data exploration with Python - Pedro Marcelino, PhD。
1 | #invite people for the Kaggle party |
1 | Index(['Id', 'MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street', |
初步分析特征
进行深度学习前,需要先对数据有初步的了解,可以从以下几个方面入手。
- 特征:特征的名字
- 数据类型:有两类数据,离散数据与量化数据。
- 特征分类:将特征进行分类,在“房价预测”中,可以将变量分为building、space、location三类。“building”指与房子的物理特征有关(如房子的质量),“space”指与房子大小有关的特征,“location”指与房子位置有关的特征。“space”可以理解为“building”的一个特例,但因为它的种类较多,于是把它单独拿出来当了一类。
- 预期影响:我们可以凭借经验先给这些特征赋予对房价影响“高”“中”“低”的预期。我们后期可以优先重点关注这些变量的拟合情况。
在房价预测中,经过初步分析,认为以下的特征可能对房价有较高的影响。
- OverallQual(总的房屋质量,这个特征事实中难以量化qwq,但题目中有提供,其实可以利用其他的“building”特征,再建立一个模型来拟合,当然这是后话)
- YearBuilt(建造时间)
- TotalBsmtSF(总地下室面积)
- GrLivArea(总生活区域面积)
在这里的预期影响中,选择两个“building”特征,两个“space”特征。但没有“location”特征,毕竟是初步的筛选,对于这些离散的数据难以直接归纳。
分析预测结果
我们预测的是最终的房价。那么我们可以先看下房价的一些分布信息。
1 | df_train['SalePrice'].describe() |
1 | count 1460.000000 |
买房还确实是不容易
1 | sns.histplot(df_train['SalePrice'], alpha=0.4, kde=True, kde_kws={"cut": 3}) |
seaborn的这个模块实在是优雅!
通过图像:
- 房价的分布偏离了正态分布(Deviate from the normal distribution)
- 具有明显的正偏斜度(高峰偏左,长尾向右延伸,Positive skewness)
- 展示了房价的峰度(Peakedness,Kurtosis)
1 | print("Skewness: %f" % df_train['SalePrice'].skew()) |
1 | Skewness: 1.882876 |
分析特征
量化特征
我们可以从刚才分析的对房价有较高影响的特诊入手。
首先是总生活区域面积:
1 | #scatter plot grlivarea/saleprice |
很明显,总生活区域面积与房价有一定的线性相关性。
下面看看总地下室面积:
1 | #scatter plot totalbsmtsf/saleprice |
看上去总地下室面积与房价有很强的线性相关性。当然还要关注没有地下室的可能性。
离散特征
房屋质量:
1 | #box plot overallqual/saleprice |
房屋的建造时间
1 | var = 'YearBuilt' |
相关性没有那么明确,但是还是可以看出来新房子会更受欢迎,当然一些特别老的房子也可能有较高的价值。
总的来说:
- 总生活区域面积与总地下室面积与房价呈正关系。
- 房屋质量与房屋的建造时间与房价也有一定关系,房屋质量与房价关系更大。
深入分析特征
到上一步之前我们都是凭借主观想法选择了四个特征。但是主观的选择往往没有说服力,我们需要一种量化方式。
可以使用关系矩阵(Correlation matrix),将它以热力图的形式画出来。
1 | #correlation matrix |
我们观察最后一列(行),描述的是不同特征与房价的相关性,越亮代表相关性越强。
1 | #saleprice correlation matrix |
根据上面的热力图,我们得出以下的结论:
- 房屋质量(OverallQual)、总的生活区面积(GrLivArea)、总的地下空间面积(TotalBsmtSF)与房价存在很强的相关性
- 车库的容量(GarageCars)和车库大小(GarageArea)与房价存在较强的相关性,当然这两个特征之间也存在很强的相关性,面积越大的车库自然可以容纳下更多的车
- 地上总房间(TotRmsAbvGrd)和总的生活区面积(GrLivArea)有很强的相关性
- 总地下室面积 (TotalBsmtSF) 和 第一层面积(1stFloor) 也有很强的相关性
我们还可以用散点图(Scatter Plot)来描述:
1 | #scatterplot |
通过上图,我们可以很清楚地看到离散数据的一些相关性。最为明显的是总地下室面积 (TotalBsmtSF)和总的生活区面积(GrLivArea)的关系图,所有的点仿佛落在了一条线的下面。这也很好理解,因为地下空间再大,一般也不会比生活区域大,除非你把你家做成了掩体。
处理缺失的数据
对于缺失的数据有两个重要问题要思考:
- 数据的缺失有多严重?
- 数据的缺失是否存在规律?
这两个问题的答案非常重要,因为缺失数据可能导致样本数的大减。此外,从问题的实质出发,我们需要保证缺失的数据没有我们没发现的隐藏信息。
1 | #missing data |
Total | Percent | |
---|---|---|
PoolQC | 1453 | 0.995205 |
MiscFeature | 1406 | 0.963014 |
Alley | 1369 | 0.937671 |
Fence | 1179 | 0.807534 |
FireplaceQu | 690 | 0.472603 |
LotFrontage | 259 | 0.177397 |
GarageCond | 81 | 0.055479 |
GarageType | 81 | 0.055479 |
GarageYrBlt | 81 | 0.055479 |
GarageFinish | 81 | 0.055479 |
GarageQual | 81 | 0.055479 |
BsmtExposure | 38 | 0.026027 |
BsmtFinType2 | 38 | 0.026027 |
BsmtFinType1 | 37 | 0.025342 |
BsmtCond | 37 | 0.025342 |
BsmtQual | 37 | 0.025342 |
MasVnrArea | 8 | 0.005479 |
MasVnrType | 8 | 0.005479 |
Electrical | 1 | 0.000685 |
Utilities | 0 | 0.000000 |
下面是一种处理缺失数据的策略。
- 对于缺失超过15%的特征,直接删除,假装从未存在。这一类的数据,也有可能是与预测结果相关性极弱的。
- 上图中,我们可以看到与车库有关(Garage*)的数据缺失率是一致的,这就是数据缺失存在的规律,因为车库有关的1特征最重要的是“车库容量”,那么车库其他的细节信息可以删去。同样的逻辑可以应用于地下室的有关特征。
- 对于装饰石材有关特征(MasVnr*)可以认为这种特征在建造时间和房屋质量里面已经得到描述了,那么也可以删去这个特征。
- 对于电力(Electrical),仅有唯一缺失,我们删去这一样本即可,不需要删去特征。
总的来说,对于缺失较多的,在不影响大局的情况下,可以考虑直接删去这个特征。如果缺失较少,可以考虑删去样本。
1 | #dealing with missing data |
找出liars
样本中可能存在偏离中心特别多的样本,他们的存在可能会严重影响最终模型的准确性。
1 | #standardizing data |
outer range (low) of the distribution:
[[-1.83820775]
[-1.83303414]
[-1.80044422]
[-1.78282123]
[-1.77400974]
[-1.62295562]
[-1.6166617 ]
[-1.58519209]
[-1.58519209]
[-1.57269236]]
outer range (high) of the distribution:
[[3.82758058]
[4.0395221 ]
[4.49473628]
[4.70872962]
[4.728631 ]
[5.06034585]
[5.42191907]
[5.58987866]
[7.10041987]
[7.22629831]]
我们再回头看看之前的散点图
1 | #scatter plot grlivarea/saleprice |
很明显右边有两个点偏离了中心,我们可以去除。至于数据偏离的原因,可能很多,这里不过多讨论。
1 | #deleting points |
以同样的方式分析地下室空间
1 | #scatter plot totalbsmtsf/saleprice |
目测并没有严重的偏离,姑且全部保留。
为深度学习做好准备
将数据喂给深度学习网络前,我们需要将数据进行一些处理。
数据需要满足四个假设:
- 正态性(Normality):简单说,就是数据的分布“像”一个正态分布。
- 等方差(Homoscedasticity): 我们希望变量的所有值的误差项都是相同的,不会因为度量的问题导致某个特征占了主导。
- 线性性(Linearity):也可通过非线性变换转化为线性。
- 协方差为零(Absence of correlated errors):变量间不存在相关性。
正态性测试
我们关注:
- 直方图(Histogram):偏斜度(Skewness)与峰度(Kurtosis)
- 正态概率图(Normal probability plot)
1 | #histogram and normal probability plot |
由上图,可以观察到,显然房价本身并不是一个正态分布。
可以考虑进行一个log的非线性变换。
1 | #applying log transformation |
1 | #transformed histogram and normal probability plot |
非线性变换之后,就正态分布了。
看看GrLivArea如何。
1 | #histogram and normal probability plot |
尝试log非线性变换后:
1 | #data transformation |
1 | #transformed histogram and normal probability plot |
变换后,更符合正态分布了。
对于地下室空间:
1 | #histogram and normal probability plot |
地下室空间的数据可以直接使用。
现在,我们需要处理一个更难的问题:
- 出现偏斜的特征
- 出现大量“0”的特征(无法进行log非线性变换)
可以考虑保留0,其余数据进行非线性变换。这个操作可能不严谨,但是会有较好的结果。
1 | #create column for new variable (one is enough because it's a binary categorical feature) |
1 | #transform data |
1 | #histogram and normal probability plot |
等方差测试
可以用散点图来检测等方差。散点图往往呈现锥形或者钻石形。
1 | #scatter plot |
1 | plt.scatter(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice']); |
最后dummy variables
将离散的类别拆分出来。这个是深度学习的基操。
1 | df_train = pd.get_dummies(df_train) |