詳解 Kaggle 房價預測競賽優勝方案:用 Python 進行全面數據探索

Kaggle 的房價預測競賽從 2016 年 8 月開始,到 2017 年 2 月結束。這段時間內,超過 2000 多人蔘與比賽,選手採用高級迴歸技術,基於咱們給出的 79 個特徵,對房屋的售價進行了準確的預測。今天咱們介紹的是目前得票數最高的優勝方案:《用 Python 進行全面數據探索》,該方案在數據探索,特徵工程上都有十分出色的表現。html


做者 Pedro Marcelino 在競賽中使用的主要方法是關注數據科學處理方法,以及尋找可以指導工做的有力文獻資料。做者主要參考《多元數據分析》(Multivariate Data Analysis, Hair et al., 2014)中的第三章 「檢查你的數據」。做者將本身研究的方法歸爲如下三步:bash

  1. 定義要解決的問題;框架

  2. 查閱相關文獻;函數

  3. 對他們進行修改以適合本身的要求。ui


**「不過是站在巨人的肩膀上。」—— Pedro Marcelino **
spa

下面咱們就一塊兒來看看做者是如何對數據進行分析的。3d


瞭解你的數據

方法框架:code

  • 理解問題:查看每一個變量而且根據他們的意義和對問題的重要性進行哲學分析。
  • 單因素研究:只關注因變量( SalePrice),而且進行更深刻的瞭解。
  • 多因素研究:分析因變量和自變量之間的關係。
  • 基礎清洗:清洗數據集而且對缺失數據,異常值和分類數據進行一些處理。
  • 檢驗假設:檢查數據是否和多元分析方法的假設達到一致。

開始以前:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as npfrom scipy.stats
import normfrom sklearn.preprocessing
import StandardScalerfrom scipy
import stats
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline

#bring in the six packs
df_train = pd.read_csv('../input/train.csv')
#check the decoration
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')
      
複製代碼

準備工做——咱們能夠指望什麼?orm


爲了瞭解咱們的數據,咱們能夠分析每一個變量而且嘗試理解他們的意義和與該問題的相關程度。cdn


首先創建一個 Excel 電子表格,有以下目錄:

  • 變量 – 變量名。
  • 類型 – 該變量的類型。這一欄只有兩個可能值,「數據」 或 「類別」。 「數據」 是指該變量的值是數字,「類別」 指該變量的值是類別標籤。
  • 劃分 – 指示變量劃分. 咱們定義了三種劃分:建築,空間,位置。
  • 指望 – 咱們但願該變量對房價的影響程度。咱們使用類別標籤 「高」,「中」 和 「低」 做爲可能值。
  • 結論 – 咱們得出的該變量的重要性的結論。在大概瀏覽數據以後,咱們認爲這一欄和 「指望」 的值基本一致。
  • 評論 – 咱們看到的全部通常性評論。

咱們首先閱讀了每個變量的描述文件,同時思考這三個問題:

  • 咱們買房子的時候會考慮這個因素嗎?
  • 若是考慮的話,這個因素的重要程度如何?
  • 這個因素帶來的信息在其餘因素中出現過嗎?

咱們根據以上內容填好了電子表格,而且仔細觀察了 「高指望」 的變量。而後繪製了這些變量和房價之間的散點圖,填在了 「結論」 那一欄,也正巧就是對咱們的指望值的校訂。


咱們總結出了四個對該問題起到相當重要的做用的變量:

  • OverallQual
  • YearBuilt.
  • TotalBsmtSF.
  • GrLivArea.

最重要的事情——分析 「房價」

描述性數據總結:

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
複製代碼

繪製直方圖

繪製直方圖

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


「房價」 的相關變量分析 與數字型變量的關係:


1.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 與 SalePrice 散點圖
能夠看出 SalePrice 和 GrLivArea 關係很密切,而且基本呈線性關係。


2.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));
複製代碼

TotalBsmtSF 與 SalePrice 散點圖
TotalBsmtSF 和 SalePrice 關係也很密切,從圖中能夠看出基本呈指數分佈,但從最左側的點能夠看出特定狀況下 TotalBsmtSF 對 SalePrice 沒有產生影響。


與類別型變量的關係

