iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
AI & Data

資幾資比系列 第 10

[Series - 9] 實戰演練

前言

接下來,我們將帶各位實際分析一筆數據,並從中獲得資訊。


波士頓房價評估

使用sklearn提供的數據,來做分析,如此一來就不用再引入csv檔,而我們用來舉例的是load_boston(),接下來就讓我們一步步的來瞭解資料,使用分析的結果,獲得資訊吧。


實作連結

colab-連結


引入資料

因為是sklearn提供的數據,直接從datasets裡引入即可。

from sklearn import datasets # 引入sklearn裏頭的資料集
import pandas as pd # 引入Pandas

data = datasets.load_boston() # 取得波士頓房價的數據
df_data = pd.DataFrame(data.data, columns=data.feature_names) # 將數據以改成DataFrame的方式呈現

資料內容

在分析資料前,一定要先了解有多少筆資料,每個特徵的意義,因此我們先將資料列印出來討論。

df_data # 查看資料

output


        CRIM	ZN	INDUS	CHAS	NOX	RM	AGE	DIS	RAD	TAX	PTRATIO	B	LSTAT
0	0.00632	18.0	2.31	0.0	0.538	6.575	65.2	4.0900	1.0	296.0	15.3	396.90	4.98
1	0.02731	0.0	7.07	0.0	0.469	6.421	78.9	4.9671	2.0	242.0	17.8	396.90	9.14
2	0.02729	0.0	7.07	0.0	0.469	7.185	61.1	4.9671	2.0	242.0	17.8	392.83	4.03
3	0.03237	0.0	2.18	0.0	0.458	6.998	45.8	6.0622	3.0	222.0	18.7	394.63	2.94
4	0.06905	0.0	2.18	0.0	0.458	7.147	54.2	6.0622	3.0	222.0	18.7	396.90	5.33
...	...	...	...	...	...	...	...	...	...	...	...	...	...
501	0.06263	0.0	11.93	0.0	0.573	6.593	69.1	2.4786	1.0	273.0	21.0	391.99	9.67
502	0.04527	0.0	11.93	0.0	0.573	6.120	76.7	2.2875	1.0	273.0	21.0	396.90	9.08
503	0.06076	0.0	11.93	0.0	0.573	6.976	91.0	2.1675	1.0	273.0	21.0	396.90	5.64
504	0.10959	0.0	11.93	0.0	0.573	6.794	89.3	2.3889	1.0	273.0	21.0	393.45	6.48
505	0.04741	0.0	11.93	0.0	0.573	6.030	80.8	2.5050	1.0	273.0	21.0	396.90	7.88
506 rows × 13 columns
data.DESCR # 查看data的特徵說明

由output可知,共有506筆資料,有13種特徵,而每個特徵的意義分別為

  • CRIM :每個城鎮的人均犯罪率 (per capita crime rate by town)
  • ZN : 佔地25,000平方英尺以上的住宅區域比例(proportion of residential land zoned for lots over 25,000 sq.ft.)
  • INDUS : 每個城鎮非零售業的營業面積比例(proportion of non-retail business acres per town)
  • CHAS : 是否靠近河邊(Charles River dummy variable (= 1 if tract bounds river; 0 otherwise))
  • NOX : 一氧化氮濃度(ppm)(nitric oxides concentration (parts per 10 million))
  • RM : 每個住宅的平均房間數(average number of rooms per dwelling)
  • AGE : 1940年前私有住宅的住房比率(proportion of owner-occupied units built prior to 1940)
  • DIS : 與五個波士頓工做地區的加權距離(weighted distances to five Boston employment centres)
  • RAD : 徑向公路的通達指數(index of accessibility to radial highways)
  • TAX : 每10,000美元的所需繳的財產稅(full-value property-tax rate per $10,000)
  • PTRATIO : 每個城鎮的師生比例(pupil-teacher ratio by town)
  • B : 黑人比例(值為$1000\times(Bk-0.63)^2$)(1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town)
  • LSTAT : 中下階級的比率(% lower status of the population)
data.target # 查看正確的房價

output

