kaggle 房价预测

背景

Kaggle的房价预测竞赛目前已有4000多人参与比赛,基于给出的80个特征,对房屋的售价进行了的预测。这里基于目前得票数最高的优胜方案:《用Python进行全面数据探索》,该方案在数据探索,特征工程上都有十分出色的表现。

作者Pedro Marcelino 在竞赛中使用的主要方法是关注数据科学处理方法,以及寻找能够指导工作的有力文献资料。作者主要参考《多元数据分析》(‘Multivariate Data Analysis’ ,Hair et al., 2014)中的第三章“检查你的数据”。作者将自己研究的方法归为以下三步:
a.定义要解决的问题
b. 查阅相关文献
c. 对他们进行修改以适应自己的要求

数据处理方法框架:
理解问题:查看每个变量并且根据他们的意义和对问题的重要性进行分析
单因素研究:只关注因变量(‘SalePrice’),并且进行更深入的了解
多因素研究:分析因变量和自变量之间的关系
基础清洗:清洗数据集并且对缺失数据,异常值和分类数据进行一些处理
检验假设:检查数据是否和多元分析方法的假设达到一致

导入模块和数据

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from scipy import stats
import warnings
warnings.filterwarnings('ignore')

%matplotlib inline

导入训练集数据

df_train = pd.read_csv('train.csv')

查看训练集数据属性,一个81个属性

df_train.columns
Index(['Id', 'MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street',
       'Alley', 'LotShape', 'LandContour', 'Utilities', 'LotConfig',
       'LandSlope', 'Neighborhood', 'Condition1', 'Condition2', 'BldgType',
       'HouseStyle', 'OverallQual', 'OverallCond', 'YearBuilt', 'YearRemodAdd',
       'RoofStyle', 'RoofMatl', 'Exterior1st', 'Exterior2nd', 'MasVnrType',
       'MasVnrArea', 'ExterQual', 'ExterCond', 'Foundation', 'BsmtQual',
       'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinSF1',
       'BsmtFinType2', 'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF', 'Heating',
       'HeatingQC', 'CentralAir', 'Electrical', '1stFlrSF', '2ndFlrSF',
       'LowQualFinSF', 'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath',
       'HalfBath', 'BedroomAbvGr', 'KitchenAbvGr', 'KitchenQual',
       'TotRmsAbvGrd', 'Functional', 'Fireplaces', 'FireplaceQu', 'GarageType',
       'GarageYrBlt', 'GarageFinish', 'GarageCars', 'GarageArea', 'GarageQual',
       'GarageCond', 'PavedDrive', 'WoodDeckSF', 'OpenPorchSF',
       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC',
       'Fence', 'MiscFeature', 'MiscVal', 'MoSold', 'YrSold', 'SaleType',
       'SaleCondition', 'SalePrice'],
      dtype='object')

查看因变量SalePrice

df_train['SalePrice'].describe()
count      1460.000000
mean     180921.195890
std       79442.502883
min       34900.000000
25%      129975.000000
50%      163000.000000
75%      214000.000000
max      755000.000000
Name: SalePrice, dtype: float64

准备工作——“我们可以期望什么?”

为了了解数据,我们可以分析每个变量并且尝试理解他们的意义和与该问题的相关程度。

首先建立一个Excel电子表格,有如下目录:

变量 – 变量名
类型 – 该变量的类型。这一栏只有两个可能值,“数据”或“类别”。 “数据”是指该变量的值是数字,“类别”指该变量的值是类别标签。
划分 – 指示变量划分. 我们定义了三种划分:建筑,空间,位置。
期望 – 我们希望该变量对房价的影响程度。我们使用类别标签“高”,“中”和“低”作为可能值。
结论 – 我们得出的该变量的重要性的结论。在大概浏览数据之后,我们认为这一栏和“期望”的值基本一致。
评论 – 我们看到的所有一般性评论。

我们首先阅读了每一个变量的描述文件,同时思考这三个问题:

我们买房子的时候会考虑这个因素吗?
如果考虑的话,这个因素的重要程度如何?
这个因素带来的信息在其他因素中出现过吗?
我们根据以上内容填好了电子表格,并且仔细观察了“高期望”的变量。然后绘制了这些变量和房价之间的散点图,填在了“结论”那一栏,也正巧就是对我们的期望值的校正。

我们总结出了四个对该问题起到至关重要的作用的变量:

OverallQual
YearBuilt.
TotalBsmtSF.
GrLivArea.

训练集SalePrice属性的分析

sns.distplot(df_train['SalePrice'])

在这里插入图片描述

从直方图中可以看出:

偏离正态分布
数据正偏
有峰值

数据偏度和峰度度量:

print('Skewness: %f' % df_train['SalePrice'].skew())
print('Kurtosis: %f' % df_train['SalePrice'].kurt())
Skewness: 1.882876
Kurtosis: 6.536282