1.‘OverallQual’與‘SalePrice’箱型圖

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);
複製代碼

‘OverallQual’與‘SalePrice’箱型圖
能夠看出 SalePrice 與 OverallQual 分佈趨勢相同。


2.YearBuilt 與 SalePrice 箱型圖

var = 'YearBuilt'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);plt.xticks(rotation=90);
複製代碼

YearBuilt 與 SalePrice 箱型圖
兩個變量之間的關係沒有很強的趨勢性,可是能夠看出建築時間較短的房屋價格更高。


總結:

  • GrLivArea 和 TotalBsmtSF 與 SalePrice 彷佛線性相關,而且都是正相關。 對於 TotalBsmtSF 線性關係的斜率十分的高。
  • OverallQual 和 YearBuilt 與 SalePrice 也有關係。OverallQual 的相關性更強, 箱型圖顯示了隨着總體質量的增加,房價的增加趨勢。

OverallQual 和 YearBuilt 與 SalePrice 也有關係。OverallQual 的相關性更強, 箱型圖顯示了隨着總體質量的增加,房價的增加趨勢。


客觀分析

1.相關係數矩陣

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

相關係數矩陣


首先兩個紅色的方塊吸引到了我,第一個是 TotalBsmtSF 和 1stFlrSF 變量的相關係數,第二個是 GarageX 變量羣。這兩個示例都顯示了這些變量之間很強的相關性。實際上,相關性的程度達到了一種多重共線性的狀況。咱們能夠總結出這些變量幾乎包含相同的信息,因此確實出現了多重共線性。


另外一個引發注意的地方是 SalePrice 的相關性。咱們能夠看到咱們以前分析的 GrLivArea,TotalBsmtSF和 OverallQual 的相關性很強,除此以外也有不少其餘的變量應該進行考慮,這也是咱們下一步的內容。


2.SalePrice 相關係數矩陣

k = 10 #number ofvariables for heatmap
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()
複製代碼

SalePrice 相關係數矩陣


從圖中能夠看出:

  • OverallQual,GrLivArea 以及 TotalBsmtSF 與 SalePrice 有很強的相關性。
  • GarageCars 和 GarageArea 也是相關性比較強的變量. 車庫中存儲的車的數量是由車庫的面積決定的,它們就像雙胞胎,因此不須要專門區分 GarageCars 和 GarageAre,因此咱們只須要其中的一個變量。這裏咱們選擇了 GarageCars,由於它與 SalePrice 的相關性更高一些。
  • TotalBsmtSF 和 1stFloor 與上述狀況相同,咱們選擇 TotalBsmtS 。
  • FullBath 幾乎不須要考慮。
  • TotRmsAbvGrd 和 GrLivArea 也是變量中的雙胞胎。
  • YearBuilt 和 SalePrice 相關性彷佛不強。

3.SalePrice 和相關變量之間的散點圖

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

SalePrice 和相關變量之間的散點圖


儘管咱們已經知道了一些主要特徵,這一豐富的散點圖給了咱們一個關於變量關係的合理想法。


其中,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)
複製代碼

缺失數據


當超過 15% 的數據都缺失的時候,咱們應該刪掉相關變量且假設該變量並不存在。


根據這一條,一系列變量都應該刪掉,例如 PoolQC,MiscFeature,Alley 等等,這些變量都不是很重要,由於他們基本都不是咱們買房子時會考慮的因素。GarageX 變量羣的缺失數據量都相同,因爲關於車庫的最重要的信息均可以由 GarageCars 表達,而且這些數據只佔缺失數據的 5%,咱們也會刪除上述的 GarageX 變量羣。一樣的邏輯也適用於 BsmtX 變量羣。


對於 MasVnrArea 和 MasVnrType,咱們能夠認爲這些因素並不重要。除此以外,他們和 YearBuilt 以及 OverallQual 都有很強的關聯性,而這兩個變量咱們已經考慮過了。因此刪除 MasVnrArea 和 MasVnrType 並不會丟失信息。最後,因爲 Electrical 中只有一個損失的觀察值,因此咱們刪除這個觀察值,可是保留這一變量。

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() #justchecking that there's no missing data missing...
複製代碼

異常值

單因素分析