array([24. , 21.6, 34.7, 33.4, 36.2, 28.7, 22.9, 27.1, 16.5, 18.9, 15. ,
       18.9, 21.7, 20.4, 18.2, 19.9, 23.1, 17.5, 20.2, 18.2, 13.6, 19.6,
       15.2, 14.5, 15.6, 13.9, 16.6, 14.8, 18.4, 21. , 12.7, 14.5, 13.2,
       13.1, 13.5, 18.9, 20. , 21. , 24.7, 30.8, 34.9, 26.6, 25.3, 24.7,......])

target 取的是該城鎮房價的中間值


檢查資料

缺失值檢查

我們先查看每一行是否具有缺失值,有的話再來決定如何處理它。由下面的輸出資訊,我們可以知道所有的資料都是完整的。

df_data.isnull().any() # 檢查是否有缺失值

output

CRIM       False
ZN         False
INDUS      False
CHAS       False
NOX        False
RM         False
AGE        False
DIS        False
RAD        False
TAX        False
PTRATIO    False
B          False
LSTAT      False
dtype: bool

不合理值的檢查

有些特徵的定義是比率,那麼值是必然會介於0到100之間,而判斷是否在河邊的值也只會有0和1,我們檢查這些值是否異常。而檢查的結果都沒有異常。

flag = False 
for i in df_data["CRIM"]: # 檢查CRIM是否都為合理值
  if(i < 0 or i > 100): 
    flag = True
    break
print(flag)

output

False

其餘特徵的檢查,把CRIM這個特徵改掉就好,而執行完你會發現全部的結果都是False。

df_data["CHAS"].unique()

output

array([0., 1.])

確認目標

這份資料提供了波士頓各個城鎮的資料,像是犯罪率師生比平均房間數,每一個特徵都有可能會影響房價,而我們的目標就是使用這些特徵來預測出房價


初步推理

  1. 犯罪率 : 當犯罪率越高的話,房價會下降。
  2. 一氧化氮濃度 : 可以初略代表空氣汙染的程度,因此濃度越高的話,房價也會下降。
  3. 房間數 : 理論上房間數越多,房價會越高。
  4. 師生比例 : 當考慮教育機能的時候,會希望此值比較低,表示老師的數量較多,越低房價會上升。
  5. 黑人的比率 : 通常黑人區的房價會比較低。
  6. 中下階級的比率 : 當比率越高,表示不是處於高級住宅區,房價會比較低。

房價與每個特徵的關係

在此我們就舉三個特徵來做討論

  1. 犯罪率
  2. 房間數
  3. 黑人的比率

犯罪率

下圖,我們可以推測犯罪率與房價成反比關係,亦即當犯罪率越高,房價會越低。

x = data.target # 房價數據
y = df_data["CRIM"] # 犯罪率數據

plt.xlabel("house price") # x軸的標題
plt.ylabel("crime rate") # y軸的標題
plt.scatter(x, y) # 繪製散點圖
plt.show() # 顯示圖形

房間數

由下圖我們可知雖然有些值有偏離,但大約呈線性的關係,為正相關。

x = data.target # 房價數據
y = df_data["RM"] # 房間數數據

plt.xlabel("house price") # x軸的標題
plt.ylabel("number of rooms") # y軸的標題
plt.scatter(x, y) # 繪製散點圖
plt.show() # 顯示圖形

黑人的比率

在初步分析的時候,我們推測黑人比率越高房價會越低,顯然對波士頓的房價不適用。

x = data.target # 房價數據
y = df_data["B"] # 黑人比率數據

plt.xlabel("house price") # x軸的標題
plt.ylabel("proportion of blacks") # y軸的標題
plt.scatter(x, y) # 繪製散點圖
plt.show() # 顯示圖形


各特徵間的相關性

由下表可知每個特徵各自的相關性,通常我們會定義大於0.7為高度相關,像是TAX與RAD之間的相關係數高達0.91,我們可以去思考其背後的意義,將數據變的更有價值。

df_data.corr(method="pearson")

output


        CRIM	       ZN	       INDUS	       CHAS	       NOX	       RM	       AGE	       DIS	       RAD	       TAX	       PTRATIO	       B	       LSTAT
