摘要

利用Python开发工具,从网站爬取某网站上的二手房数据,对数据源结合 Pandas、Numpy、Matplotlib、Sklearn 等技术进行分析,获得影响二手房买卖的具体因素。

1.引言

网络爬虫是可以自动抓取网页上各种类型数据的程序。网络爬虫使用网页的链接地址查找网页的内容,并直接返回用户所需的数据,而无需手动操纵浏览器来获取它。爬虫是搜索引擎中的重要组成部分,可在互联网上为搜索引擎抓取数据。通用搜索引擎(例如谷歌和百度)等逐渐成为人们访问互联网的入口。但是,由于它们的通用性限制,在抓取网页时没有针对性,因而也无法对其抓取的结果进行针对特定领域的进一步分析,导致查询结果不够深入和专业化;另外,一般的搜索引擎通常会返回一些与用户所寻找的主题无关的结果,从而造成信息过载。
本文提出的爬虫程序通过模拟登录二手房网站,获取相关数据,并将这些数据保存到本地,为进一步的数据挖掘和分析提供了方便。最后,本文使用Python可视化方法对数据进行简单的数据分析。

2.系统结构

2.1 Python
Python的作者是荷兰人Guido von Rossum。1982年,Guido在阿姆斯特丹大学获得了数学和计算机科学的硕士学位。 与今天相比,在他那个时代,个人计算机的主频和RAM非常低,这导致计算机配置非常低。为了使程序在个人计算机上运行,所有编译器的核心都经过了优化,因为如果不进行优化,则较大的数组会填满内存。Guido希望编写一种新语言,该语言应该具有功能齐全,易于学习,易于使用并且可以扩展的功能。 1989年,Guido开始为Python语言编写编译器。
2.2 Pandas
Pandas是用于Python的数据分析包。 它包含许多高级数据结构和操作工具,可以让人们使用Python进行数据分析更加轻松快捷。
2.3 Numpy
Numpy是使用Python进行科学计算的基础包。它包括一个功能强大的N维数组对象Array。它具有成熟的函数库,例如矩阵数据类型和矢量处理等。
2.4 Matplotlib
数据可视化是数据分析的结果,可以更直观,更优雅地显示结果。Matplotlib库是Python数据可视化的重要类库之一。它包含大量的数据可视化资源,包括地图,3D等。
Matplotlib有四个部分:
(1)Matplotlib的基本figure类型;
(2)调整figure和样式以及颜色;
(3)在图形上添加注释(包括坐标轴范围,长宽比或坐标轴等);
(4)其他复杂图形。

3.具体实现

3.1 数据获取
为了对北京二手房买卖影响因素的具体分析,于是利用Python语言对某网站上的北京二手房数据进行网络爬虫获取,以表格形式展示:

3.2代码实现

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib as mpl

import matplotlib.pyplot as plt
from IPython.display import display
plt.style.use("fivethirtyeight")
sns.set_style({'font.sans-serif':['simhei','Arial']})


# 导入链家二手房数据
lianjia_df = pd.read_csv('D:\lianjia.csv')
display(lianjia_df.head(n=10))

# 检查缺失值情况
#lianjia_df.info()

lianjia_df.describe()

# 添加新特征房屋均价
df = lianjia_df.copy()
df['PerPrice'] = lianjia_df['Price']/lianjia_df['Size']

# 重新摆放列位置
columns = ['Region', 'District', 'Garden', 'Layout', 'Floor', 'Year', 'Size', 'Elevator', 'Direction', 'Renovation', 'PerPrice', 'Price']
df = pd.DataFrame(df, columns = columns)

# 重新审视数据集
display(df.head(n=10))

# 对二手房区域分组对比二手房数量和每平米房价
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()

f, [ax1,ax2] = plt.subplots(1, 2, figsize=(15, 5))
# 建房时间的分布情况
sns.distplot(df['Size'], bins=20, ax=ax1, color='r')
sns.kdeplot(df['Size'], shade=True, ax=ax1)
# 建房时间和出售价格的关系
sns.regplot(x='Size', y='Price', data=df, ax=ax2)
plt.show()


df.loc[df['Size']< 10]
plt.show()

df.loc[df['Size']>1000]
plt.show()

