做者:xiaoyupython
微信公衆號:Python數據科學正則表達式
知乎:python數據分析師算法
上一篇和你們分享了一個入門數據分析的一個小項目 北京二手房房價分析,連接以下:數組
本篇將繼續上一篇數據分析以後進行數據挖掘建模預測,這兩部分構成了一個簡單的完整項目。結合兩篇文章經過數據分析和挖掘的方法能夠達到二手房屋價格預測的效果。bash
下面從特徵工程開始講述。微信
特徵工程包括的內容不少,有特徵清洗,預處理,監控等,而預處理根據單一特徵或多特徵又分不少種方法,如歸一化,降維,特徵選擇,特徵篩選等等。這麼多的方法,爲的是什麼呢?其目的是讓這些特徵更友好的做爲模型的輸入,處理數據的好壞會嚴重的影響模型性能,而好的特徵工程有的時候甚至比建模調參更重要。app
下面是繼上一次分析以後對數據進行的特徵工程,博主將一個一個幫你們解讀。dom
""" 特徵工程 """
# 移除結構類型異常值和房屋大小異常值
df = df[(df['Layout']!='疊拼別墅')&(df['Size']<1000)]
# 去掉錯誤數據「南北」,由於爬蟲過程當中一些信息位置爲空,致使「Direction」的特徵出如今這裏,須要清除或替換
df['Renovation'] = df.loc[(df['Renovation'] != '南北'), 'Renovation']
# 因爲存在個別類型錯誤,如簡裝和精裝,特徵值錯位,故須要移除
df['Elevator'] = df.loc[(df['Elevator'] == '有電梯')|(df['Elevator'] == '無電梯'), 'Elevator']
# 填補Elevator缺失值
df.loc[(df['Floor']>6)&(df['Elevator'].isnull()), 'Elevator'] = '有電梯'
df.loc[(df['Floor']<=6)&(df['Elevator'].isnull()), 'Elevator'] = '無電梯'
# 只考慮「室」和「廳」,將其它少數「房間」和「衛」移除
df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室']
# 提取「室」和「廳」建立新特徵
df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64')
df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64')
# 按中位數對「Year」特徵進行分箱
df['Year'] = pd.qcut(df['Year'],8).astype('object')
# 對「Direction」特徵
d_list_one = ['東','西','南','北']
d_list_two = ['東西','東南','東北','西南','西北','南北']
d_list_three = ['東西南','東西北','東南北','西南北']
d_list_four = ['東西南北']
df['Direction'] = df['Direction'].apply(direct_func)
df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]
# 根據已有特徵建立新特徵
df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num']
df['Size_room_ratio'] = df['Size']/df['Layout_total_num']
# 刪除無用特徵
df = df.drop(['Layout','PerPrice','Garden'],axis=1)
# 對於object特徵進行onehot編碼
df,df_cat = one_hot_encoder(df)
複製代碼
因爲一些清洗處理在上一篇文章已經提到,博主從17行代碼開始。函數
先來看看沒經處理的Layout特徵值是什麼樣的。性能
df['Layout'].value_counts()
複製代碼
你們也都看到了,特徵值並非像想象中的那麼理想。有兩種格式的數據,一種是"xx室xx廳"
,另外一種是"xx房間xx衛"
,可是絕大多數都是xx室xx廳的數據。而對於像"11房間3衛"
或者"5房間0衛"
這些的Layout明顯不是民住的二手房(不在咱們的考慮範圍以內),所以最後決定將全部"xx房間xx衛"格式的數據都移除掉,只保留"xx室xx廳"
的數據。
Layout特徵的處理以下:
第2行的意思是隻保留"xx室xx廳"數據,可是保留這種格式的數據也是不能做爲模型的輸入的,咱們不如干脆將"室"和"廳"都提取出來,單獨做爲兩個新特徵(如第5和6行),這樣效果可能更好。
具體的用法就是使用 str.extract()
方法,裏面寫的是正則表達式。
# 只考慮「室」和「廳」,將其它少數「房間」和「衛」移除
df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室']
# 提取「室」和「廳」建立新特徵
df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64')
df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64')
複製代碼
咱們還有一個 Year
特徵,爲建房的年限時間。年限種類不少,分佈在1950和2018之間,若是每一個不一樣的 Year 值都做爲特徵值,咱們並不能找出 Year 對 Price 有什麼影響,由於年限劃分的太細了。所以,咱們只有將連續數值型特徵 Year 離散化,作分箱處理。
如何分箱還要看實際業務需求,博主爲了方便並無手動分箱,而使用了pandas的 qcut 採用中位數進行分割,分割數爲8等份。
# 按中位數對「Year」特徵進行分箱
df['Year'] = pd.qcut(df['Year'],8).astype('object')
複製代碼
這是將 Year 進行分箱的結果:
這個特徵沒處理以前更亂,原覺得是爬蟲的問題,可是親自到鏈家看過,朝向確實是這樣的。
如上所見,像"西南西北北"
或者"東東南南"
這樣的朝向是不符合常識的(反正我是理解不了)。所以,咱們須要將這些凌亂的數據進行處理,具體實現方式是博主本身寫了一個函數 direct_func
,主要思想就是將各類重複但順序不同的特徵值合併,好比"西南北"
和"南西北"
,並將不合理的一些值移除,如"西南西北北"
等。
而後經過 apply()
方法將 Direction 數據格式轉換,代碼以下:
# 對「Direction」特徵
d_list_one = ['東','西','南','北']
d_list_two = ['東西','東南','東北','西南','西北','南北']
d_list_three = ['東西南','東西北','東南北','西南北']
d_list_four = ['東西南北']
df['Direction'] = df['Direction'].apply(direct_func)
df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]
複製代碼
處理完結果以下,全部的內容相同而順序不一樣的朝向都合併了,異常朝向也被移除了。
有時候僅靠已有的一些特徵是不夠的,須要根據對業務的理解,定義一些的新特徵,而後嘗試這些新特徵對模型的影響,在實戰中會常用這種方法。
這裏嘗試將"室"與"廳"的數量相加做爲一個總數量特徵,而後將房屋大小Size與總數量的比值做爲一個新特徵,可理解爲 "每一個房間的平均面積大小"
。固然,新特徵不是固定的,可根據本身的理解來靈活的定義。
# 根據已有特徵建立新特徵
df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num']
df['Size_room_ratio'] = df['Size']/df['Layout_total_num']
# 刪除無用特徵
df = df.drop(['Layout','PerPrice','Garden'],axis=1)
複製代碼
最後刪除舊的特徵 Layout,PerPrice,Garden。
這部分是 One-hot 獨熱編碼
,由於像 Region,Year(離散分箱後),Direction,Renovation,Elevator等特徵都是定類的非數值型類型,而做爲模型的輸入咱們須要將這些非數值量化。
在沒有必定順序(定序類型)的狀況下,使用獨熱編碼處理定類數據是很是經常使用的作法,在pandas中很是簡單,就是使用 get_dummies()
方法,而對於像Size這樣的定比數據則不使用獨熱,博主這裏用了一個本身封裝的函數實現了定類數據的自動量化處理。
對於定類,定序,定距,定比這四個很是重要的數據類型相信加入知識星球的夥伴都很是熟悉了,想要了解的同窗能夠掃描最後二維碼查看。
# 對於object特徵進行onehot編碼
df,df_cat = one_hot_encoder(df)
複製代碼
以上的特徵工程就完成了。
下面使用 seaborn
的 heatmap
方法對特徵相關性進行可視化。
# data_corr
colormap = plt.cm.RdBu
plt.figure(figsize=(20,20))
# plt.title('Pearson Correlation of Features', y=1.05, size=15)
sns.heatmap(df.corr(),linewidths=0.1,vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True)
複製代碼
顏色偏紅或者偏藍都說明相關係數較大,即兩個特徵對於目標變量的影響程度類似,即存在嚴重的重複信息,會形成過擬合現象。所以,經過特徵相關性分析,咱們能夠找出哪些特徵有嚴重的重疊信息,而後擇優選擇。
爲了方便理解,博主在建模上作了一些精簡,模型策略方法以下:
Cart決策樹
的迴歸模型對二手房房價進行分析預測交叉驗證
方法充分利用數據集進行訓練,避免數據劃分不均勻的影響。GridSearchCV
方法優化模型參數R2評分
方法對模型預測評分上面的建模方法比較簡單,旨在讓你們瞭解建模分析的過程。隨着逐漸的深刻了解,博主會介紹更多實戰內容。
# 轉換訓練測試集格式爲數組
features = np.array(features)
prices = np.array(prices)
# 導入sklearn進行訓練測試集劃分
from sklearn.model_selection import train_test_split
features_train, features_test, prices_train, prices_test = train_test_split(features, prices, test_size=0.2, random_state=0)
複製代碼
將以上數據劃分爲訓練集和測試集,訓練集用於創建模型,測試集用於測試模型預測準確率。使用sklearn的 model_selection
實現以上劃分功能。
from sklearn.model_selection import KFold
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import make_scorer
from sklearn.model_selection import GridSearchCV
# 利用GridSearchCV計算最優解
def fit_model(X, y):
""" 基於輸入數據 [X,y],利於網格搜索找到最優的決策樹模型"""
cross_validator = KFold(10, shuffle=True)
regressor = DecisionTreeRegressor()
params = {'max_depth':[1,2,3,4,5,6,7,8,9,10]}
scoring_fnc = make_scorer(performance_metric)
grid = GridSearchCV(estimator = regressor, param_grid = params, scoring = scoring_fnc, cv = cross_validator)
# 基於輸入數據 [X,y],進行網格搜索
grid = grid.fit(X, y)
# print pd.DataFrame(grid.cv_results_)
return grid.best_estimator_
# 計算R2分數
def performance_metric(y_true, y_predict):
"""計算並返回預測值相比於預測值的分數"""
from sklearn.metrics import r2_score
score = r2_score(y_true, y_predict)
return score
複製代碼
使用了 KFold
方法減緩過擬合,GridSearchCV
方法進行最優參數自動搜查,最後使用R2
評分來給模型打分。
import visuals as vs
# 分析模型
vs.ModelLearning(features_train, prices_train)
vs.ModelComplexity(features_train, prices_train)
optimal_reg1 = fit_model(features_train, prices_train)
# 輸出最優模型的 'max_depth' 參數
print("最理想模型的參數 'max_depth' 是 {} 。".format(optimal_reg1.get_params()['max_depth']))
predicted_value = optimal_reg1.predict(features_test)
r2 = performance_metric(prices_test, predicted_value)
print("最優模型在測試數據上 R^2 分數 {:,.2f}。".format(r2))
複製代碼
因爲決策樹容易過擬合的問題,咱們這裏採起觀察學習曲線的方法查看決策樹深度,並判斷模型是否出現了過擬合現象。如下是觀察到的學習曲線圖形:
經過觀察,最理想模型的參數"max_depth"
是10,此種狀況下達到了誤差與方差的最優平衡,最後模型在測試數據上的R2分數,也即二手房房價預測的準確率爲:0.81
。
以上一個完整的從數據分析到挖掘的項目就結束了,對於項目而言比較簡單,目的是讓你們瞭解整個分析的過程。可提高改進的地方很是多,能夠有更好更健壯的方案代替,一些改進思考以下:
完整項目代碼博主分享在了知識星球
中,後續博主將不斷分享更多實戰內容,Kaggle競賽項目
,以及互聯網金融風險控制項目
,加入星球掃請描如下二維碼:
關注微信公衆號:Python數據科學,查看更多精彩內容。