数据分析——房源分析项目

导入模块

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
%matplotlib inline
import seaborn as sns
from IPython.display import display
plt.style.use("fivethirtyeight")
sns.set_style({
    
    'font.sans-serif':['simhei','Arial']})

ps:数据已上传至 github 请点击下载


数据规整

lf_df = pd.read_csv('./lianjia.csv')
lf_df.head()
Direction District Elevator Floor Garden Id Layout Price Region Renovation Size Year
0 东西 灯市口 NaN 6 锡拉胡同21号院 101102647043 3室1厅 780.0 东城 精装 75.0 1988
1 南北 东单 无电梯 6 东华门大街 101102650978 2室1厅 705.0 东城 精装 60.0 1988
2 南西 崇文门 有电梯 16 新世界中心 101102672743 3室1厅 1400.0 东城 其他 210.0 1996
3 崇文门 NaN 7 兴隆都市馨园 101102577410 1室1厅 420.0 东城 精装 39.0 2004
4 陶然亭 有电梯 19 中海紫御公馆 101102574696 2室2厅 998.0 东城 精装 90.0 2010
# 获取所有数据列信息
lf_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 23677 entries, 0 to 23676
Data columns (total 12 columns):
Direction     23677 non-null object
District      23677 non-null object
Elevator      15440 non-null object
Floor         23677 non-null int64
Garden        23677 non-null object
Id            23677 non-null int64
Layout        23677 non-null object
Price         23677 non-null float64
Region        23677 non-null object
Renovation    23677 non-null object
Size          23677 non-null float64
Year          23677 non-null int64
dtypes: float64(2), int64(3), object(7)
memory usage: 2.2+ MB
# 拷贝一份数据,不对原始数据 进行 修改
df = lf_df.copy()
df.head()
Direction District Elevator Floor Garden Id Layout Price Region Renovation Size Year
0 东西 灯市口 NaN 6 锡拉胡同21号院 101102647043 3室1厅 780.0 东城 精装 75.0 1988
1 南北 东单 无电梯 6 东华门大街 101102650978 2室1厅 705.0 东城 精装 60.0 1988
2 南西 崇文门 有电梯 16 新世界中心 101102672743 3室1厅 1400.0 东城 其他 210.0 1996
3 崇文门 NaN 7 兴隆都市馨园 101102577410 1室1厅 420.0 东城 精装 39.0 2004
4 陶然亭 有电梯 19 中海紫御公馆 101102574696 2室2厅 998.0 东城 精装 90.0 2010
# 计算 每一平米的价格
# 总价 / 总面积
df['PerPrice'] = df['Price'] / df['Size']
df.head()
Direction District Elevator Floor Garden Id Layout Price Region Renovation Size Year PerPrice
0 东西 灯市口 NaN 6 锡拉胡同21号院 101102647043 3室1厅 780.0 东城 精装 75.0 1988 10.400000
1 南北 东单 无电梯 6 东华门大街 101102650978 2室1厅 705.0 东城 精装 60.0 1988 11.750000
2 南西 崇文门 有电梯 16 新世界中心 101102672743 3室1厅 1400.0 东城 其他 210.0 1996 6.666667
3 崇文门 NaN 7 兴隆都市馨园 101102577410 1室1厅 420.0 东城 精装 39.0 2004 10.769231
4 陶然亭 有电梯 19 中海紫御公馆 101102574696 2室2厅 998.0 东城 精装 90.0 2010 11.088889
# 重新摆放列的位置
columns = ['Region','District','Garden','Layout','Floor','Year','Size',
           'Elevator','Direction','PerPrice','Price']
# 第一种方法
df.loc[:,columns]
# 第二种方法
df = pd.DataFrame(df,columns=columns)
display(df.head(2))
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 NaN 东西 10.40 780.0
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.75 705.0

数据分析

地区特征分析

对二手房区域分组,对比各个地区二手房数量和每平米房价

各个地区二手房数量

# count()统计数量,
# sort_values(ascending=True)升序排列
# to_frame()将series转换为dataframe
df_house_count = df.groupby('Region')['Size']\
.count()\
.sort_values(ascending=True)\
.to_frame()
# 修改列名称
df_house_count = df_house_count.reset_index()\
.rename({
    
    'Region':'地区','Size':'数量'},axis=1)\
.set_index('地区')
df_house_count
数量
地区
密云 12
怀柔 15
平谷 41
亦庄开发区 469
门头沟 496
石景山 882
顺义 1221
房山 1442
东城 1533
通州 1602
大兴 2115
西城 2130
昌平 2811
丰台 2952
朝阳 2973
海淀 2983