df = df[(df['Layout']!='叠拼别墅')&(df['Size']<1000)]
plt.show()

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()

df['Renovation'].value_counts()

# 去掉错误数据“南北”,因为爬虫过程中一些信息位置为空,导致“Direction”的特征出现在这里,需要清除或替换
df['Renovation'] = df.loc[(df['Renovation'] != '南北'), 'Renovation']

# 画幅设置
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()

misn = len(df.loc[(df['Elevator'].isnull()), 'Elevator'])
print('Elevator缺失值数量为:'+ str(misn))

# 由于存在个别类型错误,如简装和精装,特征值错位,故需要移除
df['Elevator'] = df.loc[(df['Elevator'] == '有电梯')|(df['Elevator'] == '无电梯'), 'Elevator']

# 填补Elevator缺失值
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('有无电梯数量对比',fontsize=15)
ax1.set_xlabel('是否有电梯')
ax1.set_ylabel('数量')
sns.barplot(x='Elevator', y='Price', data=df, ax=ax2)
ax2.set_title('有无电梯房价对比',fontsize=15)
ax2.set_xlabel('是否有电梯')
ax2.set_ylabel('总价')
plt.show()

grid = sns.FacetGrid(df, row='Elevator', col='Renovation', palette='seismic',height=4)
grid.map(plt.scatter, 'Year', 'Price')
grid.add_legend()


f, ax1= plt.subplots(figsize=(20,5))
sns.countplot(x='Floor', data=df, ax=ax1)
ax1.set_title('楼层特征分析',fontsize=15)
ax1.set_xlabel('数量')
ax1.set_ylabel('楼层')
plt.show()

4.实验结果分析

  1. 根据初步观察,可以发现一共有11个特征变量,价格(Price)在这里是我的目标变量。


2. 深入观察发现了数据集一共有23677条数据,其中Elevator特征有明显的缺失值。


在这里,上面的结果给出了特征值是数值的一些统计值,包括平均数,标准差,中位数,最小值,最大值,25%分位数,75%分位数。这些统计结果简单直接,对于初始了解一个特征好坏非常有用,举个例子就是我观察到的Size(即房屋面积)特征的最大值为1019平米,最小值为2平米,那么我就要思考这个在实际中是不是存在的,如果不存在就没有意义,那么这个数据就是一个异常值,会严重影响模型的性能。
当然,这只是我的初步观察,后续我再用数据可视化来清晰地展示,并证实我的猜测。
3.进一步分析后,我发现ID这个值其实没有什么实际意义,所以将其移除。由于房屋单价分析起来比较方便,简单的使用总价与面积比就可得到,所以增加一个新的特征PerPrice(即每平方米的价格,只用于分析,不是预测特征)。另外,特征的顺序也调整了一下,看起来比较舒服。

  1. 接着我对区域特征进行可视化分析,通过下图我可以分析不同区域房价和数量的对比。

    这里我使用了pandas的网络透视功能groupby分组排序。区域特征可视化直接采用seaborn完成,颜色使用调色板palette参数,颜色渐变,越浅说明越少,反之越多。然后可以观察到:
    (1)二手房均价:西城区的房价最贵均价大约11万/平方米,因为西城在二环以里,且是热门学区房的聚集地。其次是东城大约10万/平方米,然后是海淀大约8.5万/平方米,其它均低于8万/平方米。
    (2)二手房房数量:从数量统计上来看,目前二手房市场上比较火热的区域。海淀区和朝阳区二手房数量最多,差不多都接近3000套,毕竟大区,需求量也大。然后是丰台区,近几年正在改造建设,有赶超之势。
    (3)二手房总价:通过箱型图看到,各大区域房屋总价中位数都都在1000万以下,且房屋总价离散值较高,西城最高达到了6000万,说明房屋价格特征不是理想的正态分布。

  2. 通过distplot和kdeplot绘制柱状图观察 Size (面积)特征的分布情况(如下图),属于长尾类型的分布,这说明了有很多面积很大且超出正常范围的二手房。


6. 然后我再分析一下面积与价格之间的关系,通过regplot绘制了Size 和 Price 之间的散点图(如下图),可以发现 Size 特征基本与Price呈现线性关系,符合基本常识:面积越大,价格越高。但是有两组明显的异常点:一个是面积不到10平米,但是价格超出10000万;另一个是一个点面积超过了1000平米,价格很低,所以需要查看是什么情况。