這裏的關鍵在於如何創建閾值,定義一個觀察值爲異常值。咱們對數據進行正態化,意味着把數據值轉換成均值爲 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 thedistribution:')
print(high_range)
複製代碼

單因素分析

進行正態化後,能夠看出:

  • 低範圍的值都比較類似而且在 0 附近分佈。
  • 高範圍的值離 0 很遠,而且七點幾的值遠在正常範圍以外。

雙變量分析

1.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 和 SalePrice 雙變量分析


從圖中能夠看出:

  • 有兩個離羣的 GrLivArea 值很高的數據,咱們能夠推測出現這種狀況的緣由。或許他們表明了農業地區,也就解釋了低價。 這兩個點很明顯不能表明典型樣例,因此咱們將它們定義爲異常值並刪除。
  • 圖中頂部的兩個點是七點幾的觀測值,他們雖然看起來像特殊狀況,可是他們依然符合總體趨勢,因此咱們將其保留下來。

刪除點

df_train.sort_values(by = 'GrLivArea',ascending = False)[:2]
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)
複製代碼

2.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));
複製代碼

TotalBsmtSF 和 SalePrice 雙變量分析


核心部分

「房價」 究竟是誰?

這個問題的答案,須要咱們驗證根據數據基礎進行多元分析的假設。咱們已經進行了數據清洗,而且發現了 SalePrice 的不少信息,如今咱們要更進一步理解 SalePrice 如何遵循統計假設,可讓咱們應用多元技術。


應該測量 4 個假設量:

  • 正態性
  • 同方差性
  • 線性
  • 相關錯誤缺失

正態性: 應主要關注如下兩點:

  • 直方圖 – 峯度和偏度。
  • 正態機率圖 – 數據分佈應緊密跟隨表明正態分佈的對角線。

1.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'])
複製代碼

繪製變換後的直方圖和正態機率圖:

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

2.GrLivArea

繪製直方圖和正態機率曲線圖:

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

GrLivArea


進行對數變換:

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)
複製代碼

正態機率圖


3.TotalBsmtSF

繪製直方圖和正態機率曲線圖:

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


從圖中能夠看出:

  • 顯示出了偏度
  • 大量爲 0 的觀察值(沒有地下室的房屋)
  • 含 0 的數據沒法進行對數變換

咱們創建了一個變量,能夠獲得有沒有地下室的影響值(二值變量),咱們選擇忽略零值,只對非零值進行對數變換。這樣咱們既能夠變換數據,也不會損失有沒有地下室的影響。

df_train['HasBsmt']= pd.Series(len(df_train['TotalBsmtSF']), index=df_train.index)
df_train['HasBsmt'] = 0 
df_train.loc[df_train['TotalBsmtSF']>0,'HasBsmt'] = 1
複製代碼

進行對數變換:

df_train['TotalBsmtSF']= np.log(df_train['TotalBsmtSF'])
複製代碼

繪製變換後的直方圖和正態機率圖:

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

正態機率圖


同方差性:

最好的測量兩個變量的同方差性的方法就是圖像。


1.SalePrice 和 GrLivArea 同方差性

繪製散點圖:

plt.scatter(df_train['GrLivArea'],df_train['SalePrice']);
複製代碼


2.SalePrice with TotalBsmtSF 同方差性

繪製散點圖:

plt.scatter(df_train[df_train['TotalBsmtSF']>0]
['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice']);
複製代碼

能夠看出 SalePrice 在整個 TotalBsmtSF 變量範圍內顯示出了同等級別的變化。


虛擬變量

將類別變量轉換爲虛擬變量:

df_train = pd.get_dummies(df_train)
複製代碼

結論

整個方案中,咱們使用了不少《多元數據分析》中提出的方法。咱們對變量進行了哲學分析,不只對 SalePrice 進行了單獨分析,還結合了相關程度最高的變量進行分析。咱們處理了缺失數據和異常值,咱們驗證了一些基礎統計假設,而且將類別變量轉換爲虛擬變量。


但問題尚未結束,咱們還須要預測房價的變化趨勢,房價預測是否適合線性迴歸正則化的方法?是否適合組合方法?或者一些其餘的方法?但願你能夠進行本身的探索發現。

閱讀原文

相關文章
相關標籤/搜索