上周有某高校老师来我们公司进行培训,公司安排我上了两天课。最后一天是一个数据分析的小案例,这里记录分享一下,比较适合刚入门的小白练手。
大概的逻辑是这样的:利用Scrapy爬取了链家的2900余条成都二手房的数据,然后基于这些数据做了一些关于房屋价格、区域、户型、房屋数量等方面的分析。
在分析之前呢,数据已经爬取好了,存到了Excel当中,所以这里就不演示爬虫部分,只进行分析部分的说明。但是需要看一下爬取的都是哪些字段
接下来就正式进入分析部分。首先导入必要的库,并做一部分设置:
代码语言:javascript复制import re
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# 指定画布风格
plt.style.use("fivethirtyeight")
# Mac环境下设置中文字体
plt.rcParams['font.family'] = ['Arial Unicode MS'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
sns.set_style({'font.sans-serif':['Arial Unicode MS','Arial']})
# Windows环境下设置中文字体
# sns.set_style({'font.sans-serif':['simhei','Arial']})
# 如果代码不在jupyter中执行,可以删掉
%matplotlib inline
我用的IDE是jupyter lab,分析部分主要使用pandas库,绘图部分用到了matplotlib和seaborn,在后面进行数据预处理部分我们需要用到正则表达式,所以同时导入re模块。
接下来为了美化图片我们统一设置了画布风格,同时为了在图片中正常显示中文做了字体设置。
然后就可以导入我们的数据了。需要说明的是我是将爬取的数据保存在同级目录下名为house.xlsx的文件中。
代码语言:javascript复制house_df = pd.read_excel('house.xlsx')
# 看一下数据长什么样子
house_df.head()
输出:
因为字段太多,所以字段显示不完整,但是可以大概了解一下。
查看一下缺失值的情况
代码语言:javascript复制# 查看确实值情况
house_df.info()
输出:
我们可以看到有些字段是2993行,有些字段是2895行,甚至还有2890行。这说明我们的数据中有缺失值存在,我们需要做的就是过滤掉缺失值。
代码语言:javascript复制house_df.dropna(inplace=True)
house_df.info()
过滤掉缺失值之后,再看一下
现在所有字段都是2886行,可以进行下一步了。
由于目前的数据中,很多字段都是带单位的字符串类型,我们需要做的是将它们转换为不带单位的字符串类型,这样的话有助于我们后续的分析。
代码语言:javascript复制# 做一部分预处理
house_df['houseTotalMoney'] = house_df['houseTotalMoney'].apply(lambda x: float(x.replace('万', '')))
house_df['houseSinglePrice'] = house_df['houseSinglePrice'].apply(lambda x: float(x.replace('元/平米', '')))
house_df['houseDownPayment'] = house_df['houseDownPayment'].apply(lambda x: float(x.replace('万', '')))
house_df['houseBuildingArea'] = house_df['houseBuildingArea'].apply(lambda x: float(x.replace('㎡', '')))
house_df['totalFloor'] = house_df['houseFloor'].apply(lambda x:re.search('d ', x).group())
在上面的代码中,我们去掉了houseTotalMoney、houseSinglePrice、houseDownPayment、houseBuildingArea四个字段的单位,并将其转换为float类型。最后一行中我们提取了houseFloor字段中的数字来表示房屋总楼层,保存到house_df的totalFloor列中。
然后我们再做一个小处理,从houseLocation字段中提取行政区信息,然后保存到Region列中。
代码语言:javascript复制# houseLocation字段表示区域,我们取前两个字,然后方便分区统计
house_df['Region'] = house_df['houseLocation'].apply(lambda x:x.split('/')[0])
看一下整体统计信息
代码语言:javascript复制# 再次观察有异常值
house_df.describe() # houseNumber为房屋编号 不用管
输出:
我们看到总价的平均值是143.65万,最便宜的是32万(有可能是老房子、公寓之类的,正常),最贵的是1100万。单价方面,均价是15685元/平米,最便宜的是5129元/平米,最贵的是44846元/平米。
现在正式进入可视化阶段。
首先我们看一下单价、房屋数量、总价和行政区的关系
代码语言:javascript复制# 按区域分析数量和价格
df_house_count = house_df.groupby('Region')['houseId'].count().sort_values(ascending=False).to_frame().reset_index().reindex(['Region', 'Count'], axis=1)
df_house_mean = house_df.groupby('Region')['houseSinglePrice'].mean().sort_values(ascending=False).to_frame().reset_index()
f, [ax1, ax2, ax3] = plt.subplots(3, 1, figsize=(12, 18))
sns.barplot(x='Region', y='houseSinglePrice', palette='Blues_d', data=df_house_mean, ax=ax1)
ax1.set_title('成都各区二手房每平米单价对比')
ax1.set_xlabel('区域')
ax1.set_ylabel('每平米单价')
sns.barplot(x='Region', y='Count', palette='Greens_d', data=df_house_count, ax=ax2)
ax2.set_title('成都各区二手房数量对比')
ax2.set_xlabel('区域')
ax2.set_ylabel('数量')
sns.boxplot(x='Region', y='houseTotalMoney', data=house_df, ax=ax3)
ax3.set_title('成都各区二手房房屋总价')
ax3.set_xlabel('区域')
ax3.set_ylabel('房屋总价')
plt.savefig('images/img1')
plt.show()
输出:
看一下房屋面积的分布情况,还有面积和总价的相关性
代码语言:javascript复制f, [ax1,ax2] = plt.subplots(1, 2, figsize=(16, 6))
# 房屋面积
sns.distplot(house_df['houseBuildingArea'], ax=ax1, color='r')
sns.kdeplot(house_df['houseBuildingArea'], shade=True, ax=ax1)
ax1.set_xlabel('面积')
# 房屋面积和价格的关系
sns.regplot(x='houseBuildingArea', y='houseTotalMoney', data=house_df, ax=ax2)
ax2.set_xlabel('面积')
ax2.set_ylabel('总价')
plt.savefig('images/img2')
plt.show()
输出:
看一下户型的数量情况
代码语言:javascript复制f, ax1 = plt.subplots(figsize=(12,12))
sns.countplot(y='houseType', data=house_df, ax=ax1)
ax1.set_title('房屋户型', fontsize=15)
ax1.set_xlabel('数量')
ax1.set_ylabel('户型')
plt.show()
输出:
看一下装修情况
代码语言:javascript复制f, [ax1,ax2,ax3] = plt.subplots(1, 3, figsize=(20, 5))
sns.countplot(house_df['houseDecoration'], ax=ax1)
sns.barplot(x='houseDecoration', y='houseTotalMoney', data=house_df, ax=ax2)
sns.boxplot(x='houseDecoration', y='houseTotalMoney', data=house_df, ax=ax3)
plt.savefig('images/img4')
plt.show()
输出:
看一下电梯的情况
统计电梯信息之前我们先做一下词频统计:
代码语言:javascript复制house_df['houseElevator'].value_counts()
输出:
我们看到有179套是“暂无数据”,对于这种情况我们就需要处理一下了。具体的处理方法有很多种,要根据不同的场景去选择,我们这里只是为了学习,可以粗暴一点,直接过滤掉,不让其参与计算。
代码语言:javascript复制elevator_df = house_df[house_df['houseElevator'] != '暂无数据']
elevator_df['houseElevator'].value_counts()
输出:
开始绘图:
代码语言:javascript复制f, [ax1, ax2] = plt.subplots(1, 2, figsize=(16, 8))
sns.countplot(elevator_df['houseElevator'], ax=ax1)
ax1.set_title('有无电梯数量对比', fontsize=15)
ax1.set_xlabel('是否有电梯')
ax1.set_ylabel('数量')
sns.barplot(x='houseElevator', y='houseSinglePrice', data=elevator_df, ax=ax2)
ax2.set_title('有无电梯房价对比')
ax2.set_xlabel('是否有电梯')
ax2.set_ylabel('单价')
plt.savefig('images/img5')
plt.show()
输出:
最后来看一下总楼层的数量情况
代码语言:javascript复制f, ax1 = plt.subplots(figsize=(20,5))
sns.countplot(x='totalFloor', data=house_df, ax=ax1)
ax1.set_title('房屋楼层',fontsize=15)
ax1.set_xlabel('总楼层')
ax1.set_ylabel('数量')
plt.savefig('images/img6')
plt.show()
输出:
以上只是一个学习用的简单例子,如果要在生产环境中使用的话还需要做一部分改动。比如涉及到两次缺失值的处理问题,要根据具体的情况去选择相应的方法;比如对于统计的图表选择也要根据具体的业务场景去选择;而且我们这个案例中是没有涉及到异常值的处理,实际上房屋价格是存在异常值的——20多万的房子肯定是不存在的,我怀疑是车位的交易信息。