本次實戰項目的主要目的是分析北京二手房房價,項目源自博文:入門Python數據分析最好的實戰項目(一)和入門Python數據分析最好的實戰項目(二)。本篇文章僅記錄博主在學習過程當中的思路。segmentfault
首先咱們要對數據進行分析,可分爲如下幾個主要步驟:網絡
這裏咱們重點要講的是數據可視化分析,即對一些重要對特徵逐個畫圖觀察。app
打開表格:
dom
咱們看到上述數據有 11 個特徵變量,1 個目標變量 Price。11 個特徵分別爲:
Direction
District
Elevator
Floor
Garden
Id
Layout
Region
Renovation
Size
Year機器學習
咱們分別對 Elevator, Floor, Layout, Region, Renovation, Size, Year 這 7 個特徵進行可視化分析。函數
代碼:性能
# Elevator 特徵分析 miss_value = len(df.loc[(df['Elevator'].isnull()), 'Elevator']) print('Elevator缺失值個數爲:' + str(miss_value)) # 移除表格中可能存在的錯誤的值 df['Elevator'] = df.loc[(df['Elevator']=='有電梯') | (df['Elevator']=='無電梯'), 'Elevator'] # 以樓層大於6的有電梯,小於等於6層沒有電梯爲標準,填補缺失值 df.loc[(df['Floor']>6) & (df['Elevator'].isnull()), 'Elevator'] == '有電梯' df.loc[(df['Floor']<=6) & (df['Elevator'].isnull()), 'Elevator'] == '無電梯' f, [ax1, ax2] = plt.subplots(1, 2, figsize=(20,10)) sns.countplot(df['Elevator'], ax=ax1) ax1.set_title('有無電梯數量對比') ax1.set_xlabel('是否有電梯') ax1.set_ylabel('數量') sns.barplot(x='Elevator', y='Price', data=df, ax=ax2) ax2.set_title('有無電梯價格對比') ax2.set_xlabel('是否有電梯') ax2.set_ylabel('價格') plt.show()
執行結果:
學習
分析目的:
分析有無電梯兩種二手房對數量和價格。測試
使用方法:
採用seaborn
完成可視化。優化
觀察結果:
咱們發現 Elevator
特徵是有大量缺失值。通常有大量缺失值時,須要根據實際狀況考慮。經常使用的方法有平均值/中位數填補法,直接移除,或根據其餘特徵建模預測等。
這裏咱們用填補法。因爲有無電梯不是數值,不存在平均值和中位數,這裏根據樓層 (Floor) 斷有無電梯,通常的樓層大於 6 的都有電梯,而小於等於 6 層的通常都沒有電梯。
在填補缺失值後繼續觀察,有電梯的二手房數量更多,且房價較高。
代碼:
# Floor 特徵分析 f, ax1 = plt.subplots(figsize=(20,5)) sns.countplot(df['Floor'], ax=ax1) ax1.set_title('各樓層二手房數量', fontsize=15) ax1.set_xlabel('樓層') ax1.set_ylabel('數量') plt.show()
執行結果:
分析目的:
分析不一樣的樓層二手房數量。
使用方法:
採用seaborn
完成可視化。
觀察結果:
其中 6 層的二手房數量最多,可是單獨的樓層特徵沒有什麼意義,由於每一個小區住房的總樓層數都不同,咱們須要知道樓層的相對高度。
此外,樓層與文化也有很重要的聯繫,好比在中國文化有七上八下,七層可能受歡迎等。通常來講中間樓層比較受歡迎,價格也高,底層和頂層受歡迎度較低,價格也相對較低。
樓層是一個很是複雜的特徵,對房價影響也比較大。
代碼:
# Layout特徵分析 f, ax1 = plt.subplots(figsize=(20, 20)) sns.countplot(y='Layout', data=df, ax=ax1) ax1.set_title('房屋戶型', fontsize=15) ax1.set_xlabel('數量') ax1.set_ylabel('戶型') plt.show()
執行結果:
分析目的:
分析不一樣戶型的數量。
使用方法:
採用seaborn
完成可視化。
觀察結果:
這個特徵分類下有不少不規則的命名,以上特徵是不能做爲機器學習模型的數據輸入的,須要使用特徵工程進行相應的處理。
代碼:
df_house_count = df.groupby('Region')['Price'].count().sort_values(ascending=False).to_frame().reset_index() df_house_mean = df.groupby('Region')['PerPrice'].mean().sort_values(ascending=False).to_frame().reset_index() f, [ax1, ax2, ax3] = plt.subplots(3, 1, figsize=(20,15)) sns.barplot(x='Region', y='PerPrice', palette='Blues_d', data=df_house_mean, ax=ax1) ax1.set_title('北京各區二手房每平米單價對比', fontsize=15) ax1.set_xlabel('區域') ax1.set_ylabel('每平米單價') sns.barplot(x='Region', y='Price', palette="Greens_d", data=df_house_count, ax=ax2) ax2.set_title('北京各大區二手房數量對比',fontsize=15) ax2.set_xlabel('區域') ax2.set_ylabel('數量') sns.boxplot(x='Region', y='Price', data=df, ax=ax3) ax3.set_title('北京各大區二手房房屋總價',fontsize=15) ax3.set_xlabel('區域') ax3.set_ylabel('房屋總價') plt.show()
執行結果:
分析目的:
分析不一樣區域的房價和數量,並進行對比。
使用方法:
用pandas
的網絡透視功能groupby
分組排序。
區域特徵可視化採用seaborn
完成。
顏色使用調色板palette
參數,顏色越淺數量越少,反之越多。
觀察結果:
二手房每平方米單價對比:西城區的房價最貴均價大約 11 萬/平,由於西城在二環以裏,且是熱門學區房的彙集地。其次是東城大約 10 萬/平,而後是海淀大約 8.5 萬/平,其它均低於 8 萬/平。
二手房房數量對比:從數量統計上來看,海淀區和朝陽區二手房數量最多,約接近 3000 套,由於兩者屬於大區。其次是豐臺區,近幾年正在改造建設,需求量大。
二手房房屋總價對比:經過箱型圖看到,各大區域房屋總價中位數都都在 1000 萬如下,且房屋總價離散值較高,西城最高達到了 6000 萬,說明房屋價格特徵並非理想的正態分佈。
代碼:
# Renovation 特徵分析 df['Renovation'].value_counts() f, [ax1, ax2, ax3] = plt.subplots(1, 3, figsize=(20,5)) sns.countplot(df['Renovation'], ax=ax1) sns.barplot(x='Renovation', y='Price', data=df, ax=ax2) sns.boxplot(x='Renovation', y='Price', data=df, ax=ax3) plt.show()
執行結果:
分析目的:
分析不一樣裝修程度的二手房數量和房價。
使用方法:
採用seaborn
完成可視化。
觀察結果:
對於數量來講,精裝修的二手房最多,簡裝其次;對於價格來講,毛坯房價格最高,其次是精裝修的。
代碼:
# Size特徵分析 f, [ax1, ax2] = plt.subplots(1, 2, figsize=(15,5)) # 建房時間分佈狀況 sns.distplot(df['Size'], bins=20, ax=ax1, color='r') sns.kdeplot(df['Size'], ax=ax1, shade=True) # 建房時間和出售價格的關係 sns.regplot(x='Size', y='Price', data=df, ax=ax2) plt.show() # 查看異常值 df.loc[df['Size'] < 10] df.loc[df['Size'] > 1000] # 移除上述兩種異常值 df = df[(df['Layout']!='疊拼別墅') & (df['Size']<1000)] # 從新進行可視化發現就沒有明顯的異常點 sns.regplot(x='Size', y='Price', data=df) plt.show()
執行結果:
分析目的:
分析不一樣大小的二手房和價格的關係。
使用方法:
經過distplot
和 kdeplot
繪製柱狀圖觀察 Size
特徵的分佈狀況,屬於長尾類型的分佈,這說明有不少面積很大且超出正常範圍的二手房。
經過 regplot
繪製了 Size
和 Price
之間的散點圖,發現 Size
特徵基本與Price
呈現線性關係,符合基本常識,面積越大,價格越高。
觀察結果:
有兩組明顯的異常點:面積不到 10 平米但價格超出 10000 萬和麪積超過了 1000 平米價格很低兩種狀況。
通過查看發現這兩組異常值分別是別墅和商用房,所以出現異常,故將其移除再次觀察Size分佈和Price關係。
這裏也說明咱們在觀察數據的時候,要緊密結合實際業務需求來分析,才能得出更準確的結果。
代碼:
# Year 特徵分析 grid = sns.FacetGrid(df, row='Elevator', col='Renovation', palette='seismic', size=4) grid.map(plt.scatter, 'Year', 'Price') # grid.add_legend()
執行結果:
分析目的:
分析不一樣年代對房價變化的影響。
使用方法:
在 Renovation 和 Elevator 的分類條件下,使用 FacetGrid
分析 Year 特徵
觀察結果:
觀察數據可視化圖表能夠看出,整個二手房房價趨勢是隨着時間增加而增加的,2000 年之後建造的二手房房價相較於 2000 年之前有很明顯的價格上漲。此外,1980年以前幾乎不存在有電梯二手房數據,說明1980年以前尚未大面積安裝電梯,且在 1980 年以前無電梯二手房中,簡裝二手房佔絕大多數,精裝反而不多。
特徵工程的目的是讓這些特徵更友好的做爲模型的輸入,處理數據的好壞會嚴重的影響模型性能。
這裏咱們對已有的 Layout 特徵,Year 特徵和 Direction 特徵進行處理,建立新特徵,刪除無用特徵,最後進行 One-hot 獨熱編碼。
處理 Layout 特徵
df['Layout'].value_counts() # 移除X房間X衛的格式 非民住 df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室'] df.head() # 用 str.extract() 方法,將"室"和"廳"都提取出來,單獨做爲兩個新特徵 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 特徵
# 將連續數值型特徵 Year 離散化,作分箱處理 # 如何分箱還要看實際業務需求,這裏爲了方便,使用了pandas的 qcut 採用中位數進行分割,分割數爲8等份 df['Year'] = pd.qcut(df['Year'], 8).astype('object') df['Year'].value_counts()
處理 Direction 特徵
df['Direction'].value_counts() # 寫函數 direct_func 來整理上面較亂的 Direction def direct_func(x): if not isinstance(x,str): raise TypeError x = x.strip() x_len = len(x) x_list = pd.unique([y for y in x]) if x_len != len(x_list): return 'no' if (x_len == 2) & (x not in d_list_two): m0 = x[0] m1 = x[1] return m1+m0 elif (x_len == 3) & (x not in d_list_three): for n in d_list_three: if (x_list[0] in n) & (x_list[1] in n) & (x_list[2] in n): return n elif (x_len == 4) & (x not in d_list_four): return d_list_four[0] else: return x # 經過 apply() 方法將 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['Direction'].value_counts()
建立新特徵
# 根據對業務的理解,定義新特徵,而後觀察這些新特徵對模型有什麼影響 # 根據已有特徵建立新特徵 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', 'District'], axis=1) df.head()
One-hot 獨熱編碼
是將定類的非數值型類型量化
的一種方法,在pandas
中使用 get_dummies()
方法實現。這裏使用一個自定義的封裝的函數實現了定類數據的自動量化處理。
def one_hot_encoder(df, nan_as_category = True): original_columns = list(df.columns) categorical_columns = [col for col in df.columns if df[col].dtype == 'object'] df = pd.get_dummies(df, columns= categorical_columns, dummy_na= nan_as_category) new_columns = [c for c in df.columns if c not in original_columns] return df, new_columns # 對於object特徵進行onehot編碼 df, df_cat = one_hot_encoder(df)
特徵相關性
對數據通過以上處理後,能夠用 seaborn
的 heatmap
方法對特徵相關性進行可視化。
colormap = plt.cm.RdBu plt.figure(figsize=(20, 20)) sns.heatmap(df.corr(), linewidth=0.1, vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True)
heatmap
能夠根據顏色觀察特徵的相關性。顏色偏紅或者偏藍都說明相關係數較大,即兩個特徵對於目標變量的影響程度類似,也就是說存在嚴重的重複信息,會形成過擬合現象。
咱們能經過特徵相關性分析,找出哪些特徵有嚴重的重疊信息,而後擇優選擇。
這裏還須要注意特徵太多有可能會致使 heatmap
圖畫失敗。
本次建模主要方法爲:使用Cart決策樹的迴歸模型
對二手房房價進行分析預測;使用交叉驗證方法
充分利用數據集進行訓練,避免數據劃分不均勻的影響;使用GridSearchCV方法
優化模型參數;使用R2評分方法
對模型預測評分。
數據劃分
# 特徵變量和目標變量 features = df.drop('Price', axis=1) prices = df['Price'] # 把分類特徵都轉成數值型後有{}行{}列 print('北京二手房房價有數據 {0} 條,字段 {1} 個' .format(*df.shape)) # 將數據集劃分爲訓練集與測試集 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)
創建模型
# 創建模型 from sklearn.model_selection import KFold from sklearn.tree import DecisionTreeRegressor from sklearn.metrics import make_scorer from sklearn.model_selection import GridSearchCV # 經過交叉認證緩解數據集過擬合的現象 # 創建決策樹迴歸模型 # 經過GridSearchCV找到最優深度參數(基於輸入數據[X,y] 利於網格搜索找到最優的決策樹模型) def fit_model(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) # 網格搜索 grid = grid.fit(X, y) return grid.best_estimator_
評估驗證
# 計算 R2 分數 from sklearn.metrics import r2_score def performance_metric(y_true, y_predict): score = r2_score(y_true, y_predict) return score # 調參優化模型 # 經過可視化模型學習曲線,觀察是否出現過擬合問題 # visuals 爲自定義函數 import visuals as vs # 分析模型 vs.ModelLearning(features_train, prices_train) vs.ModelComplexity(features_train, prices_train) optimal = fit_model(features_train, prices_train) # 輸出最優模型的參數 'max_depth' print('最優模型的參數 max_depth 是: {} ' .format(optimal.get_params()['max_depth'])) predicted_value = optimal.predict(features_test) r2 = performance_metric(prices_test, predicted_value) # 每次交叉驗證獲得的數據集不一樣,所以每次運行的結果也不必定相同 print('最優模型在測試數據上 R^2 分數 {: .2f}' .format(r2))
能夠看到,最理想模型的參數max_depth
是 10,此時達到了誤差與方差的最優平衡。模型在測試數據上的 R2 分數
爲:0.77,即二手房房價預測的準確率。
以上,完成了一個項目的簡單分析。能夠改進的方向有如下 3 個:
不足之處,歡迎指正