python广深地区房价数据的爬取与分析
Python房產數據分析
- 1、數據爬取
- 2、明確需求與目的
- 數據預覽
- 提出問題
- 3.數據預處理
- 數據清洗
- 缺失值
- 異常值(對連續性標簽做處理)
- 異常值(對離散標簽做處理)
- 4、數據分析
- 問題1、廣東省房價的總體情況如何?
- 問題2、高端小區都有哪些?
- 問題3、廣東省小區的命名偏好
- 問題4、廣深兩地的房源分布如何
- 問題5、廣深房價與房屋面積大小的關系如何?
- 問題6、廣深地區房源分布的地鐵線 以及 房價與距地鐵線距離的關系
- 問題7、廣深地區房屋朝向
- 問題8、廣深地區建設年份集中情況
- 問題9、廣深地區熱門戶型
- 5、機器學習預測房價
- 數據預處理
- 線性回歸
- KNN
- 其他的回歸模型
- 模型調參
- 模型加權平均集成
- 模型融合
- 6、總結
參考請標出處
1、數據爬取
房天下的網站,用最便捷的requesets+xpath定位爬取。
由于房地產市場有一定的飽和,新房的數據量太小,因此選擇二手房的數據
2、明確需求與目的
當今時代,房價問題一直處于風口浪尖,房價的上漲抑或下跌都牽動著整個社會的利益,即便是政府出臺各種政策方針也只能是暫時抑制樓市的漲勢,對于需要買房的人來說,除了關注這些變化和政策外,還有一個非常頭疼的問題,在哪里買房,房價怎樣。一般的人會不停花大量精力逛鏈家、安居客等房地產網站,借助他們展示的內容進行篩選,但因地區眾多,各個地段、房價差異的對比以及入手時機的把握,都得自己去一個個查閱與分析,非常麻煩。倘若可以通過數據的爬取,再按照自己希望的維度統計、分析與展示,會讓數據變得清晰明了。本項目旨在提取并展示數據,為剛需購房者提供有用信息。
數據預覽
拿到數據后首先要明確各個列名的含義
city | 城市
name | 樓盤名字
loc|地址
size | 房屋布局
area | 面積
price| 價格
price_sum| 總價
dire| 朝向
floor| 樓層
buildtime| 建成時間
advantage| 優勢
提出問題
1、廣東省房價的總體情況如何?
2、高端小區都有哪些?
3、廣東省小區的命名偏好
4、廣深兩地的房源分布如何
5、廣深房價與房屋面積大小的關系如何?
6、廣深地區房源分布的地鐵線 以及 房價與距地鐵線距離的關系
7、廣深地區房屋朝向
8、廣深地區建設年份集中情況
9、廣深地區熱門戶型
3.數據預處理
第一步導入相關的庫,并做相關設置
import os os.chdir('H:\\ana\data')#切換到指定路徑 import numpy as np import pandas as pd from pyecharts import Map,Bar,WordCloud,Pie import matplotlib.pyplot as plt import re import seaborn as sns from scipy import stats plt.style.use('ggplot') from pylab import mpl mpl.rcParams['font.sans-serif'] = ['SimHei'] #解決seaborn中文字體顯示問題 plt.rc('figure', figsize=(10, 10)) #把plt默認的圖片size調大一點 plt.rcParams["figure.dpi"] =mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題 %matplotlib inline第二步加載數據集
data=pd.read_excel('房產數據.xlsx') print(data.shape)#返回數據行列數 # data.head()#查看開頭指定列數 # data.tail()#查看末尾指定列數 data.sample(10)#隨機取指定列數數據清洗
缺失值
通過info查看數據信息。
也可以通過isnull與sum結合,查看缺失值情況。
缺失值占總數據的%10左右,可以直接刪去。但達到%30左右及以上,我們可以采用填充的方法,均值中值或者眾數來填充視情況而定。
異常值(對連續性標簽做處理)
通過describe查看數值信息。
可配合箱線圖輔助。
異常值可以刪除,視為缺失值,或者不處理。
箱線圖包括最小值,四分之一位點q1,中位點,四分三位點q3,最大值,離群點。
離群點定義為小于q1 - 1.5IQR,大于q3 + 1.5IQR(q3-q1=IQR)。
離群點可能為異常值,但就此看這些離群點都是算在一個合理的范圍內的。
第一張圖為價格的箱線圖,離群點很多,說明廣東省存在個別市的房價差異巨大,但大多數處于較低的水平。這一現象符合認知,珠三角城市房價大于其他地級市房價。
第二張圖為房屋面積的箱線圖,也存在不少的離群點,面積位于100-200平米的房屋占絕大多數,存在特別大面積的房屋有可能是集體宿舍,經瀏覽網頁發現也有可能存在商家亂填刷單的現象,屬于異常值,需要后續清洗
異常值(對離散標簽做處理)
1、朝向
data['dire'].unique()
由于極其少數網站頁面沒有朝向的數據,而是把后面建成時間的數據提前,因此有些錯亂。
但無妨,我們只需做個簡單的過濾,保留有正確朝向的數據
2、建成時間
data['buildtime'].unique()
發現存在幾項異常的時間,有可能是地產商預計的建成時間,我們不考慮未建成的房屋,因此做一個過濾
3、樓層
data['floor'].unique()
這一項數據非常凌亂,而且存在有一些極其異常的數據,我們做一個過濾
4、房屋布局
data['size'].unique()
室和廳數量比較多的查看后發現均為集體宿舍或大型別墅,在一個比較合理的范圍
最后將清洗后的數據保存,把廣州深圳的數據另外保存,下面會著重分析
#保存清洗后的數據 data.to_csv('data_clean.csv',index=False) data.loc[data.city=='深圳',:].to_csv('shenzhen_data_clean.csv',index=False) data.loc[data.city=='廣州',:].to_csv('guangzhou_data_clean.csv',index=False)4、數據分析
問題1、廣東省房價的總體情況如何?
g = data.groupby("city")#按城市分組 r = g["price"].agg(["mean", "count"])#按價格的均值、數量創建對象 r = r.sort_values("mean", ascending=False)#按均值的降序排列 display(r) r.plot(kind="bar")
很明顯發現,珠三角城市房價位居前列,粵東粵西的邊緣城市靠后,其中廣州深圳的房價顯著超出平均水平。數據量上珠三角城市也明顯占優,說明大城市的房地產市場更加火爆。而小城市中陽江的數據量也比較大,個人認為應該是當地海陵島的旅游業比較火爆,帶動房產市場。
接下來繪制一個價格地圖,更加直觀的展示數據
city_sum=r.index.tolist() for i in range(len(city_sum)):city_sum[i]+='市'#map的調用需要城市全稱 price_avg=r['mean'].tolist() map=Map('廣東省各地級市平均房價','單位:元/平方米',title_color="#fff",title_pos="center",width=1200, height=600,background_color='#404a59') map.add("", city_sum, price_avg,maptype='廣東',visual_range=[7000,35000],is_visualmap=True, visual_text_color='#000',is_label_show=True)問題2、高端小區都有哪些?
我們發現就算在珠三角地區中,城市內的各個樓盤也存在很大的價格差異。
定義房價大于10萬的小區為高端小區,作一個展示
問題3、廣東省小區的命名偏好
import jieba import jieba.analyse #載入數據 rows=pd.read_csv('data_clean.csv', header=0,encoding='utf-8',dtype=str)segments = [] for index, row in rows.iterrows():content = row[1] #提出小區名字的數據#TextRank 關鍵詞抽取,只獲取固定詞性words = jieba.analyse.textrank(content, topK=50,withWeight=False,allowPOS=('ns', 'n', 'vn', 'v'))for word in words:# 記錄全局分詞segments.append({'word':word, 'count':1})dfSg = pd.DataFrame(segments)# 詞頻統計 dfWord = dfSg.groupby('word')['count'].sum() dfWord.sort_values(ascending=False)[:30] #取前30輸出輸出結果如下
從上看出廣東省的小區偏好以花園、廣場、時代、國際等詞命名。同時也看出房地產商保利在廣東省占據一定的市場份額
問題4、廣深兩地的房源分布如何
想要把房源的分布在地圖上展示出來,需要經緯度的數據,這里調用百度地圖的API,把已有的地址數據轉換為經緯度數據
import json from urllib.request import urlopen, quote import requests,csv import pandas as pd def getlnglat(address):url = 'http://api.map.baidu.com/geocoder/v2/'output = 'json'ak = '###############'#這里輸出你個人在百度地圖注冊的KEYadd = quote(address) #由于本文城市變量為中文,為防止亂碼,先用quote進行編碼uri = url + '?' + 'address=' + add + '&output=' + output + '&ak=' + akreq = urlopen(uri)res = req.read().decode() #將其他編碼的字符串解碼成unicodetemp = json.loads(res) #對json數據進行解析return temp# file = open(r'H:\ana\point.json','w') #建立json數據文件 try:data=pd.read_csv(r'H:\ana\data\guangzhou_data_clean.csv')#以廣州市為例子for i in range(len(data)):loc='廣州市'+data.loc[i,'loc']price=data.loc[i,'price']lng = getlnglat(loc)['result']['location']['lng'] #采用構造的函數來獲取經度data.loc[i,'lng']=lnglat = getlnglat(b)['result']['location']['lat'] #獲取緯度data.loc[i,'lat']=latdata.loc[i,'point']=str(lng)+','+str(lat)str_temp = '{"lat":' + str(lat) + ',"lng":' + str(lng) + ',"count":' + str(c) +'},'print(str_temp,i) #也可以通過打印出來,把數據copy到百度熱力地圖api的相應位置上except KeyError:passdata.to_csv('gz_latlon.csv',index=False)獲取經緯度后,在百度地圖平臺上上傳相關帶有經緯度的數據即可制作相關的熱力地圖
結果如下:
深圳
廣州
可以發現,深圳的房源分布較為均勻,大多集中在南山區和福田區。圖示中點越大代表價格越高,發現深圳灣周邊和福田區中心的位置房價相對高很多。
廣州房源的分布集中在白云區和天河區,也有一些小的集群點分布在廣州北站、廣州東站等交通樞紐附近,而廣州南站較為偏僻,比較少房子分布
問題5、廣深房價與房屋面積大小的關系如何?
def area_price_relation(city):data=pd.read_csv('{}_data_clean.csv'.format(city))g=sns.jointplot(x='area',y='price',data=data, kind='reg' ,stat_func=stats.pearsonr)g.fig.set_dpi(100)g.ax_joint.set_xlabel('面積', fontweight='bold')g.ax_joint.set_ylabel('價格', fontweight='bold')return g area_price_relation('shenzhen') area_price_relation('guangzhou')
可見價格與面積之間有一定的正相關關系。深圳中面積的影響更大,說明深圳的房價受波動更大,房價的不穩定性更大。
問題6、廣深地區房源分布的地鐵線 以及 房價與距地鐵線距離的關系
def get_distance(city,data=data):station=[]#站distance=[]#距離station_count=[]#地鐵線房源分布數量station_name=[]#地鐵線data1=data.loc[data.city==city,:]data1=data1.reset_index(drop=True)#重置索引for i in range(len(data1)):s=re.findall('\d+',data1.loc[i,'advantage'])#用正則表達式匹配advantage標簽if len(s)==2:distance.append(s[1])#距離station.append(s[0])#站線data1.loc[i,'distance']=s[1]data1.to_csv('{}_distance.csv'.format(city),index=False) #重新保存數據,后續進行分析count=list(set(station))#列表去掉重復值的方法count.sort()#列表排序for i in count:station_count.append( station.count('{}'.format(i)) ) #統計各個站線房源分布數量station_name.append('{}號線'.format(i)) #相應站線 bar=Bar('')bar.add('' , station_name , station_count ,is_label_show=True , is_more_utils = True)return bar get_distance('深圳') get_distance('廣州') def distance_price_relation(city_short):data=pd.read_csv('{}_distance.csv'.format(city_short))g=sns.jointplot(x='distance',y='price',data=data.dropna(subset=['distance']),kind='reg',stat_func=stats.pearsonr)g.fig.set_dpi(100)g.ax_joint.set_xlabel('最近地鐵距離',fontweight='bold')g.ax_joint.set_ylabel('價格',fontweight='bold')return g distance_price_relation('sz') distance_price_relation('gz')
從上可見,深圳房子多分布在1、2、3號線,廣州多分布在2、3、5、6號線。
房價與距離地鐵站的距離有一定的負相關關系,距離越近,房價越高的趨勢大。
問題7、廣深地區房屋朝向
def dire_pie(city_short):data=pd.read_csv('{}_distance.csv'.format(city_short))dire=data.dire.value_counts().index.tolist()count=data.dire.value_counts().values.tolist()pie=Pie('朝向統計餅狀圖',title_pos='center')pie.add('餅圖', dire, count, is_label_show=True,legend_orient='vertical', legend_pos='left',is_more_utils=True)return pie dire_pie('sz') dire_pie('gz')
很明顯發現朝南的房子占%50以上。說明很多房地產商會偏向于建筑朝南的房子,以吸引顧客
朝南的房子有其優點:
1、由于我國位于北半球,大部分時間陽光從南方照射過來,而居住南面則房屋采光良好;
2、夏天時,強烈的下午陽光會偏向北方,南面的房屋可以避免下午陽光造成的高溫;
3、冬天時,陽光會偏向與南面房屋,使得房屋在寒冷季節可以保持比較溫暖。
問題8、廣深地區建設年份集中情況
def time_pie(city):time=data[data.city==city].buildtime.value_counts().index.tolist()[:5]count=data[data.city==city].buildtime.value_counts().values.tolist()[:5]pie=Pie('建年統計餅狀圖',title_pos='center')pie.add('餅圖', time, count, is_label_show=True,legend_orient='vertical', legend_pos='left',is_more_utils=True)return pie time_pie('深圳') time_pie('廣州')
從上可發現,廣深地區的房子集中在2014和2015兩年,一定程度上說明這兩年是房地產業迅猛發展的兩年。同時2018年在前列,一定程度上說明廣深地區流動人口占有較大的比重,房屋商品化,二手房的交易市場較熱。也有大量年代較遠的房子在售,說明這些老房子有一定的市場。
問題9、廣深地區熱門戶型
def size(n,data=data):size_count=data[data.city==n]['size'].value_counts().values[:5]size_kind=data[data.city==n]['size'].value_counts().index[:5]bar=Bar('戶型排行')bar.add('',size_kind,size_count,is_label_show=True,is_more_utils = True)return bar size('深圳') size('廣州')
從上得出,廣深地區熱門戶型非常一致,其中最熱門為3室2廳
5、機器學習預測房價
采用機器學習算法綜合考慮多個因素對房價的影響,建立預測模型。
首先要講數據轉換為可以作為模型輸入的矩陣形式
觀察數據,發現房屋優勢特征中滿二、滿五、優質教育的字段很多,因此單獨轉換為0和1,作為輸入。
new_data=pd.DataFrame() def datatrans(new_data,data,dire_sum=list(gz_data['dire'].unique())):new_data['city']=data['city']new_data['area']=data['area']new_data['buildtime']=data['buildtime'].astype('float')new_data['distance']=data['distance']for i in range(len(data)):s=re.findall('\d+',data.loc[i,'size'])new_data.loc[i,'room_num']=float(s[0])new_data.loc[i,'hall_num']=float(s[1])if '低層' in data.loc[i,'floor']:new_data.loc[i,'floor']=1elif '中層' in data.loc[i,'floor']:new_data.loc[i,'floor']=2elif '高層' in data.loc[i,'floor']:new_data.loc[i,'floor']=3dire=data.loc[i,'dire']idx=dire_sum.index(dire)+1new_data.loc[i,'dire']=idxnew_data['exemption of business tax']=data['exemption of business tax']new_data['exemption of double tax']=data['exemption of double tax']new_data['quality education']=data['quality education']datatrans(new_data,sz_data) new_data1=pd.DataFrame() datatrans(new_data1,gz_data) new_data1=pd.concat([new_data1,new_data],axis=0,ignore_index=True)進一步處理數據,將樓層按照低中高分別賦值1、2、3作為輸入。
再用正則表達式將房屋布局的數據拆分為房間數量和客廳數量兩個特征輸入。
將各個不同朝向的數據轉化為1-8作為輸入
當前數據有11個特征(房屋面積、建成時間、距地鐵站距離、房間數、客廳數、樓層、方向、是否滿二、是否滿五、是否優質教育、城市)和1個標記(房價)。因為預測目標——房價是一個連續變量,因此本項目中的價格預測是一個回歸問題。
數據預處理
data=pd.read_csv('new_data7.20.csv') data['distance'].fillna(5000,inplace=True) data['buildtime'].fillna(data['buildtime'].mode()[0],inplace=True) X = data.drop(["price"], axis=1)#數據分割,隨機采樣25%作為測試樣本,其余作為訓練樣本 from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.25)#數據標準化處理 歸一化 from sklearn.preprocessing import StandardScaler ss_x = StandardScaler() x_train = ss_x.fit_transform(x_train) x_test = ss_x.transform(x_test)線性回歸
from sklearn.linear_model import LinearRegression lr = LinearRegression() #初始化 lr.fit(x_train, y_train) #訓練數據 lr_y_predict = lr.predict(x_test) #回歸預測 #性能測評:使用R方得分指標對模型預測結果進行評價 from sklearn.metrics import r2_score print("LinearRegression模型的R方得分為:", r2_score(y_test, lr_y_predict))plt.figure(figsize=(15, 5)) plt.plot(y_test.values[:100], "-r", label="真實值") plt.plot(lr_y_predict[:100], "-g", label="預測值") plt.legend() plt.title("線性回歸預測結果")
KNN
param_grid = [{'weights':['uniform'],'n_neighbors':[i for i in range(1,12)]},{'weights':['distance'],'n_neighbors':[i for i in range(1,12)],'p':[i for i in range(1,6)]} ] from sklearn.neighbors import KNeighborsRegressor knnrgr = KNeighborsRegressor() from sklearn.model_selection import GridSearchCV grid_search = GridSearchCV(knnrgr,param_grid) grid_search.fit(x_train,y_train)用網格搜索法尋找調參,訓練結果為
其他的回歸模型
models = [Ridge(),Lasso(alpha=0.01,max_iter=10000),RandomForestRegressor(), GradientBoostingRegressor(),SVR(),ElasticNet(alpha=0.001,max_iter=10000), SGDRegressor(max_iter=1000,tol=1e-3),BayesianRidge(),KernelRidge(alpha=0.6, kernel='polynomial', degree=2, coef0=2.5),ExtraTreesRegressor(), XGBRegressor(max_depth=5, learning_rate=0.1, n_estimators=160, silent=False, objective='reg:gamma')] names = [ "鄰回歸", "Lasso回歸", "隨機森林", "梯度提升樹", "支持向量機" , "彈性網絡","梯度下降回歸","貝葉斯線性回歸","L2正則線性回歸","極端隨機森林回歸","Xgboost回歸"] for name, model in zip(names, models):model.fit(x_train,y_train)predicted= model.predict(x_test)print("{}: {:.6f}, {:.4f}".format(name,model.score(x_test,y_test),mean_squared_error(y_test, predicted)))結果如下
模型調參
class grid():def __init__(self,model):self.model = modeldef grid_get(self,X,y,param_grid):grid_search = GridSearchCV(self.model,param_grid,cv=5,n_jobs=-1)grid_search.fit(X,y)print(grid_search.best_params_, grid_search.best_score_)print(pd.DataFrame(grid_search.cv_results_)[['params','mean_test_score','std_test_score']])Lasso回歸調參
grid(Lasso()).grid_get(x_train,y_train,{'alpha': [0.0004,0.0005,0.0007,0.0006,0.0009,0.0008],'max_iter':[10000]})
嶺回歸調參
核鄰回歸調參
彈性網絡調參
模型加權平均集成
def r2(model,X,y):return cross_val_score(model, X, y, scoring="r2", cv=5)class AverageWeight(BaseEstimator, RegressorMixin):def __init__(self,mod,weight):self.mod = modself.weight = weightdef fit(self,X,y):self.models_ = [clone(x) for x in self.mod]for model in self.models_:model.fit(X,y)return selfdef predict(self,X):w = list()pred = np.array([model.predict(X) for model in self.models_])# for every data point, single model prediction times weight, then add them togetherfor data in range(pred.shape[1]):single = [pred[model,data]*weight for model,weight in zip(range(pred.shape[0]),self.weight)]w.append(np.sum(single))return wlasso = Lasso(alpha=0.0009,max_iter=10000) ridge = Ridge(alpha=35) ker = KernelRidge(alpha=0.5 ,kernel='polynomial',degree=3 , coef0=0.8) ela = ElasticNet(alpha=0.005,l1_ratio=0.3,max_iter=10000) bay = BayesianRidge()w1 = 0.15 #按R2指標賦權 w2 = 0.15 w3 = 0.4 w4 = 0.15 w5 = 0.15weight_avg = AverageWeight(mod = [lasso,ridge,ker,ela,bay],weight=[w1,w2,w3,w4,w5]) r2(weight_avg,x_train,y_train) r2(weight_avg,x_train,y_train).mean()模型融合
class stacking(BaseEstimator, RegressorMixin, TransformerMixin):def __init__(self,mod,meta_model):self.mod = modself.meta_model = meta_modelself.kf = KFold(n_splits=5, random_state=42, shuffle=True)def fit(self,X,y):self.saved_model = [list() for i in self.mod]oof_train = np.zeros((X.shape[0], len(self.mod)))for i,model in enumerate(self.mod):for train_index, val_index in self.kf.split(X,y):renew_model = clone(model)renew_model.fit(X[train_index], y[train_index])self.saved_model[i].append(renew_model)oof_train[val_index,i] = renew_model.predict(X[val_index])self.meta_model.fit(oof_train,y)return selfdef predict(self,X):whole_test = np.column_stack([np.column_stack(model.predict(X) for model in single_model).mean(axis=1) for single_model in self.saved_model]) return self.meta_model.predict(whole_test)def get_oof(self,X,y,test_X):oof = np.zeros((X.shape[0],len(self.mod)))test_single = np.zeros((test_X.shape[0],5))test_mean = np.zeros((test_X.shape[0],len(self.mod)))for i,model in enumerate(self.mod):for j, (train_index,val_index) in enumerate(self.kf.split(X,y)):clone_model = clone(model)clone_model.fit(X[train_index],y[train_index])oof[val_index,i] = clone_model.predict(X[val_index])test_single[:,j] = clone_model.predict(test_X)test_mean[:,i] = test_single.mean(axis=1)return oof, test_mean a = Imputer().fit_transform(x_train) b = Imputer().fit_transform(y_train.values.reshape(-1,1)).ravel() stack_model = stacking(mod=[lasso,ridge,ker,ela,bay],meta_model=ker)6、總結
本項目收集了廣東省二手房數據,著重分析廣深地區的房價。首先采用統計分析的方法對數據進行初步分析,大致了解房價分布及其影響因素;隨后調用百度地圖API,實現數據地圖可視化。最后采用機器學習方法建模預測,并比較了幾種常用回歸模型的預測效果。
基本符合一個完整數據分析案例的要求,采用直觀的數據可視化方式展示數據,并通過數據分析為二手房購買者提供建設性意見。但仍有很多不足的地方,如并沒有對數據進行特征工程,沒有進行特征的轉換和篩選,機器學習模型的調參也比較簡略,因此預測能力還有很大的提升空間。
總結
以上是生活随笔為你收集整理的python广深地区房价数据的爬取与分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cimplicity 国内知名汽车厂设备
- 下一篇: 常见Win7优化误区,教你正确方法!