各地区 每平米房价对比

df_mean_house = df.groupby('Region')['PerPrice'].mean()\
.to_frame().reset_index()\
.rename({
    
    'Region':'地区','PerPrice':'每平米房价'},axis=1)\
.set_index('地区')
df_mean_house
每平米房价
地区
东城 9.864243
丰台 6.098628
亦庄开发区 4.696853
大兴 5.414790
密云 2.392645
平谷 2.749869
怀柔 3.934458
房山 3.948686
昌平 4.402832
朝阳 7.317792
海淀 8.847025
石景山 5.535677
西城 10.688348
通州 5.240839
门头沟 4.017825
顺义 4.974990

各地区房价和数量信息可视化

fig = plt.figure(figsize=(12,9))
ax1 = fig.add_subplot(5,1,1)
sns.barplot(x = df_mean_house.index,
            y = '每平米房价',
            palette = 'Blues_d',
            data = df_mean_house,
            ax = ax1)
# 修改 x轴的字体大小
ax1.set_xlabel('地区',{
    
    'size':14})
# 修改 y轴的字体大小
ax1.set_ylabel('每平方米房价',{
    
    'size':14})
# 设置刻度字体大小
ax1.tick_params(labelsize=10)
# 设置子图的标题
_ = ax1.set_title('北京各地区二手房每平米单价对比',fontsize=14)
# 第二个子图
ax2 = fig.add_subplot(5,1,3)

sns.barplot(x=df_house_count.index,
            y=df_house_count['数量'],
            data = df_house_count,
            palette='Greens_d',
            ax=ax2,
           )
# 修改 x轴的字体大小
ax2.set_xlabel('地区',{
    
    'size':14})
# 修改 y轴的字体大小
ax2.set_ylabel('数量',{
    
    'size':14})
# 设置刻度字体大小
ax2.tick_params(labelsize=10)
# 设置子图的标题
_ = ax2.set_title('北京各地区二手房数量',fontsize=14)

# 第三个子图,箱型图
ax3 = fig.add_subplot(5,1,5)
sns.boxplot(x='Region',y='Price',data=df,ax=ax3)
_ = ax3.set_title("北京各大区二手房房屋总价",fontsize=14)
ax3.set_xlabel("区域")
_ = ax.set_ylabel("房屋总价")

房屋面积特征分析

fig = plt.figure(figsize=(15,9))
ax1 = fig.add_subplot(1,2,1)
# 画出直方图,查看房屋面积特征分布
sns.distplot(df['Size'],bins=100,ax=ax1,color='r',kde=True)
ax2 = fig.add_subplot(1,2,2)
# 房屋面积和价格的关系
sns.regplot(x='Size',y='Price',data=df,ax=ax2)

size分布

通过 distplot绘制的柱状图观察size特征的分布情况,属于长尾类型的分布
所谓的长尾分布,就是说明有一些面积很大超出正差范围的二手房

Size和Price的关系

通过 regplot绘制的线性图,发现Size特征基本于Price呈线性关系。
符合常识,面积越大,价格越高。
但是有两组明显的异常点:

  1. 面积很小,不到10平米,但价格却贵的离谱
  2. 有一个面积超过了1000平米,价格却特别低