CRIM	1.000000	-0.200469	0.406583	-0.055892	0.420972	-0.219247	0.352734	-0.379670	0.625505	0.582764	0.289946	-0.385064	0.455621
ZN	-0.200469	1.000000	-0.533828	-0.042697	-0.516604	0.311991	-0.569537	0.664408	-0.311948	-0.314563	-0.391679	0.175520	-0.412995
INDUS	0.406583	-0.533828	1.000000	0.062938	0.763651	-0.391676	0.644779	-0.708027	0.595129	0.720760	0.383248	-0.356977	0.603800
CHAS	-0.055892	-0.042697	0.062938	1.000000	0.091203	0.091251	0.086518	-0.099176	-0.007368	-0.035587	-0.121515	0.048788	-0.053929
NOX	0.420972	-0.516604	0.763651	0.091203	1.000000	-0.302188	0.731470	-0.769230	0.611441	0.668023	0.188933	-0.380051	0.590879
RM	-0.219247	0.311991	-0.391676	0.091251	-0.302188	1.000000	-0.240265	0.205246	-0.209847	-0.292048	-0.355501	0.128069	-0.613808
AGE	0.352734	-0.569537	0.644779	0.086518	0.731470	-0.240265	1.000000	-0.747881	0.456022	0.506456	0.261515	-0.273534	0.602339
DIS	-0.379670	0.664408	-0.708027	-0.099176	-0.769230	0.205246	-0.747881	1.000000	-0.494588	-0.534432	-0.232471	0.291512	-0.496996
RAD	0.625505	-0.311948	0.595129	-0.007368	0.611441	-0.209847	0.456022	-0.494588	1.000000	0.910228	0.464741	-0.444413	0.488676
TAX	0.582764	-0.314563	0.720760	-0.035587	0.668023	-0.292048	0.506456	-0.534432	0.910228	1.000000	0.460853	-0.441808	0.543993
PTRATIO	0.289946	-0.391679	0.383248	-0.121515	0.188933	-0.355501	0.261515	-0.232471	0.464741	0.460853	1.000000	-0.177383	0.374044
B	-0.385064	0.175520	-0.356977	0.048788	-0.380051	0.128069	-0.273534	0.291512	-0.444413	-0.441808	-0.177383	1.000000	-0.366087
LSTAT	0.455621	-0.412995	0.603800	-0.053929	0.590879	-0.613808	0.602339	-0.496996	0.488676	0.543993	0.374044	-0.366087	1.000000

因為有13個特徵,滿滿的數字我們無法一下看出低度相關還是高度相關,因此我們將它改成塗色的呈現。

import seaborn as sns # 引入seaborn
import matplotlib.pyplot as plt # 引入pyplot

plt.figure(figsize= (13, 13)) # 圖形大小
sns.heatmap(df_data.corr(),annot = True) # 塗顏色
plt.show() # 顯示圖形

output

線性迴歸

要做線性迴歸的第一個步驟需要將資料分成兩個部分,一個是訓練的資料,另一個是測試的資料,這個比例要拿捏好,準確性太高跟太低都不好,接下來訓練的模型,我們都採取73比,也就是把70%的資料拿去訓練,留下30%的資料作測試用。

首先我們將13個特徵全部拿去做分析,來看看結果在來考慮下一步。

from sklearn.linear_model import LinearRegression # 引入LinearRegression
from sklearn.model_selection import train_test_split # 引入train_test_split
import matplotlib.pyplot as plt # 引入pyplot

x = data.data # 13個特徵的數據
y = data.target # 房價數據

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3, random_state = 1) # 將數據分成73比
lnregr = LinearRegression()
lnregr.fit(x_train, y_train) # 將資料拿去訓練

y_predict = lnregr.predict(x_test) # 北test的資料用訓練出來的模型去預測

plt.xlabel("actual price") # x軸的標題
plt.ylabel("predict pcice") # y軸的標題
plt.plot([0,50], [0,50]) # 劃一條基準線
plt.scatter(y_test, y_predict) # 比對預測跟實際的差別
plt.show() # 察看結果

output

lnregr.score(x_train, y_train) # 訓練模型的正確率

output

0.7103879080674731

思考 : 為甚麼正確率這麼低呢?因為線性迴歸的的方程式為一條直線,當特徵與房價不成線性關係的話,那麼再怎麼分析,正確率都不會高。
解決:

  1. 只討論呈線性關係的特徵
  2. 使用多項式迴歸
  3. 降維

