房源数据分析
将租房网站租房数据作为参考,分析统计如下指标:
- 统计每个区域的房源总数量,并使用热力图分析房源位置分布情况。
- 使用条形图分析哪种户型的数量最多、更受欢迎。
- 统计每个区域的平均租金,并结合柱状图和折线图分析各区域的房源数量和租金情况。
- 统计面积区间的市场占有率,并使用饼图绘制各区间所占的比例。
导入需要包
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
file_data = pd.read_csv('./data/房源数据.csv')
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11平米 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92平米 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57平米 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09平米 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67平米 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03平米 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4房间2卫 |
59平米 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41平米 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81平米 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43平米 |
2700 |
8223 rows × 5 columns
数据基本信息
file_data.shape
(8223, 5)
file_data.info()
RangeIndex: 8223 entries, 0 to 8222
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 区域 8223 non-null object
1 小区名称 8223 non-null object
2 户型 8223 non-null object
3 面积(㎡) 8223 non-null object
4 价格(元/月) 8223 non-null int64
dtypes: int64(1), object(4)
memory usage: 321.3+ KB
file_data.describe()
|
价格(元/月) |
count |
8223.000000 |
mean |
9512.297823 |
std |
9186.752612 |
min |
566.000000 |
25% |
4800.000000 |
50% |
6800.000000 |
75% |
10000.000000 |
max |
150000.000000 |
数据预处理
滤重
file_data.duplicated()
0 False
1 False
2 False
3 False
4 False
...
8218 False
8219 False
8220 False
8221 False
8222 False
Length: 8223, dtype: bool
file_data = file_data.drop_duplicates()
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11平米 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92平米 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57平米 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09平米 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67平米 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03平米 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4房间2卫 |
59平米 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41平米 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81平米 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43平米 |
2700 |
5773 rows × 5 columns
file_data.shape
(5773, 5)
处理空值
file_data = file_data.dropna()
file_data.shape
(5773, 5)
滤重及处理空值后shape一致,说明没有空值
数据类型转换
面积转化为float
file_data["面积(㎡)"].values
array(['59.11平米', '56.92平米', '40.57平米', ..., '92.41平米', '71.81平米',
'35.43平米'], dtype=object)
data_new = np.array([])
area = file_data["面积(㎡)"].values
for i in area:
data_new = np.append(data_new, np.array(i[:-2]))
data_new
array(['59.11', '56.92', '40.57', ..., '92.41', '71.81', '35.43'],
dtype='
data_new = data_new.astype(np.float64)
file_data.loc[:, "面积(㎡)"] = data_new
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4房间2卫 |
59.00 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43 |
2700 |
5773 rows × 5 columns
替换户型
house_data = file_data["户型"]
house_data
0 1室0厅
1 3室0厅
2 1室1厅
3 2室1厅
4 1室1厅
...
8218 3室1厅
8219 4房间2卫
8220 3室1厅
8221 2室1厅
8222 1室1厅
Name: 户型, Length: 5773, dtype: object
house_new = []
for i in house_data:
new_info = i.replace("房间", "室")
house_new.append(new_info)
file_data.loc[:,"户型"] = house_new
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4室2卫 |
59.00 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43 |
2700 |
5773 rows × 5 columns
图表分析
房源数量、位置分布分析
file_data["区域"].unique()
array(['东城', '丰台', '亦庄开发区', '大兴', '房山', '昌平', '朝阳', '海淀', '石景山', '西城',
'通州', '门头沟', '顺义'], dtype=object)
new_df = pd.DataFrame({"区域": file_data["区域"].unique(), "数量": [0]*13})
new_df
|
区域 |
数量 |
0 |
东城 |
0 |
1 |
丰台 |
0 |
2 |
亦庄开发区 |
0 |
3 |
大兴 |
0 |
4 |
房山 |
0 |
5 |
昌平 |
0 |
6 |
朝阳 |
0 |
7 |
海淀 |
0 |
8 |
石景山 |
0 |
9 |
西城 |
0 |
10 |
通州 |
0 |
11 |
门头沟 |
0 |
12 |
顺义 |
0 |
area_count = file_data.groupby(by="区域").count()
area_count
|
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
区域 |
|
|
|
|
东城 |
282 |
282 |
282 |
282 |
丰台 |
577 |
577 |
577 |
577 |
亦庄开发区 |
147 |
147 |
147 |
147 |
大兴 |
362 |
362 |
362 |
362 |
房山 |
180 |
180 |
180 |
180 |
昌平 |
347 |
347 |
347 |
347 |
朝阳 |
1597 |
1597 |
1597 |
1597 |
海淀 |
605 |
605 |
605 |
605 |
石景山 |
175 |
175 |
175 |
175 |
西城 |
442 |
442 |
442 |
442 |
通州 |
477 |
477 |
477 |
477 |
门头沟 |
285 |
285 |
285 |
285 |
顺义 |
297 |
297 |
297 |
297 |
new_df["数量"] = area_count.values
new_df
|
区域 |
数量 |
0 |
东城 |
282 |
1 |
丰台 |
577 |
2 |
亦庄开发区 |
147 |
3 |
大兴 |
362 |
4 |
房山 |
180 |
5 |
昌平 |
347 |
6 |
朝阳 |
1597 |
7 |
海淀 |
605 |
8 |
石景山 |
175 |
9 |
西城 |
442 |
10 |
通州 |
477 |
11 |
门头沟 |
285 |
12 |
顺义 |
297 |
new_df.sort_values(by="数量", ascending=False)
|
区域 |
数量 |
6 |
朝阳 |
1597 |
7 |
海淀 |
605 |
1 |
丰台 |
577 |
10 |
通州 |
477 |
9 |
西城 |
442 |
3 |
大兴 |
362 |
5 |
昌平 |
347 |
12 |
顺义 |
297 |
11 |
门头沟 |
285 |
0 |
东城 |
282 |
4 |
房山 |
180 |
8 |
石景山 |
175 |
2 |
亦庄开发区 |
147 |
户型数量分析
统计市场中哪种户型的房源数量偏多,并筛选出数量大于50的户型
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4室2卫 |
59.00 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43 |
2700 |
5773 rows × 5 columns
file_data.groupby(by="户型").count().sort_values(by="区域",ascending=False).head()
|
区域 |
小区名称 |
面积(㎡) |
价格(元/月) |
户型 |
|
|
|
|
2室1厅 |
2249 |
2249 |
2249 |
2249 |
1室1厅 |
844 |
844 |
844 |
844 |
3室1厅 |
766 |
766 |
766 |
766 |
3室2厅 |
489 |
489 |
489 |
489 |
2室2厅 |
265 |
265 |
265 |
265 |
groupby_house = file_data.groupby(by="户型").count().sort_values(by="区域",ascending=False)["区域"]
groupby_house = groupby_house[groupby_house > 50]
groupby_house
户型
2室1厅 2249
1室1厅 844
3室1厅 766
3室2厅 489
2室2厅 265
1室0厅 244
4室2厅 191
1室1卫 126
2室1卫 120
3室1卫 92
4室1厅 58
Name: 区域, dtype: int64
groupby_house.shape
(11,)
groupby_house.index
Index(['2室1厅', '1室1厅', '3室1厅', '3室2厅', '2室2厅', '1室0厅', '4室2厅', '1室1卫', '2室1卫',
'3室1卫', '4室1厅'],
dtype='object', name='户型')
groupby_house.values
array([2249, 844, 766, 489, 265, 244, 191, 126, 120, 92, 58],
dtype=int64)
house_size = groupby_house.index.size
show_houses = pd.DataFrame({"户型": [groupby_house.index[i] for i in range(house_size)],
"数量": [groupby_house.values[i] for i in range(house_size)]})
show_houses
|
户型 |
数量 |
0 |
2室1厅 |
2249 |
1 |
1室1厅 |
844 |
2 |
3室1厅 |
766 |
3 |
3室2厅 |
489 |
4 |
2室2厅 |
265 |
5 |
1室0厅 |
244 |
6 |
4室2厅 |
191 |
7 |
1室1卫 |
126 |
8 |
2室1卫 |
120 |
9 |
3室1卫 |
92 |
10 |
4室1厅 |
58 |
house_type = show_houses["户型"]
house_type_num = show_houses["数量"]
plt.barh(range(11), house_type_num)
plt.yticks(range(11), house_type)
plt.xlim(0, 2500)
plt.title("北京市各区域租房数量统计")
plt.xlabel("数量")
plt.ylabel("房屋类型")
for x, y in enumerate(house_type_num):
plt.text(y+0.5, x-0.2, "%s" % y)
plt.show()
平均租金分析
分析各地区目前的平均租金情况
file_data
|
区域 |
小区名称 |
户型 |
面积(㎡) |
价格(元/月) |
0 |
东城 |
万国城MOMA |
1室0厅 |
59.11 |
10000 |
1 |
东城 |
北官厅胡同2号院 |
3室0厅 |
56.92 |
6000 |
2 |
东城 |
和平里三区 |
1室1厅 |
40.57 |
6900 |
3 |
东城 |
菊儿胡同 |
2室1厅 |
57.09 |
8000 |
4 |
东城 |
交道口北二条35号院 |
1室1厅 |
42.67 |
5500 |
... |
... |
... |
... |
... |
... |
8218 |
顺义 |
怡馨家园 |
3室1厅 |
114.03 |
5500 |
8219 |
顺义 |
旭辉26街区 |
4室2卫 |
59.00 |
5000 |
8220 |
顺义 |
前进花园玉兰苑 |
3室1厅 |
92.41 |
5800 |
8221 |
顺义 |
双裕小区 |
2室1厅 |
71.81 |
4200 |
8222 |
顺义 |
樱花园二区 |
1室1厅 |
35.43 |
2700 |
5773 rows × 5 columns
area_sum = file_data.groupby(by="区域").sum()
area_sum = area_sum.rename(columns={'面积(㎡)': '总面积', '价格(元/月)': '总金额'})
area_sum
|
总面积 |
总金额 |
区域 |
|
|
东城 |
27353.99 |
3945550 |
丰台 |
50922.79 |
4404893 |
亦庄开发区 |
15995.53 |
1318400 |
大兴 |
35884.15 |
2286950 |
房山 |
15275.41 |
726750 |
昌平 |
35972.92 |
2521515 |
朝阳 |
166921.72 |
20281396 |
海淀 |
57210.39 |
7279350 |
石景山 |
13956.67 |
1156500 |
西城 |
37141.64 |
5636975 |
通州 |
46625.23 |
2719600 |
门头沟 |
20258.20 |
1048300 |
顺义 |
33668.97 |
2190900 |
area_sum["每平米租金(元)"] = np.array(round(area_sum["总金额"] / area_sum["总面积"],2).values)
area_sum
|
总面积 |
总金额 |
每平米租金(元) |
区域 |
|
|
|
东城 |
27353.99 |
3945550 |
144.24 |
丰台 |
50922.79 |
4404893 |
86.50 |
亦庄开发区 |
15995.53 |
1318400 |
82.42 |
大兴 |
35884.15 |
2286950 |
63.73 |
房山 |
15275.41 |
726750 |
47.58 |
昌平 |
35972.92 |
2521515 |
70.09 |
朝阳 |
166921.72 |
20281396 |
121.50 |
海淀 |
57210.39 |
7279350 |
127.24 |
石景山 |
13956.67 |
1156500 |
82.86 |
西城 |
37141.64 |
5636975 |
151.77 |
通州 |
46625.23 |
2719600 |
58.33 |
门头沟 |
20258.20 |
1048300 |
51.75 |
顺义 |
33668.97 |
2190900 |
65.07 |
之前创建的 new_df
对象(各区域房源数量)与area_sum
对象进行合并展示,由于这两个对象中都包含“区域”一列,所以这里可以采用主键的方式进行合并,也就是说通过 merge()函数来实现,具体代码如下。
area_sum = area_sum.reset_index()
area_sum
|
区域 |
总面积 |
总金额 |
每平米租金(元) |
0 |
东城 |
27353.99 |
3945550 |
144.24 |
1 |
丰台 |
50922.79 |
4404893 |
86.50 |
2 |
亦庄开发区 |
15995.53 |
1318400 |
82.42 |
3 |
大兴 |
35884.15 |
2286950 |
63.73 |
4 |
房山 |
15275.41 |
726750 |
47.58 |
5 |
昌平 |
35972.92 |
2521515 |
70.09 |
6 |
朝阳 |
166921.72 |
20281396 |
121.50 |
7 |
海淀 |
57210.39 |
7279350 |
127.24 |
8 |
石景山 |
13956.67 |
1156500 |
82.86 |
9 |
西城 |
37141.64 |
5636975 |
151.77 |
10 |
通州 |
46625.23 |
2719600 |
58.33 |
11 |
门头沟 |
20258.20 |
1048300 |
51.75 |
12 |
顺义 |
33668.97 |
2190900 |
65.07 |
df_merge = pd.merge(new_df, area_sum)
df_merge
|
区域 |
数量 |
总面积 |
总金额 |
每平米租金(元) |
0 |
东城 |
282 |
27353.99 |
3945550 |
144.24 |
1 |
丰台 |
577 |
50922.79 |
4404893 |
86.50 |
2 |
亦庄开发区 |
147 |
15995.53 |
1318400 |
82.42 |
3 |
大兴 |
362 |
35884.15 |
2286950 |
63.73 |
4 |
房山 |
180 |
15275.41 |
726750 |
47.58 |
5 |
昌平 |
347 |
35972.92 |
2521515 |
70.09 |
6 |
朝阳 |
1597 |
166921.72 |
20281396 |
121.50 |
7 |
海淀 |
605 |
57210.39 |
7279350 |
127.24 |
8 |
石景山 |
175 |
13956.67 |
1156500 |
82.86 |
9 |
西城 |
442 |
37141.64 |
5636975 |
151.77 |
10 |
通州 |
477 |
46625.23 |
2719600 |
58.33 |
11 |
门头沟 |
285 |
20258.20 |
1048300 |
51.75 |
12 |
顺义 |
297 |
33668.97 |
2190900 |
65.07 |
num = df_merge["数量"]
price = df_merge["每平米租金(元)"]
lx = df_merge["区域"]
l = [i for i in range(13)]
fig = plt.figure(figsize=(10, 8), dpi=100)
ax1 = fig.add_subplot(111)
ax1.plot(l, price, "or-", label="价格")
for i, (_x, _y) in enumerate(zip(l, price)):
plt.text(_x+0.2, _y, price[i])
ax1.set_ylim([0, 160])
ax1.set_ylabel("价格")
plt.legend(loc="upper right")
ax2 = ax1.twinx()
plt.bar(l, num, label="数量", alpha=0.2, color="green")
ax2.set_ylabel("数量")
plt.legend(loc="upper left")
plt.xticks(l, lx)
plt.show()
从图中可以看出,西城区、东城区、海淀区、朝阳区的房租价格相对较高,这主要是因为东城区和西城区作为北京市的中心区,租金相比其他几个区域自然偏高一些,而海淀区租金较高的原因推测可能是海淀区名校较多,也是学区房最火热的地带,朝阳区内的中央商务区聚集了大量的世界500强公司,因此这四个区域的房租相对其他区域较高。
面积区间分析
下面我们将房屋的面积数据按照一定的规则划分成多个区间,看一下各面积区间的上情况,便于分析租房市场中哪种房屋类型更好出租,哪个面积区间的相房人数最多.
将数据划分为若干个区间,则可以使用Pame中的cut()函数来实现,首先,使用max()与min()方法分别计算出房屋面积的最大值和最小值,具体代码如下。
print('房屋最大面积是%d平米'%(file_data['面积(㎡)'].max()))
print('房屋最小面积是%d平米'%(file_data['面积(㎡)'].min()))
print('房租最高价格为每月%d元'%(file_data['价格(元/月)'].max()))
print('房屋最低价格为每月%d元'%(file_data['价格(元/月)'].min()))
房屋最大面积是1133平米
房屋最小面积是11平米
房租最高价格为每月150000元
房屋最低价格为每月566元
area_divide = [1, 30, 50, 70, 90, 120, 140, 160, 1200]
area_cut = pd.cut(list(file_data["面积(㎡)"]), area_divide)
area_cut
[(50, 70], (50, 70], (30, 50], (50, 70], (30, 50], ..., (90, 120], (50, 70], (90, 120], (70, 90], (30, 50]]
Length: 5773
Categories (8, interval[int64]): [(1, 30] < (30, 50] < (50, 70] < (70, 90] < (90, 120] < (120, 140] < (140, 160] < (160, 1200]]
area_cut_num = area_cut.describe()
area_cut_num
|
counts |
freqs |
categories |
|
|
(1, 30] |
41 |
0.007102 |
(30, 50] |
710 |
0.122986 |
(50, 70] |
1566 |
0.271263 |
(70, 90] |
1094 |
0.189503 |
(90, 120] |
1082 |
0.187424 |
(120, 140] |
381 |
0.065997 |
(140, 160] |
274 |
0.047462 |
(160, 1200] |
625 |
0.108263 |
area_per = (area_cut_num["freqs"].values)*100
labels = ['30平米以下', '30-50平米', '50-70平米', '70-90平米',
'90-120平米', '120-140平米', '140-160平米', '160平米以上']
plt.figure(figsize=(20, 8), dpi=100)
plt.pie(x=area_per, labels=labels, autopct="%.2f %%")
plt.legend()
plt.show()