# 筛选出面积小于10平米所有的房屋
condition = df['Size'] < 10
df[condition]
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price
1168 房山 长阳 世茂维拉 叠拼别墅 5 2015 5.0 毛坯 240.97平米 216.000000 1080.0
1458 房山 长阳 世茂维拉 叠拼别墅 5 2015 5.0 毛坯 242.78平米 220.000000 1100.0
1797 房山 长阳 世茂维拉 叠拼别墅 5 2015 5.0 精装 242.96平米 196.000000 980.0
2268 顺义 顺义其它 龙湖好望山 叠拼别墅 4 2014 4.0 精装 295.88平米 250.000000 1000.0
2274 顺义 顺义其它 鹭峯国际 叠拼别墅 4 2014 5.0 精装 295.01平米 290.000000 1450.0
2276 顺义 顺义其它 龙湖好望山 叠拼别墅 3 2014 4.0 毛坯 292.31平米 215.000000 860.0
2432 顺义 顺义其它 龙湖好望山 叠拼别墅 5 2013 6.0 精装 294.42平米 163.333333 980.0
4078 大兴 西红门 鸿坤林语墅 叠拼别墅 3 2015 4.0 精装 427.5平米 787.500000 3150.0
4079 大兴 西红门 鸿坤林语墅 叠拼别墅 4 2015 4.0 精装 361.8平米 595.000000 2380.0
4761 大兴 西红门 鸿坤林语墅 叠拼别墅 3 2015 5.0 精装 386.83平米 540.000000 2700.0
7533 昌平 回龙观 龙城花园N区 叠拼别墅 4 1997 2.0 简装 107.93平米 310.000000 620.0
8765 通州 通州其它 旭辉御锦 叠拼别墅 6 2014 5.0 毛坯 195.32平米 156.000000 780.0
9020 通州 通州其它 旭辉御锦 叠拼别墅 6 2014 4.0 精装 259.87平米 280.000000 1120.0
9080 通州 通州其它 旭辉御锦 叠拼别墅 6 2014 4.0 毛坯 259.76平米 262.500000 1050.0
9203 通州 通州其它 旭辉御锦 叠拼别墅 6 2014 4.0 精装 260.07平米 262.500000 1050.0
9254 通州 通州其它 旭辉御锦 叠拼别墅 6 2014 4.0 毛坯 264.6平米 275.000000 1100.0
11531 丰台 丽泽 西宸原著 叠拼别墅 6 2016 4.0 毛坯 335.51平米 1000.000000 4000.0
14298 海淀 西山 中间建筑一区 叠拼别墅 3 2007 8.0 精装 266.61平米 168.750000 1350.0
15334 海淀 西山 西山美墅馆F区 叠拼别墅 4 2004 4.0 简装 203.73平米 550.000000 2200.0
17311 朝阳 大望路 首府官邸 叠拼别墅 5 2007 5.0 精装 523.4平米 900.000000 4500.0

所有面积小于10的数据全部都是别墅,数据出现异常的原因是由于别墅的结构
比较特殊,字段定义与二手商品放不太一样导致爬虫爬取数据错位。
将这些别墅房移除。

# 筛选出面积大于1000的房屋
condition = df['Size']>1000
df[condition]
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price
8754 通州 通州其它 新华联科技大厦 1房间0卫 8 2009 1019.0 有电梯 1.668302 1700.0

结果发现,房屋面积大于1000的不是普通的民用房屋
而是商业用房,这个也需要移除

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

重新可视化后,就没异常数据了

fig = plt.figure(figsize=(15,9))
ax1 = fig.add_subplot(1,2,1)
# 画出直方图,查看房屋面积特征分布
sns.distplot(df['Size'],bins=100,ax=ax1,color='r',kde=True)
ax2 = fig.add_subplot(1,2,2)
# 房屋面积和价格的关系
sns.regplot(x='Size',y='Price',data=df,ax=ax2)

户型特征分析

plt.figure(figsize=(15,15))
sns.countplot(y='Layout',data=df)
_ = plt.title('房屋户型',fontsize=15)
plt.xlabel('数量')
plt.ylabel('户型')
plt.show()

# 找出户型特征最多的五个
df['Layout'].value_counts().sort_values(ascending=False).head()
2室1厅    9485
3室1厅    3999
3室2厅    2765
1室1厅    2681
2室2厅    1671
Name: Layout, dtype: int64

户型特征的厅室搭配花样太多了,各种奇怪的结构,需要进行特征处理

可以看出,最多的是二室一厅

现在假设,两室一厅、三室两厅为精装房

三室一厅、二室二厅为简装房

一室0厅、二室0厅为毛胚房

剩余的划分为 其它

# 条件
map_dict = {
    
    
    '2室1厅':'精装房',
    '2室2厅':'简装房',
    '3室1厅':'简装房',
    '1室0厅':'毛胚房',
    '2室0厅':'毛胚房',
    '3室1厅':'精装房',
}
# 增加一列,作为户型类别
df.loc[:,'Renovation'] = df.Layout.map(map_dict)
df.loc[:,'Renovation']=df['Renovation'].fillna('其它')
df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 NaN 东西 10.400000 780.0 精装房
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.750000 705.0 精装房
2 东城 崇文门 新世界中心 3室1厅 16 1996 210.0 有电梯 南西 6.666667 1400.0 精装房
3 东城 崇文门 兴隆都市馨园 1室1厅 7 2004 39.0 NaN 10.769231 420.0 其它
4 东城 陶然亭 中海紫御公馆 2室2厅 19 2010 90.0 有电梯 11.088889 998.0 简装房

