線性迴歸
美國房價問題
Alex經過一年的努力,終於拿到了美國波士頓麻省理工學院的研究生錄取通知書,在遠離家鄉的地方上學,Alex想在波士頓買一套房子,他手頭有一些積蓄,在網上找了幾套自己滿意的房子,但是又不敢相信網上的價格,人生地不熟的,Alex怕被宰,就從自己做數據分析的朋友Bachelor手裏要到了過去幾年一些有關波士頓房價的資料。
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
!dir
驅動器 G 中的卷是 Code
卷的序列號是 AC81-1911
G:\Python\PythonAI\第二模塊_人工智能之機器學習、智能玩具\Linear_Regression_Learn 的目錄
2020/02/17 21:36 <DIR> .
2020/02/17 21:36 <DIR> ..
2020/01/27 09:01 <DIR> .ipynb_checkpoints
2019/12/15 21:33 13,370 data_description.txt
2020/02/10 10:16 195,892 HousePrices.zip
2020/02/17 21:36 43,963 Linear_Regression_Learn.ipynb
2019/12/15 21:33 31,939 sample_submission.csv
2020/02/10 09:08 35,527 submisson.csv
2019/12/15 21:33 451,405 test.csv
2019/12/15 21:33 460,676 train.csv
2020/02/14 16:40 565,862 線性迴歸.pptx
8 個文件 1,798,634 字節
3 個目錄 92,257,865,728 可用字節
house_prices = pd.read_csv("train.csv")
Bachelor給的數據非常非常多,包含各個方面。
house_prices
Id | MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | ... | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
1 | 2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
2 | 3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
3 | 4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
4 | 5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1455 | 1456 | 60 | RL | 62.0 | 7917 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 8 | 2007 | WD | Normal | 175000 |
1456 | 1457 | 20 | RL | 85.0 | 13175 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | MnPrv | NaN | 0 | 2 | 2010 | WD | Normal | 210000 |
1457 | 1458 | 70 | RL | 66.0 | 9042 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | GdPrv | Shed | 2500 | 5 | 2010 | WD | Normal | 266500 |
1458 | 1459 | 20 | RL | 68.0 | 9717 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 4 | 2010 | WD | Normal | 142125 |
1459 | 1460 | 20 | RL | 75.0 | 9937 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 6 | 2008 | WD | Normal | 147500 |
1460 rows × 81 columns
爲了方便分析呢,先提取其中三個特徵作爲分析素材,分別是LotArea表示房屋面積,TotalBsmtSF表示地下室的總面積,SalePrice表示的就是房價了。
house_prices[['LotArea', 'TotalBsmtSF', 'SalePrice']]
LotArea | TotalBsmtSF | SalePrice | |
---|---|---|---|
0 | 8450 | 856 | 208500 |
1 | 9600 | 1262 | 181500 |
2 | 11250 | 920 | 223500 |
3 | 9550 | 756 | 140000 |
4 | 14260 | 1145 | 250000 |
... | ... | ... | ... |
1455 | 7917 | 953 | 175000 |
1456 | 13175 | 1542 | 210000 |
1457 | 9042 | 1152 | 266500 |
1458 | 9717 | 1078 | 142125 |
1459 | 9937 | 1256 | 147500 |
1460 rows × 3 columns
理工科出生的Alex想起了曾經學過的知識,想計算一下自己喜歡的那幾套房子的大概房價是多少,到買房的時候心裏好有點數。
於是他把數據重新處理了一下。
sample_test_data = house_prices[['LotArea', 'TotalBsmtSF', 'SalePrice']].copy()
sample_test_data.rename(columns={'LotArea':'x1', 'TotalBsmtSF':'x2', 'SalePrice':'y'}, inplace=True)
sample_test_data
x1 | x2 | y | |
---|---|---|---|
0 | 8450 | 856 | 208500 |
1 | 9600 | 1262 | 181500 |
2 | 11250 | 920 | 223500 |
3 | 9550 | 756 | 140000 |
4 | 14260 | 1145 | 250000 |
... | ... | ... | ... |
1455 | 7917 | 953 | 175000 |
1456 | 13175 | 1542 | 210000 |
1457 | 9042 | 1152 | 266500 |
1458 | 9717 | 1078 | 142125 |
1459 | 9937 | 1256 | 147500 |
1460 rows × 3 columns
作爲一名優秀的理科生,看見熟悉的x和y覺得舒服多了。
接下來就是建立一個數學模型了,簡單的說就是找一個符合x1、x2與y關係的式子,這個簡單嘛,高中就學過的東西,待定係數法唄,只不過從一個x變成了x1、x2而已,那設兩個未知數不就可以了。
設:
這公式那麼一寫阿,瞅起來像是一個線性模型,簡單理解也就是一條線嘛。
再仔細一看,這條線還必須過原點,那就麻煩了,還不能確定這個x1、x2跟y組成的函數圖像一定過原點阿,必須要經過原點這個限制可就太大了。
沒辦法,再加一個未知數吧,把方程改成
這樣以來,在沒確定a、b、c之前,函數圖像可以是空間中任意的。
接下來就是把a、b、c確定下來了,只要它們三個確定下來就好辦了,把自己喜歡的房子的平均房間數和到五個波士頓就業中心的平均距離直接代入x1、x2,不就能計算出來一個大概的房價y了麼。
看着
這個式子,Alex自己都笑了,這玩意是高中才寫的方程,自己孬好也是上過大學的人,於是把方程又改了改:
這樣,如果跟房價有關的x特徵很多的話,就能把方程寫成矩陣相乘的形式了:
這個θi阿,有個學名叫權重項。
這個θ0x0呢,就是之前寫的θ0,讓x0=1不就是咯。
也就是說,我得在我的數據裏再加一行x0的數據,值全部設置爲1就行了。
sample_test_data['x0'] = 1
sample_test_data = sample_test_data[['y', 'x0', 'x1', 'x2']] # 更換一下y、x1、x2、x0的順序,方便看而已,無意義
sample_test_data
y | x0 | x1 | x2 | |
---|---|---|---|---|
0 | 208500 | 1 | 8450 | 856 |
1 | 181500 | 1 | 9600 | 1262 |
2 | 223500 | 1 | 11250 | 920 |
3 | 140000 | 1 | 9550 | 756 |
4 | 250000 | 1 | 14260 | 1145 |
... | ... | ... | ... | ... |
1455 | 175000 | 1 | 7917 | 953 |
1456 | 210000 | 1 | 13175 | 1542 |
1457 | 266500 | 1 | 9042 | 1152 |
1458 | 142125 | 1 | 9717 | 1078 |
1459 | 147500 | 1 | 9937 | 1256 |
1460 rows × 4 columns
畢竟阿,這個方程是咱自己寫的,假設最後算出來了一套θi,爲了擬合大部分的數據,按照θi計算出來的房價跟真實的房價之間還是有一定的差距的。
如果說對比真實的房價數據,根據咱們這個方程計算出來的房價跟真實的房價相差無幾,那不就是想要的結果麼,我輸入想要買的房子的x特徵值,計算出來的房價y就越接近真實房價。
當然這是後話,現在咱們的目的是啥,就是讓通過咱們方程計算出來的房價跟真實的房價的差值越小越好嘛。
對於每一套房子,y(i)表示真實的房價,通過θTxi計算出來的是模型預測房價,εi是預測房價跟真實房價之間的差距,當然εi有可能是正數也有可能是負數。
目的什麼來着,讓差距越來越小對吧,也就是讓εi越來越小,當然這個越來越小是說差距越來越小,也就是絕對值越來越接近於0。
給方程移下項:
這時候咱們再來回顧一下阿,對於方程
θTxi是表示係數,如果在座標系表示的話是由x確定一個圖像。
當只有一個x的時候,圖像是一維的,也就是平面上的一條線;
當有兩個x的時候,圖像是二維的,也就是空間裏的一個面;
當有更多x的時候,我是想象不出來了……
就拿只有兩個x舉例隨便畫一個圖像:
在這個圖像裏,假設青藍色的那個平面表示的就是θTxi,真實房價y(i)在圖中表示的紅色的點。
這樣看就清晰了,目的是讓
的絕對值越小越好,表示在圖像上就是紅色的點越接近青藍色的平面越好。
這裏要明確一個思想,真實房價是確定了,也就是y(i)、紅色的點是確定的,而青藍色的平面θTxi纔是變動的,也就是說,在空間裏,我們要移動平面來擬合點,找到哪一個平面到所有點的距離最小。
這個預測房價與真實房價之間的距離,也就是誤差ε(i)是獨立並同分佈,並且服從均值爲0方差爲σ2的高斯分佈。
這裏突然間冒出來三個不熟悉的名詞解釋一下阿:
獨立:各個樣本點之間是相互獨立的。也就是說,Alex去波士頓買房,跟Bachelor早波士頓買房,只要兩個人買的不是同一套房子,它們之間是沒有關係的,買到的房價多少隻跟各個房東有關。
同分布:數據的來源必須相同。Alex是想在波士頓買房,所以需要的是過去幾年波士頓的房價數據,如果拿得是紐約的房價數據,顯然是不符合需求的,建立的數學模型也不準確。
高斯分佈:也就是正態分佈,一種連續型隨機變量的概率密度函數。先來看一下正態分佈的函數圖像:
看起來是不是特別圓潤,符合一種對稱美,是不是覺得手感肯定特別好。
爲什麼要用高斯分佈?
其實我們一開始並不能確定誤差一定服從高斯分佈,只不過阿,根據前人的經驗,大多數誤差經過測量被證實是服從高斯分佈的,說明高斯分佈對誤差假設來說是一種很好的模型。
在自然界與生產中,一些現象受到許多相互獨立的隨機因素的影響,也就是我們買房過程中的每套住房的平均房間數、到五個波士頓就業中心的平均距離,如果每個因素所產生的影響都很微小,總的影響可以看作是服從正態分佈的。
當然上面那個高斯分佈圖看起來不是很清晰,我們可以用numpy和matplotlib自己來畫一個簡單的高斯分佈函數圖像。
在數學上,正態分佈的概率密度函數:
當u=0,σ=1時,正態分佈稱爲標準正態分佈:
def gaussian(x, mean, sigma):
return (np.exp((-(x - mean) ** 2) / (2 * sigma ** 2)) / (np.sqrt(2 * np.pi) * sigma))
mean, sigma = 0, 1
x = np.arange(-3, 3, 0.001)
plt.plot(x, gaussian(x, mean, sigma))
plt.show()
從高斯分佈圖可知,[-2,2]之間的分佈佔大部分,啥意思呢,說的其實是預測房價跟真實房價大多數情況下都是差異不大的。
在我們的房價預測問題中,均值μ=0,所以可以把公式寫成:
之前所假設的是誤差ε(i)服從正態分佈,所以ε(i)就是正態分佈函數中的x,我們再把ε(i)代入到公式中:
我們把之前推導出的結果
也就是預測房價與真實房價之間的差距,再代入到公式中:
在這個公式中,y(i)表示的真實房價,是已知的,x(i)呢,是每間房子的特徵,也就是房屋的平均房間數之類的,也是已知的。
也就是說,整個公式中,只有θT是未知的。
這樣的話,咱們把自變量換一下,整成一個關於未知數θ的函數:
我們一開始的目的是想讓誤差變小,在正態分佈中,對於自變量x,絕對值越小,越接近於0,因變量的值越大。
好了,現在我們的目的進階了一下,讓L(θ)的值越大越好。
那麼,怎麼才能讓L(θ)的值變大呢?
我們不是有往年的波士頓房價資料麼,把以前的一些資料全部代入進去不就行了。
代入一個,得到一個L(θ1),再代入一個得到一個L(θ2),然後一直代入,一直代入……
咱們是想讓建立的數學模型最好能夠擬合所有的樣本,也就是說,讓所有的L(θ)都最大。
現在把所有的L(θ)整合起來,做一個累乘:
爲什麼要做累乘而不是累加呢?
這個其實跟數學有關,多個樣本之間的累乘依然保留原本的分佈模式,並且會使常見的概率更常見,這個概率對所有的樣本關聯性更強。
累加當然可以用,假如說L(θi)={1,2,3,4,5,6,7,8,9},把所有的數累加起來,其結果sum_L(θi)=45,但是如果把所有的結果累乘起來,其結果ride_L(θi)=362880。
這個時候,如果缺了一個數7,那麼sum_L(θi)’=38,ride_L(θi)’=51840,累乘的結果降低的更多,說明累乘的結果對每一個樣本的關聯性更大。
對於L(θ),它有一個學名叫似然函數。
來看一下定義:似然函數也是一個概率密度函數L(θ∣x),表示在樣本值x已知的情況下求最可能的θ值;實際運用中,根據我們的樣本去估計參數值,找到最最符合的參數,使得與我們的數據組合後恰好是真實值。
暫時不需要去深刻的理解似然函數的概念,咱現在的主要目的是買房。
現在得到了一個累乘的結果L(θ),但是累乘算起來比較麻煩,咱們可以把累乘轉換爲累加,方法是對等式兩邊同時取對數:
雖然轉換了,但是效果還是一樣的,只是通過累加來計算而已。
根據對數運算的性質,可以將累乘計算提到ln前邊,變成累加運算:
再看一下右式ln運算的真數:
這也是一個乘法運算,而且前一項
是一個常數,後一項
根據對數運算的性質可以消去e。
化簡一下,公式就變成了:
之前我們分析過了,整個式子裏只有θ𝑇是未知數,所以可以先把常數項能化簡的化簡,能提前的提前,將公式化簡爲:
這時候再看一下公式的常數項:
把1/2保留到變量項裏,後續有用。
我們的目的是讓似然函數L(θ)越大越好,也就是lnL(θ)越大越好,也就是讓
越小越好。
寫成目標函數:
你可能覺得,
這個式子不就是之前推導出來的麼,整了那麼一大圈,最後的出來的還是這個。
實則不然,現在要計算的是:J(θ)的最小值,
當x只有一維取值的時候,這是一個二次函數,用高中的話講就是一個開口向上的二次函數,有最小值,怎麼求呢?
求導,導數爲0的時候取極值,極值中存在最值。
我們得到的目標函數J(θ)不是簡單的二次函數,而是將所有的x(i)映射到一個x上。
現在得到了目標函數:
轉換成矩陣相乘的形式並化簡一下:
咱們的未知數是θ,現在是一個關於θ的函數,對θ求導得:
令J’(θ)=0得:
現在,我們終於得到得到最終的θ了,爲什麼說最終了呢?X是已經獲得的房屋的數據,y是房價,都是已知的,直接代入就OK了。
有了公式,現在回頭來看看真實的問題和真實的數據該怎麼處理:
House Prices: Advanced Regression Techniques
房價:先進的迴歸技術
housing = pd.read_csv("train.csv")
housing
Id | MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | ... | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
1 | 2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
2 | 3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
3 | 4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
4 | 5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1455 | 1456 | 60 | RL | 62.0 | 7917 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 8 | 2007 | WD | Normal | 175000 |
1456 | 1457 | 20 | RL | 85.0 | 13175 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | MnPrv | NaN | 0 | 2 | 2010 | WD | Normal | 210000 |
1457 | 1458 | 70 | RL | 66.0 | 9042 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | GdPrv | Shed | 2500 | 5 | 2010 | WD | Normal | 266500 |
1458 | 1459 | 20 | RL | 68.0 | 9717 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 4 | 2010 | WD | Normal | 142125 |
1459 | 1460 | 20 | RL | 75.0 | 9937 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 6 | 2008 | WD | Normal | 147500 |
1460 rows × 81 columns
數據預處理
拿到數據之後,不是上來就要進行計算的,首先要做的就是數據預處理。
首先,我們來分析一下每一個x特徵:
MSSubClass: Identifies the type of dwelling involved in the sale.
MSSubClass:標識參與銷售的住宅類型。
20 1-STORY 1946 & NEWER ALL STYLES
1946年1層和更新的所有樣式
30 1-STORY 1945 & OLDER
1945年1層及以上
40 1-STORY W/FINISHED ATTIC ALL AGES
1層,帶成品閣樓,所有年齡段
45 1-1/2 STORY - UNFINISHED ALL AGES
1-1/2層-未完成所有年齡段
50 1-1/2 STORY FINISHED ALL AGES
1-1/2層完成所有年齡段
60 2-STORY 1946 & NEWER
1946年2層及以上
70 2-STORY 1945 & OLDER
1945年2層及以上
75 2-1/2 STORY ALL AGES
2-1/2層,所有年齡段
80 SPLIT OR MULTI-LEVEL
拆分或多級
85 SPLIT FOYER
分體式門廳
90 DUPLEX - ALL STYLES AND AGES
複式-所有樣式和年齡
120 1-STORY PUD (Planned Unit Development) - 1946 & NEWER
一層建築(計劃單元開發)-1946年及更新版本
150 1-1/2 STORY PUD - ALL AGES
1-1/2層佈德-所有年齡段
160 2-STORY PUD - 1946 & NEWER
兩層樓的PUD-1946及更新版本
180 PUD - MULTILEVEL - INCL SPLIT LEV/FOYER
PUD-多級-包括分離式LEV/門廳
190 2 FAMILY CONVERSION - ALL STYLES AND AGES
2家庭轉換-所有風格和年齡
雖然沒太看懂啥意思,但是大致就是MSSubClass越高越好,從一兩層的到複式再到什麼家庭轉換,看着就感覺挺貴的樣子。
先看一下MSSubClass這一列有沒有缺失值。
housing["MSSubClass"].isnull().sum()
0
MSSubClass這一列數據狀況良好,沒有缺失值,看一下它的分佈情況。
plt.scatter(housing["MSSubClass"], housing["SalePrice"], )
plt.show()
這麼一看分佈的話,我們之前的猜測不成立啊,MSSubClass和SalePrice不是正相關的關係,那它倆的關係先待定。
通過散點圖雖然能看到不同房價的分佈,但是有些地方很多點重合在一起,很難看出房價密度。
這時候,我們給MSSubClass和SalePrice添加一個抖動,抖動只是讓它在圖上的顯示位置稍微偏離一下,並不會更改真實的數據,然後再設置一下透明度,重疊的點越多,圖像顏色越深。
sns.regplot(data=housing, x="MSSubClass", y="SalePrice", x_jitter=3, scatter_kws={"alpha":0.3})
如圖可見,大部分MSSubClass都在0~100之內,而MSSubClass與SalePrice的擬合線接近於一條直線,而且有點偏向於負相關。
MSZoning: Identifies the general zoning classification of the sale.
MSZoning:標識銷售的一般分區分類。
A Agriculture
農業
C Commercial
商業
FV Floating Village Residential
流動村住宅
I Industrial
工業
RH Residential High Density
住宅高密度
RL Residential Low Density
住宅低密度
RP Residential Low Density Park
住宅低密度公園
RM Residential Medium Density
住宅中密度
上來還是先看有沒有缺失值:
housing["MSZoning"].isnull().sum()
housing["MSZoning"].value_counts()
沒有缺失值,還是看一下MSZoning跟SalePrice的關係,但是我們看一下數據:
housing["MSZoning"]
數據並不是常規的數值,而是字母變量,我們要把字母變量對應到數值。
確定一個簡單的對應關係:
1 <—— A Agriculture
2 <—— C Commercial
3 <—— FV Floating Village Residential
4 <—— I Industrial
5 <—— RH Residential High Density
6 <—— RL Residential Low Density
7 <—— RP Residential Low Density Park
8 <—— RM Residential Medium Density
housing.loc[housing["MSZoning"] == "A", "MSZoning"] = 1.0
housing.loc[housing["MSZoning"] == "C (all)", "MSZoning"] = 2.0
housing.loc[housing["MSZoning"] == "FV", "MSZoning"] = 3.0
housing.loc[housing["MSZoning"] == "I", "MSZoning"] = 4.0
housing.loc[housing["MSZoning"] == "RH", "MSZoning"] = 5.0
housing.loc[housing["MSZoning"] == "RL", "MSZoning"] = 6.0
housing.loc[housing["MSZoning"] == "RP", "MSZoning"] = 7.0
housing.loc[housing["MSZoning"] == "RM", "MSZoning"] = 8.0
housing["MSZoning"].value_counts()
sns.regplot(data=housing, x="MSZoning", y="SalePrice", x_jitter=0.4, scatter_kws={"alpha":0.3})
哎呀我去,我就隨便一對應,沒想到對應出來一個正相關。
LotFrontage: Linear feet of street connected to property
地塊臨街:連接到地產的街道的直線英尺
housing["LotFrontage"]
housing["LotFrontage"].isnull().sum()
終於找到一個含有缺失值的了,LotFrontage列含有259個缺失值,這時候,我們需要對缺失值做填充。
我比較喜歡的一種方案是利用均值和標準差的信息進行填充。
當然,並不是只有LotFrontage採用缺失值,所以我們可以把缺失值填充抽象成一個函數。
def fill_null(df, col):
mean = df[col].dropna().mean()
std = df[col].dropna().std()
null_sum = df[col].isnull().sum()
fill_num = np.random.randint(mean - std, mean + std, null_sum)
df.loc[df[col].isnull(), col] = fill_num
fill_null(housing, "LotFrontage")
sns.regplot(data=housing, x="LotFrontage", y="SalePrice", scatter_kws={"alpha":0.3})
這麼一看的話,LotFrontage和SalePrice也是成正相關的關係。
LotArea: Lot size in square feet
housing["LotArea"].isnull().sum()
sns.regplot(data=housing, x="LotArea", y="SalePrice", scatter_kws={"alpha":0.4})
總結
找一下套路,對於每一個特徵數據:
1.判斷該列數據是否含有缺失值
1.1、如果不含有缺失值,繼續後續操作
1.2、如果含有缺失值,對缺失值進行填充
2.判斷該列數據是不是數值
2.1、如果是數值繼續後續操作
2.2、如果不是數值,定義對應關係,將數據對應到數值
3.去除異常數據
4.繪製散點圖和線性關係
那麼接下來我們吧數據統一重新處理一下:
train_house = pd.read_csv("http://kaggle.shikanon.com/house-prices-advanced-regression-techniques/train.csv")
第一列數據是ID,是我們人爲標註的數據,對房價並無影響,先把它單獨抽離出來。
train_house_ID = train_house["Id"]
train_house.drop("Id", axis=1, inplace=True)
我們第一步要做的就是缺失值處理,缺失值處理有兩種方案,一種是分析含缺失值的特徵對任務有沒有用,沒用的特徵直接刪除,有用的特徵依據缺失量,少則刪除樣本,多則用mean,median或mod補全;另一種方案是分析這些缺失值缺失的原因,並用一定方法將其轉換爲一類數據(成爲類型變量的一個類型)。
na_count = train_house.isnull().sum().sort_values(ascending=False)
na_rate = na_count / len(train_house)
na_data = pd.concat([na_count,na_rate],axis=1,keys=['count','ratio'])
pd.set_option('display.max_rows',None) # 將pandas的輸出數據不省略顯示
print(na_data)
首先,如果某一特徵的數據缺失量達到15%以上,那這項特徵應該予以刪除並認爲數據集中不存在這樣的特徵。
也就是說我們並不會設法去填補這些特徵的缺失值,因爲假定它是不存在的,因此刪除數據的 ’PoolQC’, ‘MiscFeature’, ‘Alley’, ‘Fence’, ‘FireplaceQu’和‘LotFrontage’這幾列。
這應該不會導致數據的有效信息量下降,因爲這些特徵的字面含義似乎根本與房價無關,難怪會有這麼多缺失值,而且這些特徵的有效數據具有各種離羣值。
其次,在剩下的含缺失值變量中,以Garage開頭的5個GarageX特徵具有相同數量的缺失值,據此推測他們可能代表的是同一組觀測值,而關於Garage的信息,’GarageCars’已經能夠很好地表徵了,因此刪除這幾個特徵,對BsmtX也可以進行同樣的操作。
之後,對於MasVnrArea和MasVnrType,根據其字面意思我們認爲它們並不重要,而且它們與YearBuilt和OverallQual有較強的相關性。因此,我們刪除這兩個特徵也不會丟失任何信息。
然後,除了Electrical,其它無意義的含缺失值的變量我們都已經刪除了,Electrical這個變量下只有一個樣本帶有缺失值,因此我們不妨刪除帶有這個缺失值的那各樣本。
最後,經過我們的嚴密分析哈,所有的含有缺失值的特徵都可以刪除。
train_house.drop(na_data[na_data['count'] > 1].index, axis=1, inplace=True)
train_house.drop(train_house.loc[train_house['Electrical'].isnull()].index, inplace=True)
train_house.shape
第二步我們要做的就是講數據中的字符串型特徵映射爲數值型特徵,這樣才能方便計算,但是我總不能一列一列的看是不是數值型數據然後再做數據對應吧,那還不如不學了,pandas給我提供好了這樣的功能——factorize函數可以將Series中的標稱型數據映射稱爲一組數字,相同的標稱型映射爲相同的數字。
舉個簡單的例子看一下factorize函數:
由上面可見,我們送入一串序列[‘b’, ‘b’, ‘a’, ‘c’, ‘b’],該函數做了什麼事,其實,它是將送入的字符映射成數字,原則是相同的字符對應同一個數字。
比如上面,函數先處理 ‘b’,將其映射成 1, 然後處理第二個字符還是 ‘b’,因爲已經映射成功,所以直接返回 1, 接着處理到 ‘a’,將其映射爲 0,然後處理 ‘c’,將其映射成 2, 最後處理 ‘b’,已經映射過了,所以直接輸出 1。
將上述輸出彙總成序列,輸出到 labels 中,codes裏面其實就是存儲我們輸入序列中互異的元素,即上面的[‘a’, ‘b’, ‘c’]。
有了這個函數,就可以幫助我們將所有的非數值型數據轉換爲數值型數據:
for col in train_house.columns:
if train_house[col].dtypes == "object":
train_house[col], uniques = pd.factorize(train_house[col])
train_house.dtypes
所有的缺失值都被處理,所有的字符串型數據都被替換爲數值型數據,這樣就可以計算了。
我們把之前計算的θ公式拿過來:
y = train_house["SalePrice"]
X = train_house.drop('SalePrice', axis=1)
theta = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), y)
theta.shape
求出了θ,我們就可以根據自己輸入的特徵預測房價了:
test_house = pd.read_csv("test.csv")
在計算之前,需要把測試集的數據做一遍跟訓練集一樣的處理:
test_house_ID = test_house["Id"]
test_house.drop("Id", axis=1, inplace=True)
test_house.drop(na_data[na_data['count'] > 1].index, axis=1, inplace=True)
for col in test_house.columns:
if test_house[col].dtypes == "object":
test_house[col], uniques = pd.factorize(test_house[col])
test_house[col].fillna(test_house[col].mean(), inplace=True)
test_house.shape
再把房價的計算公式拿過來:
# 不以科學計數顯示:
np.set_printoptions(suppress = True)
Y = np.dot(test_house, theta)
submisson = pd.concat([test_house_ID, pd.Series(abs(Y))], axis=1, keys=["Id","SalePrice"])
submisson.to_csv("submisson.csv", index=False)
我們把submission提交到Kaggle的平臺上,看看能獲得什麼樣的分數:
結果顯示並不是很好,當然,我們還有好多因素沒有考慮,不過,線性迴歸,我們就先講到這裏,後續我們有更好的方法來解決房價問題。