分析目標(biāo)
1.了解廣州二手房各地區(qū)的房價情況
2.了解每個特征對房價的影響程度
3.對數(shù)據(jù)進(jìn)行建模,預(yù)測房價
數(shù)據(jù)獲取
從廣州鏈家網(wǎng)爬取共54573條數(shù)據(jù)至扰,數(shù)據(jù)特征如下:
price:二手房的總價格(萬元)
price_per_m2:二手房的每平米價格(元)
house_type:二手房的戶型
direction:二手房的朝向
area:二手房的面積
region:二手房所屬的區(qū)域
built_date:二手房建成時間
elevator:是否有電梯
floor:二手房的樓層
renovation:裝修情況
field:地段
xiaoqu:小區(qū)
數(shù)據(jù)導(dǎo)入
import pandas as pd
from pymongo import MongoClient
client = MongoClient('localhost')
db = client['lianjia']
def get_data():
data = []
for item in db['lianjiagz'].find():
data.append(item)
return data
data = get_data()
df = pd.DataFrame(data)
df.head()
首先查看數(shù)據(jù)整體情況纠俭,檢查是否存在缺失值雄可。
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 54573 entries, 0 to 54572
Data columns (total 12 columns):
price 54544 non-null object
price_pre_m2 54544 non-null object
house_type 54544 non-null object
direction 54544 non-null object
area 54544 non-null object
region 54544 non-null object
built_date 54544 non-null object
elevator 53593 non-null object
floor 54544 non-null object
renovation 54221 non-null object
field 54544 non-null object
xiaoqu 54544 non-null object
dtypes: object(12)
memory usage: 5.0+ MB
數(shù)據(jù)集總共包含54573條數(shù)據(jù)乌助,大部分特征都存在相同數(shù)量的缺失值呻率,推測是數(shù)據(jù)爬取失敗導(dǎo)致的空白躏将。此外電梯存在951條缺失值锄弱,裝修存在323條缺失值。
缺失值處理
刪除因爬取失敗導(dǎo)致的空白祸憋。
df.dropna(subset=['price'], inplace=True)
查看缺少evevator特征數(shù)據(jù)發(fā)現(xiàn)会宪,存在缺失的數(shù)據(jù),皆為地下室/低樓層/獨棟蚯窥,并非常規(guī)的商品房掸鹅,此處選擇將這些數(shù)據(jù)丟棄。
df.loc[df.elevator.isna(), ['elevator', 'floor']]
df.dropna(subset=['elevator'], inplace=True)
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 53593 entries, 0 to 54572
Data columns (total 12 columns):
price 53593 non-null object
price_pre_m2 53593 non-null object
house_type 53593 non-null object
direction 53593 non-null object
area 53593 non-null object
region 53593 non-null object
built_date 53593 non-null object
elevator 53593 non-null object
floor 53593 non-null object
renovation 53593 non-null object
field 53593 non-null object
xiaoqu 53593 non-null object
dtypes: object(12)
memory usage: 5.3+ MB
在處理elevator缺失值的過程中拦赠,renovation的缺失值也被舍棄了巍沙。
數(shù)據(jù)轉(zhuǎn)換
去除area的單位并轉(zhuǎn)換為float格式。
df.area = df.area.apply(lambda x: x.replace('平米', ''))
df.area = df.area.astype(float)
將x室x廳單獨提取出來荷鼠,創(chuàng)造兩個新特征句携,分別為living_room和room。
import re
def get_room(x):
return re.search('(\d+)室.*?', x).group(1)
def get_living_room(x):
return re.search('\d+室(\d+)廳', x).group(1)
df['room'] = df.house_type.apply(get_room).astype(int)
df['living_room'] = df.house_type.apply(get_living_room).astype(int)
將建成年份單獨提取出來允乐。
def get_built_year(x):
if x.startswith('未知'):
return pd.NaT
else:
return re.search('(\d+)年.*?', x).group(1)
df.built_date = df.built_date.apply(get_built_year)
對direction進(jìn)行處理矮嫉。查看direction數(shù)據(jù)可以發(fā)現(xiàn),存在一些奇怪的值牍疏,比如“東 東南 南 西南 西”這種無法理解的朝向蠢笋。編寫函數(shù)對direction處理后,每種方向只留下一個鳞陨。
from functools import reduce
def clean_direction(x):
x = x.replace(' ', '')
direction = []
for i in x:
if i not in direction:
direction.append(i)
direction.sort()
return reduce(lambda x, y: x+y, direction)
df.direction = df.direction.apply(clean_direction)
df.direction.value_counts()
南 16989
北 9231
東南 7707
北南 4217
東 4041
東北 3275
南西 3012
北西 2092
西 2046
東西 380
東北南 206
北南西 118
東南西 116
東北南西 105
東北西 57
據(jù)數(shù)無暫 1
Name: direction, dtype: int64
將‘據(jù)數(shù)無暫’刪去昨寞。
df = df.loc[df.direction != '據(jù)數(shù)無暫', :]
處理floor特征,將低、中援岩、高樓層提取為floor_level特征熟史,將樓層總數(shù)提取為total_floor特征。
def get_floor_level(x):
if re.search('(.*?)/.*?', x):
return re.search('(.*?)/.*?', x).group(1)
else:
return pd.NaT
def get_total_floor(x):
if re.search('.*?(\d+)層', x):
return re.search('.*?(\d+)層', x).group(1)
else:
return pd.NaT
df['floor_level'] = df.floor.apply(get_floor_level)
df['total_floor'] = df.floor.apply(get_total_floor)
轉(zhuǎn)換后發(fā)現(xiàn)存在少量缺失值窄俏,此處選擇將缺失值刪去蹂匹。
df.floor_level.notna().value_counts()
False 53590
True 2
Name: floor_level, dtype: int64
df = df.loc[df.floor_level.notna(),:]
數(shù)據(jù)分析
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# 圖表正常顯示中文
plt.rcParams['font.sans-serif']=['SimHei']
# 正常顯示符合
plt.rcParams['axes.unicode_minus']=False
描述性統(tǒng)計
df.describe()
可以看出,二手房平均總價為293.34萬元/套凹蜈,中位數(shù)為240萬元/套限寞。平均每平米房價為32125元/平米,中位數(shù)為29249元/平米仰坦。平均面積為91.1平米履植,最大面積為877.64平米∏幕危可以看出玫霎,由于一些超大戶型的影響,數(shù)據(jù)整體呈右偏趨勢妈橄。
房源數(shù)量分布
df.region.value_counts().sort_values(ascending=False)
番禺 10832
增城 7153
天河 6983
白云 6044
海珠 5928
花都 4574
越秀 3753
黃埔 2792
荔灣 2792
南沙 1701
從化 1038
數(shù)據(jù)可視化
plt.figure(figsize=(10, 5))
region_count = df.groupby('region', as_index=False).count().sort_values(by='price', ascending=False)
sns.barplot(x='region', y='price', data=region_count)
plt.title('各城區(qū)房源數(shù)量分布')
從化區(qū)房源最少庶近,不足2000套,番禺區(qū)房源最多眷蚓,超過10000套鼻种,其他區(qū)房源數(shù)量在2000-8000之間。其中番禺二手房源數(shù)量占全市全區(qū)二手房數(shù)量的20.21%沙热。
房源總面積及平均面積統(tǒng)計
房源總面積排序如下:
df.groupby('region').sum().area.sort_values(ascending=False)
番禺 1067891.33
增城 739562.84
天河 608179.56
白云 514934.24
花都 472354.17
海珠 465171.83
越秀 281218.75
黃埔 244319.00
荔灣 223256.17
南沙 169461.14
各城區(qū)二手房總面積分布柱形圖如下:
plt.figure(figsize=(10, 5))
area_sum = df.groupby('region', as_index=False).sum().sort_values(by='area', ascending=False)
sns.barplot(x='region', y='area', data=area_sum)
plt.title('各城區(qū)房源總面積分布')
各城區(qū)二手房面積分布基本與房源數(shù)量分布一致叉钥。
接下來查看各城區(qū)二手房平均面積分布。
df.groupby('region').mean().area.sort_values(ascending=False)
增城 103.391981
花都 103.269386
從化 102.541753
南沙 99.624421
番禺 98.586718
黃埔 87.506805
天河 87.094309
白云 85.197591
荔灣 79.962812
海珠 78.470282
越秀 74.931721
可視化
plt.figure(figsize=(10, 5))
area_mean = df.groupby('region', as_index=False).mean().sort_values(by='area', ascending=False)
sns.barplot(x='region', y='area', data=area_mean)
plt.title('各城區(qū)房源平均面積分布')
總體來說篙贸,增城投队、花都、從化爵川、南沙敷鸦、番禺等郊區(qū)的二手房平均面積相對較大,而黃埔雁芙、天河轧膘、白云、荔灣兔甘、海珠谎碍、越秀等老城區(qū)的二手房平均面積相對較小。推測可能原因為郊區(qū)土地價格較低洞焙,較低的成本允許開發(fā)商建造更大的戶型蟆淀。
全市房源戶型分布
以區(qū)間[0,50)拯啦、[50,100)、[100,150)熔任、[150,200)褒链、[200,+∞)為劃分標(biāo)準(zhǔn),將面積劃分為tinysmall疑苔、small甫匹、medium、big惦费、huge五個等級兵迅,分別對應(yīng)極小戶型、小戶型薪贫、中等戶型恍箭、大戶型和巨大戶型,并創(chuàng)造一個新特征值area_level瞧省。
df.loc[df.area < 50, 'area_level'] = 'tinysmall'
df.loc[(df.area >= 50) & (df.area < 100), 'area_level'] = 'small'
df.loc[(df.area >= 100) & (df.area < 150), 'area_level'] = 'medium'
df.loc[(df.area >= 150) & (df.area < 200), 'area_level'] = 'big'
df.loc[df.area >= 200, 'area_level'] = 'huge'
查看戶型分布情況
df.groupby('area_level').count().area.sort_values(ascending=False)
small 31401
medium 13443
tinysmall 5572
big 2231
huge 943
可視化
groupped_area_level = df.groupby('area_level').area.count().reset_index().sort_values(by='area',ascending=False)
plt.figure(figsize=(10,5))
plt.bar(groupped_area_level.area_level,groupped_area_level.area)
plt.title('上海市二手房戶型分布')
可見扯夭,廣州二手房以中小戶型為主,占總數(shù)的94%以上鞍匾,大戶型及超大戶型只占很小一部分交洗。
各城區(qū)二手房總價及單價分布
總價分布情況
plt.figure(figsize=(10, 5))
sns.boxplot(x='region', y='price', data=df)
plt.ylim(0, 1250)
plt.grid()
plt.title('各城區(qū)總價箱型圖')
各城區(qū)平均總價排名
天河 464.750766
越秀 383.557367
海珠 329.517072
荔灣 296.762178
白云 291.132429
番禺 278.156564
黃埔 268.663933
南沙 213.583892
增城 200.978499
花都 170.217403
從化 145.550482
1.天河平均總價排第一,為464.75萬元候学,箱型圖區(qū)間為[275, 550]藕筋, 中位數(shù)約為380萬元纵散,中位數(shù)比平均值小梳码,少數(shù)價格較高的數(shù)據(jù)把整體平均值拉高了,數(shù)據(jù)呈現(xiàn)右偏伍掀。
2.越秀平均總價排第二掰茶,為383.56萬元,箱型圖區(qū)間為[210蜜笤, 500]濒蒋,中位數(shù)約為370萬元,中位數(shù)與平均值相差不大把兔。
3.海珠沪伙、荔灣、白云二手房總價平均值和中位數(shù)都比較接近县好,且三者箱型圖下限也比較接近围橡,但箱型圖上限有所差別。
df.price.hist(bins=50)
plt.xlim(0, 3000)
約90%以上的二手房都在1000萬元以下缕贡,極少數(shù)房價突破1000萬元翁授。
單價分布情況
plt.figure(figsize=(10, 5))
sns.boxplot(x='region', y='price_pre_m2', data=df)
plt.ylim(0, 125000)
plt.grid()
plt.title('各城區(qū)單價箱型圖')
各城區(qū)平均單價排名
df.groupby('region').mean().price_pre_m2.sort_values(ascending=False)
天河 50782.225834
越秀 49305.911005
海珠 40221.435223
荔灣 35937.422994
白云 32755.814030
黃埔 29843.146848
番禺 27449.547360
南沙 21036.807760
增城 19520.788061
花都 16302.711412
從化 14006.782274
排名前三的分別是天河拣播,越秀,海珠收擦。
天河區(qū)均價為50782元贮配,價格區(qū)間大約為[38000, 60000]元。
越秀區(qū)均價為49305元塞赂,價格區(qū)間大約為[40000, 59000]元泪勒。
海珠區(qū)均價為40221元,價格區(qū)間大約為[32000, 48000]元宴猾。
plt.figure(figsize=(10, 5))
df.price_pre_m2.hist(bins=50)
plt.xlim(0, 150000)
plt.title('廣州市二手房單價分布')
二手房單價主要集中在60000元以下的區(qū)間酣藻,總體分布明顯右偏,存在少數(shù)價格高的數(shù)據(jù)嚴(yán)重拉高了單價均值鳍置。
各小區(qū)總價及單價排名
總價排名
首先過濾掉房源較少的小區(qū)辽剧,此處取20為閾值。
df1 = df.groupby('xiaoqu').count().area.reset_index().sort_values(by='area', ascending=False)
df1 = df1[df1.area > 20]
df1 = df.loc[df.xiaoqu.isin(df.xiaoqu)]
df1.groupby('xiaoqu', as_index=False).mean().sort_values(by='price', ascending=False)[:10]
查看總價均值排名前十的小區(qū)税产。
df1.groupby('xiaoqu').mean().price.sort_values(ascending=False)[:10]
嘉裕公館 1684.22
粵海麗江花園 1671.81
凱旋新世界楓丹麗舍 1563.47
中海璟暉華庭 1212.66
雋峰苑 1177.27
星匯云錦 1104.36
南國花園 951.25
東風(fēng)廣場 927.52
朱美拉公寓 909.83
西關(guān)海 895.60
plt.figure(figsize=(20, 5))
xiaoqu_price = df1.groupby('xiaoqu', as_index=False).mean().sort_values(by='price', ascending=False)[:10]
sns.barplot(x='xiaoqu', y='price', data=xiaoqu_price)
plt.title('全市總價排名前十的小區(qū)')
總價排名前10的小區(qū)均超過了895萬元怕轿,排前兩名的嘉裕公館和粵海麗江花園相差不大,總價都在1600萬元以上辟拷。
單價排名
xiaoqu_price_pre_m2 = df1.groupby('xiaoqu', as_index=False).mean().sort_values(by='price_pre_m2', ascending=False)[:10]
xiaoqu_price_pre_m2[['xiaoqu', 'price_pre_m2']]
plt.figure(figsize=(20, 5))
sns.barplot(x='xiaoqu', y='price_pre_m2', data=xiaoqu_price_pre_m2)
plt.title('全市單價排名前十的小區(qū)')
排名前十的小區(qū)單價均超過了8萬元/平米撞羽,其中嘉裕公館和凱旋新世界楓丹麗舍遙遙領(lǐng)先,其余小區(qū)之間的差距并不大衫冻。
房價與樓層的關(guān)系
plt.figure(figsize=(10, 5))
floor_level_price_per_m2 = df.groupby('floor_level', as_index=False).mean().sort_values(by='price_pre_m2', ascending=False)
sns.barplot(x='floor_level', y='price_pre_m2', data=floor_level_price_per_m2)
plt.title('不同樓層等級與房價的關(guān)系')
可以明顯看出诀紊,不同樓層等級之間,房價單價并沒有明顯的差距隅俘,都在30000元以上邻奠。不同樓層對房價沒有太大的影響。
plt.figure(figsize=(10, 5))
floor_price = df.groupby('total_floor', as_index=False).mean().sort_values(by='price_pre_m2', ascending=False)
sns.lineplot(x='total_floor', y='price_pre_m2', data=floor_price)
plt.title('不同樓層總數(shù)與房價的關(guān)系')
由上圖分析可知为居,隨著樓層數(shù)目的增加碌宴,房價也隨之升高。在40層之前蒙畴,房價隨樓層數(shù)增加而增加的趨勢比較線性贰镣,但是在這之后房價隨樓層數(shù)增加而增加的趨勢波動很大,可能的原因是樓層數(shù)超過40的住宅數(shù)量較小膳凝,超過40層的數(shù)據(jù)量比較小碑隆,容易產(chǎn)生誤差。
房價與朝向的關(guān)系
plt.figure(figsize=(20, 15))
direction_price = df.groupby('direction', as_index=False).mean().sort_values(by='price_pre_m2', ascending=False)
sns.barplot(x='direction', y='price_pre_m2', data=direction_price)
可以看出蹬音,朝向?qū)Ψ績r有一定的影響上煤,但由于朝向數(shù)值比較復(fù)雜,并不是單一的東南西北祟绊,無法具體推測房價到底受什么朝向影響楼入。
房價與戶型的關(guān)系
上文中已將戶型拆分為房間數(shù)(room)和客廳數(shù)(living_room)哥捕,下文將分別探索這兩個特征與房價的關(guān)系。
room
df.room.value_counts()
3 22674
2 19293
1 5897
4 4577
5 930
6 152
7 39
8 10
0 5
9 4
12 2
13 1
10 1
房間數(shù)以1-4個為主嘉熊,占全部數(shù)據(jù)的90%以上遥赚。此外,存在一些數(shù)據(jù)量較少的房間數(shù)阐肤,這里選擇將數(shù)量為100以下的房間數(shù)都刪去凫佛。
df2 = df.groupby('room').count().area.reset_index().sort_values(by='area', ascending=False)
df2 = df2[df2.area > 20]
df2 = df.loc[df.room.isin(df2.room)]
5 35080.61
6 33549.43
1 33417.01
4 33253.89
2 32815.55
3 30831.33
plt.figure(figsize=(10, 5))
df2.groupby('room').mean().sort_values(by='price_pre_m2', ascending=False)[:10].price_pre_m2.plot(kind='bar')
plt.title('房間數(shù)對房價的影響')
房間數(shù)量對房價影響程度較低,側(cè)面反映了在選購二手房時孕惜,買房對房間數(shù)量沒有特別的偏好愧薛。
living_room
df2.living_room.value_counts()
2 31221
1 20587
0 1441
3 253
4 18
5 3
客廳數(shù)量主要為0-3個,過濾掉不足20條數(shù)據(jù)的living_room衫画。
df3 = df2.groupby('living_room').count().area.reset_index().sort_values(by='area', ascending=False)
df3 = df3[df3.area > 20]
df3 = df.loc[df.living_room.isin(df3.living_room)]
df3.groupby('living_room').mean().price_pre_m2.sort_values(ascending=False)
1 33904.73
3 33371.71
0 33300.13
2 30885.00
plt.figure(figsize=(10, 5))
df3.groupby('living_room').mean().sort_values(by='price_pre_m2', ascending=False)[:10].price_pre_m2.plot(kind='bar')
plt.title('客廳數(shù)對房價的影響')
平均單價最高的是1客廳的小戶型毫炉,3客廳及0客廳緊隨其后,三者之間單價相差不大削罩。平均單價最低的是2客廳的戶型瞄勾。可能原因是弥激,隨著房價的上漲进陡,小戶型由于總價較低的原因,更受市場的青睞微服。
房屋與建成年代的關(guān)系
plt.figure(figsize=(10, 5))
df.groupby('built_date').count().area.plot()
plt.title('建成年份-房源數(shù)量分布')
圖形呈雙峰狀態(tài)趾疚,分別在1999年和2013年出現(xiàn)了兩個房屋建筑的高峰。
plt.figure(figsize=(10, 5))
df.groupby('built_date').mean().price.plot()
plt.title('建成年份-房屋平均總價')
1973年以前建成的房屋總價最高以蕴,可能原因是早年間房屋都在城市的核心區(qū)域即市中心建成糙麦。1973年后房價逐漸上升,在2009年達(dá)到高峰舒裤,之后開始下滑喳资。可能原因是腾供,1973年后房屋大多在郊區(qū)建成,由于較低的土地成本鲜滩,郊區(qū)的房屋面積更大伴鳖,總價便水漲船高。2009年后房屋總價逐漸下滑的原因可能是徙硅,09年后房屋大多在增城榜聂、從化等地建成,由于其相對其他城區(qū)較低的房價嗓蘑,房屋總價也在逐漸下降须肆。
plt.figure(figsize=(10, 5))
df.groupby('built_date').mean().price_pre_m2.plot()
plt.title('建成年份-房屋平均單價')
由建成年份-房屋單價圖可以看出匿乃,其趨勢符合上文的推測,即早年間的房屋建在市中心故單價較高豌汇。其后房屋大多建在郊區(qū)幢炸,單價逐漸下降。
電梯與房價的關(guān)系
df.loc[df.elevator == '暫無數(shù)據(jù)', ['elevator', 'total_floor']]
可以看出拒贱,elevator特征存在1043行缺失值宛徊,此處考慮基于樓層總數(shù)填補(bǔ)缺失值。一般默認(rèn)大于7層樓的住宅擁有電梯逻澳,小于7層樓的住宅沒有電梯闸天。此處將暫無數(shù)據(jù)進(jìn)行替換。
df.loc[(df.elevator == '暫無數(shù)據(jù)') & (df.total_floor > 7), 'elevator'] = 1
df.loc[(df.elevator == '暫無數(shù)據(jù)') & (df.total_floor <= 7), 'elevator'] = 0
查看一下電梯數(shù)量分布斜做。
plt.figure(figsize=(10, 5))
sns.countplot(x='elevator', data=df)
plt.title('電梯數(shù)量分布')
可以看出苞氮,超過60%的房屋都擁有電梯,無電梯的房屋數(shù)量只占有電梯的一半瓤逼。
plt.figure(figsize=(10, 5))
sns.barplot(x='elevator', y='price_pre_m2', data=df)
plt.title('電梯數(shù)量-房屋單價')
由圖看出葱淳,有電梯的房屋單價比沒有電梯的房屋單價要高,這也符合我們的常識抛姑。
特征工程
首先對categorical類型的特征進(jìn)行one-hot處理赞厕。
def get_dummies(column, prefix):
global df
dummies = pd.get_dummies(df[column], prefix=prefix)
df = pd.concat([df, dummies], axis=1)
needed_get_dummies = ['renovation', 'direction', 'region', 'floor_level', 'field', 'xiaoqu', 'area_level']
for needed in needed_get_dummies:
get_dummies(needed, prefix=needed)
df.drop(needed, axis=1, inplace=True)
其次對連續(xù)型變量進(jìn)行標(biāo)準(zhǔn)化處理,此處用到的是MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
def scaler(column):
global df
scaler = MinMaxScaler()
df[column] = scaler.fit_transform(df[column].values.reshape(-1, 1))
needed_scale = ['area', 'room', 'living_room', 'total_floor']
for needed in needed_scale:
scaler(needed)
將房屋單價作為模型的target定硝,并歸一化皿桑。
y = df.pop('price_pre_m2')
y = MinMaxScaler().fit_transform(y.values.reshape(-1, 1))
df.drop(['price', 'house_type', 'built_date', 'floor'], axis=1, inplace=True)
特征選擇
df.shape
(53585, 5400)
由于特征數(shù)量較多,需要用特征選擇蔬啡,選出有用變量诲侮,刪去無用變量∠潴。可以使用隨機(jī)森林沟绪、gbdt等模型進(jìn)行特征選擇。對于回歸問題空猜,通常采用的是方差或者最小二乘擬合绽慈。當(dāng)訓(xùn)練決策樹的時候,可以計算出每個特征減少了多少樹的不純度辈毯。對于一個決策樹森林來說坝疼,可以算出每個特征平均減少了多少不純度,并把它平均減少的不純度作為特征選擇的值谆沃。
此處采用的是gbdt模型進(jìn)行特征選擇钝凶,保留feature_importance累計前99%的特征。
from sklearn.ensemble import GradientBoostingRegressor
X = df.values
names = df.columns
def get_importance(X, y, names):
gbdt = GradientBoostingRegressor(n_estimators=500)
gbdt.fit(X, y)
feature_importance = pd.Series(dict(zip(names, map(lambda x: round(x, 4), gbdt.feature_importances_))))
return feature_importance
feature_importance = get_importance(X, y, names)
feature_importance = feature_importance.sort_values(ascending=False)
sum = 0
features = []
for k, v in enumerate(feature_importance):
if sum > 0.99:
break
else:
sum += v
features.append(feature_importance.index[k])
print(len(features))
233
通過gbdt模型進(jìn)行特征選擇唁影,保留了233個特征耕陷。
X = df[features]
建模
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=0)
將數(shù)據(jù)分為訓(xùn)練集和測試集掂名,由于數(shù)據(jù)量有5萬多條,此處選擇測試集的test_size為0.1哟沫。用更多的數(shù)據(jù)進(jìn)行訓(xùn)練饺蔑。
使用keras建立神經(jīng)網(wǎng)絡(luò)模型。
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, BatchNormalization, Activation
from tensorflow.keras.optimizers import Adam, RMSprop, SGD
from tensorflow.keras.regularizers import l2
from tensorflow.keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler
def create_model():
input = Input(shape=(X.shape[1],))
x = Dense(128, kernel_regularizer=l2(0.1))(input)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(256, kernel_regularizer=l2(0.1))(x)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(256, kernel_regularizer=l2(0.1))(x)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(128, kernel_regularizer=l2(0.1))(x)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(64, kernel_regularizer=l2(0.1))(x)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(64, kernel_regularizer=l2(0.1))(x)
x = Activation('relu')(x)
x = BatchNormalization(axis=-1)(x)
x = Dense(1)(x)
model = Model(inputs=input, outputs=x)
return model
model = create_model()
model.compile(optimizer=SGD(1e-2), loss='mean_squared_error', metrics=['mse', 'mae'])
建立了一個7層神經(jīng)網(wǎng)絡(luò)模型南用,其每層神經(jīng)元數(shù)量分別是128,256,256,128,64,64,1膀钠。除了最后一層輸出層外,每一層都加入了一個l2正則項裹虫,正則化系數(shù)為0.1肿嘲,目的是防止過擬合。激活函數(shù)使用的是relu激活函數(shù)筑公,在激活函數(shù)后加入了一個batch normalization層雳窟,可以加快收斂速度。模型的優(yōu)化器使用sgd即隨機(jī)梯度下降匣屡,初始學(xué)習(xí)率定為1e-2封救,loss為均方差。
lrReduce = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10)
filepath="/content/drive/My Drive/tmp/sgd4_weights-improvement-{epoch:03d}-{val_loss:.4f}.hdf5"
checkpoint= ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_best_only=True, mode='min')
def scheduler(epoch):
lr_base = 1e-2
decay = 0.3
lr = lr_base * (0.95 ** epoch)
return lr
lrScheduler = LearningRateScheduler(scheduler)
為模型加入了三個回調(diào)對象捣作。分別是LearningRateScheduler即衰減學(xué)習(xí)率誉结。ReduceLROnPlateau用于監(jiān)視學(xué)習(xí)率,當(dāng)loss不再下降時券躁,將學(xué)習(xí)率減半惩坑。ModelCheckpoint,作用分別是保存最好的模型也拜。
history = model.fit(X_train, y_train, batch_size=128, epochs=200, validation_data=(X_test, y_test), callbacks=[lrReduce, checkpoint])
開始訓(xùn)練以舒,訓(xùn)練200個epochs,batch_size選128慢哈。
Epoch 00199: val_loss improved from 0.30553 to 0.30284, saving model to /content/drive/My Drive/tmp/sgd4_weights-improvement-199-0.3028.hdf5
訓(xùn)練完成后可見蔓钟,最小的驗證集誤差為0.30284。
mse = history.history['mean_squared_error']
val_mse = history.history['val_mean_squared_error']
plt.figure(figsize=(10, 5))
plt.plot(range(1, 201), mse, c='b', label='mse')
plt.plot(range(1, 201), val_mse, c='r', label='val_mse')
plt.grid()
plt.ylim(0, 3)
plt.legend()
繪制訓(xùn)練集-測試集誤差曲線卵贱,可以看出測試集誤差整體隨訓(xùn)練集誤差下降滥沫。在100個epoch前測試集誤差會有較大的波動,之后逐漸趨于平穩(wěn)艰赞,最終收斂不再下降佣谐。
from tensorflow.keras.models import load_model
model1 = load_model('/content/drive/My Drive/tmp/sgd4_weights-improvement-199-0.3028.hdf5')
print(r2_score(y_test, model1.predict(X_test)))
0.8750705820051174
模型在測試集上的r2系數(shù)為0.875,預(yù)測準(zhǔn)確度還算可以方妖。
總結(jié)
1.二手房單價受地理因素影響較大,不同城區(qū)之間房屋單價差異較大罚攀,最高的天河區(qū)平均單價為50782元/平米党觅,最低的從化區(qū)平均單價為14006元/平米同一城區(qū)不同小區(qū)的房屋單價也有較大的差異雌澄。最貴的小區(qū),房屋單價高達(dá)10萬元杯瞻,遠(yuǎn)遠(yuǎn)超過城區(qū)的均價镐牺。
2.二手房的數(shù)量和總面積可以反映市場的大小。番禺區(qū)二手房源數(shù)量10832套魁莉,占全市所有房源的20.21%睬涧,其二手房總面積為1067891.33平米為全市最高。不管從數(shù)量還是面積來看旗唁,番禺的二手房市場都是全區(qū)最大的畦浓,主要原因為其平均單價為27000元/平米,僅為天河區(qū)的一半检疫。
3.樓層讶请、戶型、朝向屎媳、電梯等要素對二手房的單價也有影響夺溢,但影響并不顯著。
4.建成年份可以反映房屋的建造時間烛谊,進(jìn)一步可以推測房屋大概的建造地點风响,從而推測房價。越早建成的房屋越傾向于建于市中心丹禀,反之則傾向于建在郊區(qū)状勤。
5.模型對測試集的r2系數(shù)為0.875,不算特別好湃崩。后續(xù)可以通過爬取更多的特征荧降、創(chuàng)建更多的特征、優(yōu)化模型結(jié)構(gòu)攒读、調(diào)參等方式來優(yōu)化模型的表現(xiàn)朵诫。