经过查看发现这组数据是别墅,出现异常的原因是由于别墅结构比较特殊(无朝向无电梯),字段定义与二手商品房不太一样导致爬虫爬取数据错位。也因为别墅类型二手房不在我的考虑范围之内,故将其移除再次观察Size分布和Price关系。


观察另一个异常点不是普通的民用二手房,很可能是商用房,所以才有1房间0厅确有如此大超过1000平米的面积,这里选择移除。


重新进行可视化发现就没有明显的异常点了。

再进一步可视化分析,发现各种厅室组合搭配,竟然还有9室3厅,4室0厅等奇怪的结构。其中,2室一厅占绝大部分,其次是3室一厅,2室2厅,3室两厅。但是仔细观察特征分类下有很多不规则的命名,比如2室一厅与2房间1卫,还有别墅,没有统一的叫法。这样的特征是不能作为机器学习模型的数据输入的,需要使用特征工程进行相应的处理。


7. 接着再进行Renovation 特征分析
精装 11345 简装 8497 其他 3239 毛坯 576 南北 20
发现Renovation装修特征中有南北,它属于朝向的类型,这里可能是因为爬虫过程中一些信息位置为空,导致“Direction”朝向特征出现在这里,所以需要清除或替换掉。
进一步可视化分析观察到,精装修的二手房数量最多,简装其次。而对于价格来说,毛坯类型却是最高,其次是精装修。


8. 接着对有无电梯(Elevator)特征分析,初探数据的时候,我发现Elevator特征是有大量缺失值的,然后先看看有多少缺失值:


根据上图我们知道一共有8237条缺省值,有点大,为了解决,根据实际情况考虑,常用的方法有平均值/中位数填补法,直接移除,或者根据其他特征建模预测等。这里我考虑填补法,但是有无电梯不是数值,不存在平均值和中位数,所以我就是楼层 Floor 来判断有无电梯,一般的楼层大于6的都有电梯,而小于等于6层的一般都没有。


再进一步分析观察到,有电梯的二手房数量居多一些,因为高层土地利用率比较高,适合北京庞大的人群需要,而高层就需要电梯。相应的,有电梯二手房房价较高,因为电梯前期装修费和后期维护费包含内了(但这个价格比较只是一个平均的概念,比如无电梯的6层豪华小区价格就更高了)。
9. 接着对Year(年份)进行分析:
根据图24我们可以了解到,在Renovation(装修)和Elevator的分类条件下,使用FaceGrid分析 Year 值,观察结果为:整个二手房房价趋势是随着时间增长而增长的;2000年以后建造的二手房房价相较于2000年以前有很明显的价格上涨;1980年之前几乎不存在有电梯二手房数据,说明1980年之前还没有大面积安装电梯;1980年之前无电梯二手房中,简装二手房占绝大多数,精装反而很少。

10.然后对楼层(Floor)观察可以看到,6层二手房数量最多,但是单独的楼层特征没有什么意义,因为每个小区住房的总楼层数都不一样。通常情况下,中层比较受欢迎,价格也相对较高,底层和顶层较不受欢迎,价格也相对较低。 因此,楼层是非常复杂的特征,其也对房价具有较大的影响。

图25 Floor特征分析

5.总结和展望

这次的数据分析,算是比较全面的分析了北京二手房数据,也对于基于python的数据分析也有了更深层次的理解。但是还有一些待改进的地方,比如:
需要保证爬虫所得到的数据源是否准确,在进行数据分析时,需要先对所得数据进行筛选,不然可能会造成分析结果出现误差。所以,在以后的学习里,我会这对这些问题加倍注意,争取做出更加合理的数据分析。

参考文献:
[1] 邓英、夏帮贵.Python3基础教程[M].北京:人民邮电出版社,2016
[2] 张俊红.Python数据分析[M].北京:电子工业出版社,2019
[3] 张良均. Python数据分析与挖掘实战[M]. 机械工业出版社, 2016.
[4] IvanIdris, 伊德里斯, 张驭宇. Python数据分析基础教程:NumPy学习指南[M]. 人民邮电出版社, 2014.

更多推荐

关于二手房的Python数据分析