广州二手房价分析与预测
一、概述
?
1.1問(wèn)題介紹與分析
隨著社會(huì)經(jīng)濟(jì)的迅猛發(fā)展,房地產(chǎn)開(kāi)發(fā)建設(shè)的速度越來(lái)越快,二手房市場(chǎng)迅猛發(fā)展,對(duì)二手房房產(chǎn)價(jià)格評(píng)估的需求也隨之增大.因此,對(duì)二手房房?jī)r(jià)預(yù)測(cè)與分析是必要的.詳細(xì)文檔與代碼資料保存在我的百度云盤(pán)。
數(shù)據(jù)收集:在鏈家網(wǎng)站上收集廣州二手房數(shù)據(jù),把數(shù)據(jù)分為訓(xùn)練集、測(cè)試集。訓(xùn)練和測(cè)試數(shù)據(jù)是一些二手房信息以及價(jià)格狀況,要嘗試根據(jù)它生成合適的模型并預(yù)測(cè)其他二手房的價(jià)格狀況。這是一個(gè)回歸問(wèn)題,很多回歸算法都可以解決。
收集工具:八爪魚(yú)
?
?
1.2總體思路
首先從數(shù)據(jù)清洗開(kāi)始,對(duì)缺失值的多維度處理、對(duì)離群點(diǎn)的剔除方法以及對(duì)字符、空格等的處理;其次進(jìn)行特征工程,包括類別特征編碼、連續(xù)特征編碼等;再次進(jìn)行特征選擇,采用了xgboost,xgboost的訓(xùn)練過(guò)程即對(duì)特征重要性的排序過(guò)程;最后一部分是模型設(shè)計(jì)與分析,采用了嶺回歸模型、xgboost,取得了不錯(cuò)的效果,此外還介紹了模型融合方法。
?
二、 數(shù)據(jù)清洗
?
2.1缺失值的多維度處理
(1)通常遇到缺值的情況,我們會(huì)有幾種常見(jiàn)的處理方式
①如果缺值的樣本占總數(shù)比例極高,我們可能就直接舍棄了,作為特征加入的話,可能反倒帶入noise,影響最后的結(jié)果了
②如果缺值的樣本適中,而該屬性非連續(xù)值特征屬性(比如說(shuō)類目屬性),那就把NaN作為一個(gè)新類別,加到類別特征中
③如果缺值的樣本適中,而該屬性為連續(xù)值特征屬性,有時(shí)候我們會(huì)考慮給定一個(gè)step(比如這里的age,我們可以考慮每隔2/3歲為一個(gè)步長(zhǎng)),然后把它離散化,之后把NaN作為一個(gè)type加到屬性類目中。
(2)有些情況下,缺失的值個(gè)數(shù)并不是特別多,那我們也可以試著根據(jù)已有的值,擬合一下數(shù)據(jù),補(bǔ)充上。
2.2數(shù)據(jù)預(yù)處理
#載入數(shù)據(jù):
train=pd.read_csv('train.csv')
test= pd.read_csv('test.csv')
#檢視源數(shù)據(jù)
print(train.head())
?
#合并數(shù)據(jù)
這么做主要是為了用DF進(jìn)行數(shù)據(jù)預(yù)處理的時(shí)候更加方便。等所有的需要的預(yù)處理進(jìn)行完之后,我們?cè)侔阉麄兎指糸_(kāi)。
首先,價(jià)格作為我們的訓(xùn)練目標(biāo),只會(huì)出現(xiàn)在訓(xùn)練集中,不會(huì)在測(cè)試集中。所以,我們先把價(jià)格這一列給拿出來(lái)。
?
#可視化價(jià)格特征
prices= pd.DataFrame({"price":train["價(jià)格"],"log(price + 1)":np.log1p(train["價(jià)格"])})
prices.hist()
#可見(jiàn),label本身并不平滑。為了分類器的學(xué)習(xí)更加準(zhǔn)確,首先把label給“平滑化”(正態(tài)化)
#這里使用log1p, 也就是 log(x+1),避免了復(fù)值的問(wèn)題。值得注意的是,如果這里把數(shù)據(jù)都給平滑化了,那么最后算結(jié)果的時(shí)候,要把預(yù)測(cè)到的平滑數(shù)據(jù)給變回去。按照“怎么來(lái)的怎么去”原則,log1p()就需要expm1(); 同理,log()就需要exp()
?
y_train= np.log1p(train.pop('價(jià)格'))
#然后把剩下的部分合并起來(lái)?????
all_df= pd.concat((train, test), axis=0)
?
三、特征工程
?
#變量轉(zhuǎn)換
all_df["樓齡"]=2017-all_df["樓齡"]
#有一些數(shù)據(jù)是缺失的,根據(jù)缺失值的多少進(jìn)行相應(yīng)的處理
#查看缺失情況
print(all_df.isnull().sum().sort_values(ascending=False).head(10))
#可以看到,缺失最多的column是裝修情況。由于裝修情況的值缺失太多就刪掉這一列
all_df.pop("裝修情況")
?
#接著處理樓齡的缺失值,由于缺失不多,這里我用平均值進(jìn)行填充
mean_col= all_df["樓齡"].mean()
all_df["樓齡"]=all_df["樓齡"].fillna(mean_col)
?
#由于變量交易權(quán)屬 建筑類型 配備電梯的絕大多數(shù)的值相同,區(qū)分的效果不會(huì)很明顯這里就刪掉了
all_df.pop("建筑類型")
all_df.pop("配備電梯")
all_df.pop("交易權(quán)屬")
#該特征很多都不相同,這里簡(jiǎn)單處理就刪掉了
all_df.pop("房型")
?
#看看是不是沒(méi)有空缺了?
print(all_df.isnull().sum().sum())
?
#把category的變量轉(zhuǎn)變成numerical表達(dá)形式
#當(dāng)我們用numerical來(lái)表達(dá)categorical的時(shí)候,要注意,數(shù)字本身有大小的含義所以亂用數(shù)字會(huì)給之后的模型學(xué)習(xí)帶來(lái)麻煩。于是我們可以用One-Hot的方法來(lái)表達(dá)category。pandas自帶的get_dummies方法可以幫你一鍵做到One-Hot。
pd.get_dummies(all['區(qū)域'], prefix='區(qū)域')
#此刻區(qū)域被分成了5個(gè)column,每一個(gè)代表一個(gè)category。是就是1,不是就是0。
#同理,我們把所有的category數(shù)據(jù),都給One-Hot了
all_dummy_df= pd.get_dummies(all_df)
?
#標(biāo)準(zhǔn)化numerical數(shù)據(jù)
#這一步并不是必要,但是得看你想要用的分類器是什么。一般來(lái)說(shuō),regression的分類器最好是把源數(shù)據(jù)給放在一個(gè)標(biāo)準(zhǔn)分布內(nèi)。不要讓數(shù)據(jù)間的差距太大。這里,我們當(dāng)然不需要把One-Hot的那些0/1數(shù)據(jù)給標(biāo)準(zhǔn)化。我們的目標(biāo)應(yīng)該是那些本來(lái)就是numerical的數(shù)據(jù):
?
#先來(lái)看看哪些是numerical的:
numeric_cols= all_df.columns[all_df.dtypes != 'object']
#這里也是用log1p()方法進(jìn)行平滑
all_dummy_df["樓齡"]=np.log1p(all_dummy_df["樓齡"])
all_dummy_df["建筑面積"]=np.log1p(all_dummy_df["建筑面積"])
?
四、 特征選擇
?
在特征工程部分,這么多維特征一方面可能會(huì)導(dǎo)致維數(shù)災(zāi)難,另一方面很容易導(dǎo)致過(guò)擬合,需要做降維處理,降維方法常用的有如 PCA,t-SNE 等,這類方法的計(jì)算復(fù)雜度比較高。并且根據(jù)以往經(jīng)驗(yàn),PCA 或t-SNE 效果往往不好。除了采用降維算法之外,也可以通過(guò)特征選擇來(lái)降低特征維度。特征選擇的方法很多:最大信息系數(shù)(MIC)、皮爾森相關(guān)系數(shù)(衡量變量間的線性相關(guān)性)、正則化方法(L1,L2)、基于模型的特征排序方法。比較高效的是最后一種,即基于學(xué)習(xí)模型的特征排序方法,這種方法有一個(gè)好處:模型學(xué)習(xí)的過(guò)程和特征選擇的過(guò)程是同時(shí)進(jìn)行的,因此我們采用這種方法,基于xgboost 來(lái)做特征選擇,xgboost 模型訓(xùn)練完成后可以輸出特征的重要性,據(jù)此我們可以保留Top N 個(gè)特征,從而達(dá)到特征選擇的目的。
?
五、 模型設(shè)計(jì)與分析
?
5.1單個(gè)分類器
從訓(xùn)練數(shù)據(jù)中隨機(jī)劃分部分?jǐn)?shù)據(jù)作為驗(yàn)證集,剩下作為訓(xùn)練集,供模型調(diào)參使用。在訓(xùn)練集上采用十折交叉驗(yàn)證進(jìn)行模型調(diào)參。得到多個(gè)單模型結(jié)果后,再在驗(yàn)證集上做進(jìn)一步的模型融合。
#把數(shù)據(jù)集分回訓(xùn)練/測(cè)試集
dummy_train_df= all_dummy_df[:110]
dummy_test_df= all_dummy_df[110:-1]
X_train= dummy_train_df.values
X_test= dummy_test_df.values
(1)隨機(jī)森林
max_features= [.1, .3, .5, .7, .9, .99]
test_scores= []
formax_feat in max_features:
??? clf =RandomForestRegressor(n_estimators=200, max_features=max_feat)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=5, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
plt.plot(max_features,test_scores)
plt.title("MaxFeatures vs CV Error");
#用RF的最優(yōu)值達(dá)到了0.194
?
(2)這里我們用一個(gè)Stacking的思維來(lái)汲取兩種或者多種模型的優(yōu)點(diǎn)首先,我們把最好的parameter拿出來(lái),
做成我們最終的model
ridge= Ridge(alpha=1)
rf= RandomForestRegressor(n_estimators=500, max_features=.3)
ridge.fit(X_train,y_train)
rf.fit(X_train,y_train)
#上面提到了,因?yàn)樽钋懊嫖覀兘olabel做了個(gè)log(1+x), 這里我們需要把predit的值給exp回去,并且減掉那個(gè)"1"
所以就是我們的expm1()函數(shù)。
y_ridge= np.expm1(ridge.predict(X_test))
y_rf= np.expm1(rf.predict(X_test))
#一個(gè)正經(jīng)的Ensemble是把這群model的預(yù)測(cè)結(jié)果作為新的input,再做一次預(yù)測(cè)。
y_final= 0.8*y_ridge + 0.2*y_rf
print(y_final)
?
要判定一下當(dāng)前模型所處狀態(tài)(欠擬合or過(guò)擬合)
?
有一個(gè)很可能發(fā)生的問(wèn)題是,我們不斷地做feature engineering,產(chǎn)生的特征越來(lái)越多,用這些特征去訓(xùn)練模型,會(huì)對(duì)我們的訓(xùn)練集擬合得越來(lái)越好,同時(shí)也可能在逐步喪失泛化能力,從而在待預(yù)測(cè)的數(shù)據(jù)上,表現(xiàn)不佳,也就是發(fā)生過(guò)擬合問(wèn)題。從另一個(gè)角度上說(shuō),如果模型在待預(yù)測(cè)的數(shù)據(jù)上表現(xiàn)不佳,除掉上面說(shuō)的過(guò)擬合問(wèn)題,也有可能是欠擬合問(wèn)題,也就是說(shuō)在訓(xùn)練集上,其實(shí)擬合的也不是那么好。而在機(jī)器學(xué)習(xí)的問(wèn)題上,對(duì)于過(guò)擬合和欠擬合兩種情形。我們優(yōu)化的方式是不同的。
?
對(duì)過(guò)擬合而言,通常以下策略對(duì)結(jié)果優(yōu)化是有用的:
- 做一下feature selection,挑出較好的feature的subset來(lái)做training
- 提供更多的數(shù)據(jù),從而彌補(bǔ)原始數(shù)據(jù)的bias問(wèn)題,學(xué)習(xí)到的model也會(huì)更準(zhǔn)確
而對(duì)于欠擬合而言,我們通常需要更多的feature,更復(fù)雜的模型來(lái)提高準(zhǔn)確度。
著名的learning curve可以幫我們判定我們的模型現(xiàn)在所處的狀態(tài)。我們以樣本數(shù)為橫坐標(biāo),訓(xùn)練和交叉驗(yàn)證集上的錯(cuò)誤率作為縱坐標(biāo),兩種狀態(tài)分別如下兩張圖所示:過(guò)擬合,欠擬合
?
著名的learningcurve可以幫我們判定我們的模型現(xiàn)在所處的狀態(tài)。我們以樣本數(shù)為橫坐標(biāo),訓(xùn)練和交叉驗(yàn)證集上的錯(cuò)誤率作為縱坐標(biāo)
在實(shí)際數(shù)據(jù)上看,我們得到的learning curve沒(méi)有理論推導(dǎo)的那么光滑,但是可以大致看出來(lái),訓(xùn)練集和交叉驗(yàn)證集上的得分曲線走勢(shì)還是符合預(yù)期的。目前的曲線看來(lái),我們的model并不處于overfitting的狀態(tài)(overfitting的表現(xiàn)一般是訓(xùn)練集上得分高,而交叉驗(yàn)證集上要低很多,中間的gap比較大)。
?
5.2 Bagging
?
一般來(lái)說(shuō),單個(gè)分類器的效果真的是很有限。我們會(huì)傾向于把N多的分類器合在一起,做一個(gè)“綜合分類器”以達(dá)到最好的效果。模型融合可以比較好地緩解,訓(xùn)練過(guò)程中產(chǎn)生的過(guò)擬合問(wèn)題,從而對(duì)于結(jié)果的準(zhǔn)確度提升有一定的幫助。Bagging把很多的小分類器放在一起,每個(gè)train隨機(jī)的一部分?jǐn)?shù)據(jù),然后把它們的最終結(jié)果綜合起來(lái)(多數(shù)投票制)。Sklearn已經(jīng)直接提供了這套構(gòu)架,我們直接調(diào)用就行:在這里,我們用CV結(jié)果來(lái)測(cè)試不同的分類器個(gè)數(shù)對(duì)最后結(jié)果的影響。注意,我們?cè)诓渴養(yǎng)agging的時(shí)候,要把它的函數(shù)base_estimator里填上你的小分類器
?
(1)用bagging自帶的DT
params= [1, 10, 15, 20, 25, 30, 40]
test_scores= []
forparam in params:
clf= BaggingRegressor(n_estimators=param)
?test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
???
plt.plot(params,test_scores)
plt.title("n_estimatorvs CV Error");
#用bagging自帶的DT,結(jié)果是0.196
?
(2)ridge + bagging
ridge= Ridge(1)
params= [1, 10, 15, 20, 25, 30, 40]
test_scores= []
forparam in params:
??? clf = BaggingRegressor(n_estimators=param,base_estimator=ridge)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
plt.plot(params,test_scores)
print(test_scores)
plt.title("n_estimatorvs CV Error");
?
#使用25個(gè)小ridge分類器的bagging,達(dá)到了0.163的結(jié)果.
#learning_curve曲線,準(zhǔn)確率比較高,基本沒(méi)有過(guò)擬合
?
5.3Boosting
#Boosting比Bagging理論上更高級(jí)點(diǎn),它也是攬來(lái)一把的分類器。但是把他們線性排列。下一個(gè)分類器把上一個(gè)分類器分類得不好的地方加上更高的權(quán)重,這樣下一個(gè)分類器就能在這個(gè)部分學(xué)得更加“深刻”。
?
(1)Adaboost+Ridge
params= [34,35,36,45,50,55,60,65]
test_scores= []
forparam in params:
??? clf = AdaBoostRegressor(n_estimators=param,base_estimator=ridge)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
plt.plot(params,test_scores)
plt.title("n_estimatorvs CV Error");??
#Adaboost+Ridge在這里,55個(gè)小分類器的情況下,達(dá)到了0.163的結(jié)果。其learning_curve曲線如下:
?
(2)使用Adaboost自帶的DT
params= [10, 15, 20, 25, 30, 35, 40, 45, 50]
test_scores= []
forparam in params:
??? clf = AdaBoostRegressor(n_estimators=param)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
plt.plot(params,test_scores)
plt.title("n_estimatorvs CV Error");
#test_scores達(dá)到0.198的結(jié)果
?
5.3XGBoost
#這依舊是一個(gè)Boosting框架的模型,但是卻做了很多的改進(jìn)。用Sklearn自帶的cross validation方法來(lái)測(cè)試模型
params= [1,2,3,4,5,6]
test_scores= []
forparam in params:
??? clf = XGBRegressor(max_depth=param)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
?
plt.plot(params,test_scores)
plt.title("max_depthvs CV Error");
#當(dāng)params值為6時(shí),test_scores達(dá)到0.185
#輕微過(guò)擬合
?
5.4 GBM
params= [80,90,100,110,120,130,140,150]
test_scores= []
forparam in params:
??? clf =GradientBoostingRegressor(n_estimators=param)
??? test_score = np.sqrt(-cross_val_score(clf,X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
??? test_scores.append(np.mean(test_score))
???
plt.plot(params,test_scores)
plt.title("max_depthvs CV Error");
?
#當(dāng)params值為90時(shí),test_scores達(dá)到0.188,從其學(xué)習(xí)率曲線可以看出,模型過(guò)擬合挺嚴(yán)重
#綜上,Bagging + Ridge 與Adaboost+Ridge模型的準(zhǔn)確率和泛化能力是比較好。
六 使用ridge + bagging模型預(yù)測(cè)房?jī)r(jià)
?
ridge= Ridge(1)
br=BaggingRegressor(n_estimators=param, base_estimator=ridge)
br.fit(X_train,y_train)
#房?jī)r(jià)預(yù)測(cè)
y_br= np.expm1(br.predict(X_test))
?
features={'樓齡' , '建筑面積' , '區(qū)域_增城' , '區(qū)域_天河' , '區(qū)域_海珠’, '區(qū)域_番禺' , '區(qū)域_白云' ,? '樓層_中樓層' , '樓層_低樓層' , '樓層_獨(dú)棟' , '樓層_高樓層' , '朝向_東' , '朝向_東北' , '朝向_東南' , '朝向_東西' , '朝向_北' , '朝向_南' , '朝向_南北' , '朝向_西' , '朝向_西北' , '朝向_西南' , '房屋用途_別墅' , '房屋用途_普通住宅'}???
?
#使用GBM模型進(jìn)行特征選擇
gbr= GradientBoostingRegressor(loss='ls', n_estimators=100, max_depth=3,verbose=1, warm_start=True)
gbr_fitted= gbr.fit(X_train, y_train)
?
#查看特征重要度,根據(jù)特征重要度可以進(jìn)行特征選擇,選取重要度高的特征,刪除不重要的特征以減少維度降低過(guò)擬合
print(zip(features,list(gbr.feature_importances_)))
#可見(jiàn)建筑面積是影響房屋價(jià)格的最重要特征。
總結(jié)
以上是生活随笔為你收集整理的广州二手房价分析与预测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信页面跳转设计
- 下一篇: HTAP 能够取代 OLAP 吗?