对 Renovation 特征分析

df['Renovation'].value_counts()
精装房    13484
其它      7952
简装房     1671
毛胚房      549
Name: Renovation, dtype: int64
fig = plt.figure(figsize=(20,5))
ax1 = fig.add_subplot(1,3,1)
# 统计各类房的数量
sns.countplot(df['Renovation'],ax=ax1)

# 柱状图
ax2 = fig.add_subplot(1,3,2)
# 使用的是平均值
sns.barplot(x='Renovation',y='Price',data=df,ax=ax2)
# 箱型图
ax3 = fig.add_subplot(1,3,3)
sns.boxplot(x='Renovation',y='Price',data=df,ax=ax3)

电梯特征分析

之前的处理阶段,我们能发现,Elevator特征是存在着大量的缺失值
所以需要进行一些处理

# 查看缺失值数量
condition = df['Elevator'].isnull()
len(df[condition])
8237

常见的处理缺失值方法有:平均数、中位数填补/删除缺失值

拉格朗日中值法等等,但是有无电梯并不是具体的数据。根本不存在平均数、中位数这一说

可以换一种思路,当楼层大于等于6层就要有电梯,小于六层就没有

df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 NaN 东西 10.400000 780.0 精装房
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.750000 705.0 精装房
2 东城 崇文门 新世界中心 3室1厅 16 1996 210.0 有电梯 南西 6.666667 1400.0 精装房
3 东城 崇文门 兴隆都市馨园 1室1厅 7 2004 39.0 NaN 10.769231 420.0 其它
4 东城 陶然亭 中海紫御公馆 2室2厅 19 2010 90.0 有电梯 11.088889 998.0 简装房
def ModifyElevator(x):
    # 由于 np.nan为flaot对象
    if isinstance(x['Elevator'],float):
        if x.Floor >6:
            return '有电梯'
        else:
            return '无电梯'
    return x['Elevator']
df['Elevator']= df.apply(ModifyElevator,axis=1)

电梯特征可视化

fig = plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(1,2,1)
sns.countplot(x='Elevator',data=df,ax=ax1)
ax2 = fig.add_subplot(1,2,2)
sns.barplot(x='Elevator',y='Price',data=df,ax=ax2)

年份特征分析

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

整个二手房房价趋势是随着时间增长而增长的

毛胚房的价格稳定

简装房主要是在 1980年之后

1980年之前几乎不存在电梯二手房数据,说明1980年之前还没有大面积安装电梯

楼层特征分析

fig = plt.figure(figsize=(12,9))
ax1 = fig.add_subplot(1,1,1)
sns.countplot(x='Floor',data=df,ax=ax1)
_ = ax1.set_xlabel('楼层',fontsize=20)
_ =ax1.set_ylabel('数量',fontsize=20)

可以看到,6层二手房数量最多,但是单独的楼层特征没有什么意义,因为每个小区

住房的总楼层都一样,我们需要知道楼层的相对意义。

此外,楼层与文化也紧密联系在一次,中国人喜欢6不喜欢4

喜欢七层不喜欢八层,也不喜欢十八层

这些复杂的条件结合起来,就使得楼层变成一个非常复杂的特征。

特征工程

特征工程包括的内容很多,有特征的清洗,预处理、监控等,而预处理根据单一特征

或多特征又分很多种方法,如归一化、降维,特征选择,特征筛选等等。

这么多方法,为的是什么呢?其目的就是让这些特征更友好的作为模型输入

处理数据的好坏会严重影响模型的性能,而好的特征工程有的时候甚至比建模调参更重要

之前我们处理一些数据,就是我们常说的特征工程,如下几个例子:

'''
    特征工程
'''

# 1. 移除结构类型异常值和房屋大小异常值
# condition = (df['Layout'] != '叠拼别墅') & ( df['Size'] <1000)
# df = df[condition]

# 2. 填补 Elevator缺失值
# def ModifyElevator(x):
#     # 由于 np.nan为flaot对象
#     if isinstance(x['Elevator'],float):
#         if x.Floor >6:
#             return '有电梯'
#         else:
#             return '无电梯'
#     return x['Elevator']
# df['Elevator']= df.apply(ModifyElevator,axis=1)

# 3. 只考虑 “室” 和 “厅”,将其它少数“房间” 和 “卫” 移除
# 3室1厅
df = df[(df['Layout']\
         .str.extract('^\d(.*?)\d.*?') == '室')[0]]