而前兩種解決方法這裡就不再詳細討論了。


特徵重要性

我們在上面的分析是使用直接13個特徵,如果我們可以只選取比較重要的特徵出來討論,會不會得到更好的結果呢?特別注意到如果要找尋重要性的話,一定要先做標準化,不然數據跑出來的結果會整個失真。

import pandas as pd # 引入pandas
import numpy as np # 引入numpy
from sklearn.tree import DecisionTreeClassifier # 引入DecisionTreeClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA # 引入PCA
import matplotlib.pyplot as plt # 引入pyplot

x = data.data # 13個特徵的數據
y = data.target # 特徵名稱

std_tool = StandardScaler() 
x = std_tool.fit_transform(x) # 將資料標準化

dt_model = LinearRegression()
dt_model.fit(x,y)
feature_importance = dt_model.coef_ # 重要性

plt.figure(figsize=(12, 6)) # 圖形大小
plt.bar(df_data.columns , feature_importance) # 繪製成直方圖
plt.show() # 顯示圖形


基本上數值(取絕對值後)越大代表越重要。因此我們決定將CRIMINDUSCHASAGEB刪除,再做一次線性迴歸。

df_data2 = df_data.drop(["CRIM", "INDUS", "CHAS", "AGE", "B"], axis = 1)

x = df_data2 # 重要特徵的數據
y = data.target # 房價數據

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3, random_state = 1) # 將數據分成73比

std_tool = StandardScaler() 
x_train = std_tool.fit_transform(x_train) # 將資料標準化

lnregr = LinearRegression()
lnregr.fit(x_train, y_train) # 將資料拿去訓練

x_test = std_tool.transform(x_test)
y_predict = lnregr.predict(x_test) # 北test的資料用訓練出來的模型去預測

plt.xlabel("actual price") # x軸的標題
plt.ylabel("predict pcice") # y軸的標題
plt.plot([0,50], [0,50]) # 劃一條基準線
plt.scatter(y_test, y_predict) # 比對預測跟實際的差別
plt.show() # 察看結果

output

lnregr.score(x_train, y_train) # 訓練模型的正確率

output

0.7723543146059919

0.7609335561698132


PCA降維

因為數據只有506筆,基本上當數據量越少,所需的維度就要少,因此我們使用PCA降維的方式,它是屬於線性的降維。

from sklearn.model_selection import train_test_split # 引入train_test_split
from sklearn import decomposition # 引入decomposition
from sklearn.preprocessing import StandardScaler

x = data.data # pca降維後的數據
y = data.target # 房價數據

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3, random_state = 1) # 將數據分成73比

# Standarize our training data
std_tool = StandardScaler()
std_tool.fit(x_train)
x_train = std_tool.transform(x_train)

# PC降維
pca = decomposition.PCA(n_components=0.95)
pca.fit(x_train)
x_train = pca.transform(x_train)
from sklearn.linear_model import LinearRegression # 引入LinearRegression
import matplotlib.pyplot as plt # 引入pyplot

lnregr = LinearRegression()
lnregr.fit(x_train, y_train) # 將資料拿去訓練

# Standarize x_test
x_test = std_tool.transform(x_test)

# Dimension reduction usng PCA
x_test = pca.transform(x_test)
y_predict = lnregr.predict(x_test) # 將test的資料用訓練出來的模型去預測

plt.xlabel("actual price") # x軸的標題
plt.ylabel("predict pcice") # y軸的標題
plt.plot([0,50], [0,50]) # 劃一條基準線
plt.scatter(y_test, y_predict) # 比對預測跟實際的差別
plt.show() # 察看結果
lnregr.score(x_train, y_train) # 訓練模型的正確率

output

0.7723543146059919

結語

我們在這篇文章僅做了一些初步的分析,尚未找到最好的模型,可以考慮SVM或決策樹,但是它的分析步驟,就類似這樣模式,在分析的過程中延伸問題,改變問題,尋找更好的方向,得到更加有用的結果。


參考資料


上一篇
[Series - 8] Python時間轉換介紹
下一篇
[Series - 10] 資料篇總結
系列文
資幾資比31

尚未有邦友留言

立即登入留言