训练集相关变量分析

训练集GrLivArea属性和Saleprice属性相关性分析

GrLivArea:高于(地面)居住面积的平方英尺,居住面积肯定和房价关系很大了

var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'],df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000))

可以看出’’SalePrice’和’’GrLivArea’ 关系很密切,并且基本呈线性关系。有两个异常点
在这里插入图片描述

训练集TotalBsmtSF属性和Saleprice属性相关性分析

TotalBsmtSF :地下室总面积

var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000))

TotalBsmtSF’ 和 ‘SalePrice’关系也很密切,从图中可以看出基本呈线性关系,但从最左侧的点可以看出特定情况下’TotalBsmtSF’ 对’SalePrice’ 没有产生影响。(???)
在这里插入图片描述

训练集OverallQual属性和Saleprice属性相关性分析

OverallQual :给房屋整体材料和装修评分

var = 'OverallQual'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000)
# data.describe()

可以看出’SalePrice’ 与 ‘OverallQual’分布趋势相同

(-0.5, 9.5, 0, 800000)

在这里插入图片描述

训练集YearBuilt属性和Saleprice属性相关性分析

YearBuilt: 建造日期

var = 'YearBuilt'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(12, 9))
fig = sns.boxplot(x=var, y='SalePrice', data=data)
fig.axis(ymin=0, ymax=800000)
plt.xticks(rotation=90)
(array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
         13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
         26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
         39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
         52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
         65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
         78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
         91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
        104, 105, 106, 107, 108, 109, 110, 111]),
 <a list of 112 Text xticklabel objects>)

YearBuilt和Saleprice之间的关系没有OverallQual和Saleprice之间的关系趋势性那么趋同,但是可以看出建筑时间较短的房屋价格更高
在这里插入图片描述

总结:

‘GrLivArea’ 和 ‘TotalBsmtSF’ 与 ‘SalePrice’似乎线性相关,并且都是正相关。 ‘TotalBsmtSF’线性关系的斜率十分的高。
‘OverallQual’ 和 ‘YearBuilt’ 与 ‘SalePrice’也有关系。’OverallQual’的相关性更强, 箱型图显示了随着整体质量的增长,房价的增长趋势。
我们只分析了四个变量,但是还有许多其他变量我们也应该分析,这里的技巧在于选择正确的特征(特征选择)而不是定义他们之间的复杂关系(特征工程)。

相关系数分析

corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True)

首先两个红色的方块吸引到了我,第一个是’TotalBsmtSF’ 和’1stFlrSF’ 变量的相关系数,第二个是 ‘Garage X ‘ 变量群。这两个示例都显示了这些变量之间很强的相关性。实际上,相关性的程度达到了一种多重共线性的情况。我们可以总结出这些变量几乎包含相同的信息,所以确实出现了多重共线性。

另一个引起注意的地方是 ‘SalePrice’ 的相关性。我们可以看到我们之前分析的 ‘GrLivArea’,’TotalBsmtSF’,和 ‘OverallQual’的相关性很强,除此之外也有很多其他的变量应该进行考虑,这也是我们下一步的内容。
属性和Saleprice相关系数越高则表示越重要,当然需要排除共线性属性
在这里插入图片描述

和Saleprice相关前10的属性热图

k = 10
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values, xticklabels=cols.values )
plt.show()

从图中可以看出:

‘OverallQual’, ‘GrLivArea’ 以及 ‘TotalBsmtSF’ 与 ‘SalePrice’有很强的相关性。
‘GarageCars’ 和 ‘GarageArea’ 也是相关性比较强的变量. 车库中存储的车的数量是由车库的面积决定的,它们就像双胞胎,所以不需要专门区分’GarageCars’ 和 ‘GarageArea’ ,所以我们只需要其中的一个变量。这里我们选择了’GarageCars’因为它与’SalePrice’ 的相关性更高一些。
‘TotalBsmtSF’ 和 ‘1stFloor’ 与上述情况相同,我们选择 ‘TotalBsmtSF’ 。
‘FullBath’几乎不需要考虑。
‘TotRmsAbvGrd’ 和 ‘GrLivArea’也是变量中的双胞胎。
‘YearBuilt’ 和 ‘SalePrice’相关性似乎不强。
在这里插入图片描述

’SalePrice’ 和相关变量之间的散点图