df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 无电梯 东西 10.400000 780.0 精装房
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.750000 705.0 精装房
2 东城 崇文门 新世界中心 3室1厅 16 1996 210.0 有电梯 南西 6.666667 1400.0 精装房
3 东城 崇文门 兴隆都市馨园 1室1厅 7 2004 39.0 有电梯 10.769231 420.0 其它
4 东城 陶然亭 中海紫御公馆 2室2厅 19 2010 90.0 有电梯 11.088889 998.0 简装房
# 4. 提取 “室” 和 “厅”创建新特征
df.loc[:,'Layout_roomNum'] = df['Layout'].str.extract(r'(\d)室')[0]
df.loc[:,'Layout_hallNum'] = df['Layout'].str.extract(r'\d.*(\d).*')[0]
df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation Layout_roomNum Layout_hallNum
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 无电梯 东西 10.400000 780.0 精装房 3 1
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.750000 705.0 精装房 2 1
2 东城 崇文门 新世界中心 3室1厅 16 1996 210.0 有电梯 南西 6.666667 1400.0 精装房 3 1
3 东城 崇文门 兴隆都市馨园 1室1厅 7 2004 39.0 有电梯 10.769231 420.0 其它 1 1
4 东城 陶然亭 中海紫御公馆 2室2厅 19 2010 90.0 有电梯 11.088889 998.0 简装房 2 2
# 5. 根据已有特征创造新特征

df.loc[:,'Layout_total_num'] = df['Layout_roomNum'].astype(np.int16) + \
    df['Layout_hallNum'].astype(np.int16)
df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation Layout_roomNum Layout_hallNum Layout_total_num
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 1988 75.0 无电梯 东西 10.400000 780.0 精装房 3 1 4
1 东城 东单 东华门大街 2室1厅 6 1988 60.0 无电梯 南北 11.750000 705.0 精装房 2 1 3
2 东城 崇文门 新世界中心 3室1厅 16 1996 210.0 有电梯 南西 6.666667 1400.0 精装房 3 1 4
3 东城 崇文门 兴隆都市馨园 1室1厅 7 2004 39.0 有电梯 10.769231 420.0 其它 1 1 2
4 东城 陶然亭 中海紫御公馆 2室2厅 19 2010 90.0 有电梯 11.088889 998.0 简装房 2 2 4
# 6. 按中位数对 “year” 特征进行分箱
df.loc[:,'Year'] = pd.qcut(df['Year'],q=8,precision=0)
df.head()
Region District Garden Layout Floor Year Size Elevator Direction PerPrice Price Renovation Layout_roomNum Layout_hallNum Layout_total_num
0 东城 灯市口 锡拉胡同21号院 3室1厅 6 (1949.0, 1990.0] 75.0 无电梯 东西 10.400000 780.0 精装房 3 1 4
1 东城 东单 东华门大街 2室1厅 6 (1949.0, 1990.0] 60.0 无电梯 南北 11.750000 705.0 精装房 2 1 3
2 东城 崇文门 新世界中心 3室1厅 16 (1990.0, 1997.0] 210.0 有电梯 南西 6.666667 1400.0 精装房 3 1 4
3 东城 崇文门 兴隆都市馨园 1室1厅 7 (2003.0, 2004.0] 39.0 有电梯 10.769231 420.0 其它 1 1 2
4 东城 陶然亭 中海紫御公馆 2室2厅 19 (2007.0, 2010.0] 90.0 有电梯 11.088889 998.0 简装房 2 2 4
# 7. 删除无用特征
df.drop(['Garden','PerPrice'],axis=1,inplace=True)
df.head()
Region District Layout Floor Year Size Elevator Direction Price Renovation Layout_roomNum Layout_hallNum Layout_total_num
0 东城 灯市口 3室1厅 6 (1949.0, 1990.0] 75.0 无电梯 东西 780.0 精装房 3 1 4
1 东城 东单 2室1厅 6 (1949.0, 1990.0] 60.0 无电梯 南北 705.0 精装房 2 1 3
2 东城 崇文门 3室1厅 16 (1990.0, 1997.0] 210.0 有电梯 南西 1400.0 精装房 3 1 4
3 东城 崇文门 1室1厅 7 (2003.0, 2004.0] 39.0 有电梯 420.0 其它 1 1 2
4 东城 陶然亭 2室2厅 19 (2007.0, 2010.0] 90.0 有电梯 998.0 简装房 2 2 4

end …

猜你喜欢

转载自blog.csdn.net/weixin_42218582/article/details/103655328