sns.set()
cols =  ['SalePrice', 'OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size=2.5)
plt.show()

尽管我们已经知道了一些主要特征,这一丰富的散点图给了我们一个关于变量关系的合理想法。
其中,’TotalBsmtSF’ 和 ‘GrLiveArea’之间的散点图是很有意思的。我们可以看出这幅图中,一些点组成了线,就像边界一样。大部分点都分布在那条线下面,这也是可以解释的。地下室面积和地上居住面积可以相等,但是一般情况下不会希望有一个比地上居住面积还大的地下室。
‘SalePrice’ 和’YearBuilt’ 之间的散点图也值得我们思考。在“点云”的底部,我们可以观察到一个几乎呈指数函数的分布。我们也可以看到“点云”的上端也基本呈同样的分布趋势。并且可以注意到,近几年的点有超过这个上端的趋势。
在这里插入图片描述

缺失数据

关于缺失数据需要思考的重要问题:
这一缺失数据的普遍性如何?
缺失数据是随机的还是有律可循?
这些问题的答案是很重要的,因为缺失数据意味着样本大小的缩减,这会阻止我们的分析进程。除此之外,以实质性的角度来说,我们需要保证对缺失数据的处理不会出现偏离或隐藏任何难以忽视的真相

total = df_train.isnull().sum().sort_values(ascending=False)
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)
TotalPercent
PoolQC14530.995205
MiscFeature14060.963014
Alley13690.937671
Fence11790.807534
FireplaceQu6900.472603
LotFrontage2590.177397
GarageCond810.055479
GarageType810.055479
GarageYrBlt810.055479
GarageFinish810.055479
GarageQual810.055479
BsmtExposure380.026027
BsmtFinType2380.026027
BsmtFinType1370.025342
BsmtCond370.025342
BsmtQual370.025342
MasVnrArea80.005479
MasVnrType80.005479
Electrical10.000685
Utilities00.000000
df_train = df_train.drop((missing_data[missing_data['Total'] > 1]).index, 1)
df_train = df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)
df_train.isnull().sum().max()

当超过**15%**的数据都缺失的时候,我们应该删掉相关变量且假设该变量并不存在。
根据这一条,一系列变量都应该删掉,例如’PoolQC’, ‘MiscFeature’, ‘Alley’等等,这些变量都不是很重要,因为他们基本都不是我们买房子时会考虑的因素。
‘Garage X ‘ 变量群的缺失数据量都相同,由于关于车库的最重要的信息都可以由’GarageCars’ 表达,并且这些数据只占缺失数据的5%,我们也会删除上述的’Garage X ‘ 变量群。同样的逻辑也适用于 ‘Bsmt X ‘ 变量群。
对于 ‘MasVnrArea’ 和 ‘MasVnrType’,我们可以认为这些因素并不重要。除此之外,他们和’YearBuilt’ 以及 ‘OverallQual’都有很强的关联性,而这两个变量我们已经考虑过了。所以删除 ‘MasVnrArea’和 ‘MasVnrType’并不会丢失信息。
最后,由于’Electrical’中只有一个损失的观察值,所以我们删除这个观察值,但是保留这一变量。

异常值

单因素分析

这里的关键在于如何建立阈值,定义一个观察值为异常值。我们对数据进行正态化,意味着把数据值转换成均值为0,方差为1的数据。

saleprice_scaled = StandardScaler().fit_transform(df_train['SalePrice'][:, np.newaxis])
low_range = saleprice_scaled[saleprice_scaled[:, 0].argsort()][:10]
high_range = saleprice_scaled[saleprice_scaled[:, 0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of the distribution:')
print(high_range)

进行正态化后,可以看出:
低范围的值都比较相似并且在0附近分布。
高范围的值离0很远,并且七点几的值远在正常范围之外。

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]]

双变量分析

‘GrLivArea’和’SalePrice’双变量分析

var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));

从图中可以看出:
有两个离群的’GrLivArea’ 值很高的数据,我们可以推测出现这种情况的原因。或许他们代表了农业地区,也就解释了低价。 这两个点很明显不能代表典型样例,所以我们将它们定义为异常值并删除。
图中顶部的两个点是七点几的观测值,他们虽然看起来像特殊情况,但是他们依然符合整体趋势,所以我们将其保留下来。
在这里插入图片描述
找到两个异常点

df_train.sort_values(by='GrLivArea', ascending=False)[:2]
IdMSSubClassMSZoningLotAreaStreetLotShapeLandContourUtilitiesLotConfigLandSlope...EnclosedPorch3SsnPorchScreenPorchPoolAreaMiscValMoSoldYrSoldSaleTypeSaleConditionSalePrice
1298129960RL63887PaveIR3BnkAllPubCornerGtl...000480012008NewPartial160000
52352460RL40094PaveIR1BnkAllPubInsideGtl...00000102007NewPartial184750

2 rows × 63 columns

删除两个异常点

df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)

‘TotalBsmtSF’和’SalePrice’双变量分析

var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0.800000))

基本线性相关
在这里插入图片描述

核心部分

“房价”到底是谁?

这个问题的答案,需要我们验证根据数据基础进行多元分析的假设。
我们已经进行了数据清洗,并且发现了“SalePrice”的很多信息,现在我们要更进一步理解‘SalePrice’如何遵循统计假设,可以让我们应用多元技术。
应该测量4个假设量:
正态性
同方差性
线性
相关错误缺失

正态性:

应主要关注以下两点:
直方图 – 峰度和偏度。
正态概率图 – 数据分布应紧密跟随代表正态分布的对角线

绘制‘SalePrice’直方图和正态概率图:

sns.distplot(df_train['SalePrice'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

可以看出,房价分布不是正态的,显示了峰值,正偏度,但是并不跟随对角线。
可以用对数变换来解决这个问题
在这里插入图片描述
在这里插入图片描述

对数转换

df_train['SalePrice'] = np.log(df_train['SalePrice'])

Saleprice转换后的直方图和正态概率图

sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

在这里插入图片描述
在这里插入图片描述

绘制’GrLivArea’直方图和正态概率图

sns.distplot(df_train['GrLivArea'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

在这里插入图片描述
在这里插入图片描述
对数转换

df_train['GrLivArea'] = np.log(df_train['GrLivArea'])

对数转换后的直方图和正态概率图

sns.distplot(df_train['GrLivArea'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

在这里插入图片描述
在这里插入图片描述

绘制‘TotalBsmtSF’ 直方图和正态概率图

sns.distplot(df_train['TotalBsmtSF'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['TotalBsmtSF'],plot=plt)

在这里插入图片描述
在这里插入图片描述

TotalBsmtSF属性中有0值,进行对数转换需要转换大于0的值

#create column for new variable (one is enough because it's a binary categorical feature)
#if area>0 it gets 1, for area==0 it gets 0
df_train['HasBsmt'] = pd.Series(len(df_train['TotalBsmtSF']), index=df_train.index)

找到‘TotalBsmtSF’属性中大于0的记录

df_train['HasBsmt'] = 0
df_train.loc[df_train['TotalBsmtSF']>0, 'HasBsmt'] = 1

对于TotalBsmtSF属性中大于0额记录进行对数转换

df_train.loc[df_train['HasBsmt']==1, 'TotalBsmtSF'] = np.log(df_train['TotalBsmtSF'])
df_train['TotalBsmtSF'].describe()
count    1457.000000
mean        6.747838
std         1.145003
min         0.000000
25%         6.679599
50%         6.898715
75%         7.167809
max         8.072779
Name: TotalBsmtSF, dtype: float64

对数转换后的直方图和正态概率图

sns.distplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], plot=plt)

在这里插入图片描述
在这里插入图片描述

同方差性

最好的测量两个变量的同方差性的方法就是图像。

‘SalePrice’ 和 ‘GrLivArea’同方差性 绘制散点图

plt.scatter(df_train['GrLivArea'], df_train['SalePrice'])

在这里插入图片描述

’SalePrice’ with ‘TotalBsmtSF’同方差性绘制散点图:

plt.scatter(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice'])

在这里插入图片描述
独热处理,不重要

# df_train = pd.get_dummies(df_train)
X_train = df_train[['GrLivArea', 'TotalBsmtSF']]
X_train['TotalBsmtSF'].head()
0    6.752270
1    7.140453
2    6.824374
3    6.628041
4    7.043160
Name: TotalBsmtSF, dtype: float64
Y_train = df_train['SalePrice']
Y_train.describe()
count    1457.000000
mean       12.024005
std         0.399853
min        10.460242
25%        11.774520
50%        12.001505
75%        12.273731
max        13.534473
Name: SalePrice, dtype: float64
test = pd.read_csv('test.csv')
X_test = test[['GrLivArea', 'TotalBsmtSF']]#.astype('float64')
X_test['TotalBsmtSF'] = X_test['TotalBsmtSF'].fillna(0)
X_test['GrLivArea'] = np.log(X_test['GrLivArea'])
X_test.loc[X_test['TotalBsmtSF']>0, 'TotalBsmtSF'] = np.log(X_test['TotalBsmtSF'])
X_test.describe()
# df_train.loc[df_train['HasBsmt']==1, 'TotalBsmtSF'] = np.log(df_train['TotalBsmtSF'])
GrLivAreaTotalBsmtSF
count1459.0000001459.000000
mean7.2537456.710040
std0.3161521.218175
min6.0088130.000000
25%7.0188496.664409
50%7.2668276.895683
75%7.4506617.173191
max8.5360158.536015
X_test.isnull().sum()

GrLivArea      0
TotalBsmtSF    0
dtype: int64
from sklearn.linear_model import LinearRegression#导入模型
model = LinearRegression()
model1 = model.fit(X_train,Y_train)
predict = model1.predict(X_test)
predict = np.exp(predict)
result=pd.DataFrame({'Id':test.Id, 'SalePrice':predict})
result.to_csv("submission_2.csv",index=False)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值