matplotlib实例————北京市链家二手房源深度分析


matplotlib实例————北京市链家二手房源深度分析

分析背景

基于matplotlib(v3.2.1),通过爬取的链家北京二手房数据,深度分析当前北京二手房数据,为购房人士提供指导

爬取数据

来自链家北京二手房,使用scrapy库,分别爬取不同城区的数据并存入csv。限于网页,每个城区可以爬取到至多3000条数据。由于部分城区房源较少(不足500),不足以构成统计学依据,故最终选择均达到3000条数据的城区:东城、西城、海淀、朝阳、昌平、大兴、房山、顺义、通州。数据示例如下:

关注人数,区域,单价,城区,小区,已发布日,建成年份,建筑结构,总价,户型_厅,户型_室,朝向,标题,楼层,满二,满五,装修,近地铁,面积
27,和平里,120581,东城,化工大院 ,7,1988,板楼,1030,1,4,南 北,户型格局好 满五年央产 视野开阔 采光充足 配套齐全,中楼层(共4层),False,True,简装,True,85.42
74,永定门,69979,东城,东革新里40号院 ,7,1979,板楼,355,1,2,南,东城区低总价小两居,满五年唯一,有钥匙,诚心出售,中楼层(共5层),False,True,简装,True,50.73
30,永定门,71926,东城,李村 ,7,1995,板楼,496,1,2,南 北,南二环东城区满五唯一,优质南北通透低楼层两居,低楼层(共6层),False,True,简装,True,68.96
11,交道口,119718,东城,交道口北二条35号院 ,6,1988,板楼,660,1,2,南 北,交道口北二条35号院 2室1厅 660万,顶层(共6层),False,True,精装,True,55.13
34,永定门,79500,东城,景泰西里 ,6,1985,板楼,318,1,1,南,景泰西里 南向一居室 中间楼层 诚意出售,低楼层(共6层),False,True,简装,True,40

具体参见Scrapy实例————爬取链家二手房数据,并处理后存入csv

分析数据

初步分析

绘制各城区总价和单价箱型图

import pandas as pd
import matplotlib.pyplot as plt
import os

# 为了显示中文,需要更换字体
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
# 如果你是macOSX,请使用以下
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


path = 'data.csv'  # 文件名
data = pd.read_csv(path, usecols=['总价', '单价', '城区', '面积'])

data.boxplot(by='城区', column='总价')
plt.title('北京市(部分城区)二手房总价分析', fontsize=20)  # 标题,并设定字号大小
plt.suptitle('')  # 隐藏默认的groupby文字
# plt.show()
plt.savefig(os.path.join('图表', '初步分析', '总价箱型图.png'), bbox_inches='tight')


plt.cla  # 清空
data.boxplot(by='城区', column='单价')
plt.title('北京市(部分城区)二手房单价分析', fontsize=20)  # 标题,并设定字号大小
plt.suptitle('')  # 隐藏默认的groupby文字
# plt.show()
plt.savefig(os.path.join('图表', '初步分析', '单价箱型图.png'), bbox_inches='tight')


plt.cla  # 清空
data.boxplot(by='城区', column='面积')
plt.title('北京市(部分城区)二手房面积分析', fontsize=20)  # 标题,并设定字号大小
plt.suptitle('')  # 隐藏默认的groupby文字
# plt.show()
plt.savefig(os.path.join('图表', '初步分析', '面积箱型图.png'), bbox_inches='tight')




至此,我们对数据有了初步的认识,那么是什么因素导致单价的区别呢?

房屋总价与面积的线性回归

面积和总价线性回归

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os

# 为了显示中文,需要更换字体
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
# 如果你是macOSX,请使用以下
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


path = 'data.csv'  # 文件名
df_all = pd.read_csv(path, usecols=['总价', '面积', '城区'])  # 读取文件


# 拟合方法
def fitSLR(x,y):
    dinominator = 0
    numerator = 0
    for i in range(len(x)):
        numerator += (x[i] - np.mean(x)) * (y[i] - np.mean(y))
        dinominator += (x[i] - np.mean(x)) ** 2
    # print("numerator = ", numerator)
    # print("dinominator = ", dinominator)
    slope = numerator / float(dinominator)
    intercept = np.mean(y) - slope * np.mean(x)
    # print("slope = ", slope)
    # print("intercept = ", intercept)
    return slope, intercept


lines = pd.DataFrame(columns=('slope', 'intercept'))


# 总
slope, intercept = fitSLR(df_all['面积'], df_all['总价'])  # 拟合,得到直线的斜率和截距
plt.scatter(df_all['面积'], df_all['总价'], color='blue', s=0.1, )  # 绘制散点
plt.plot(df_all['面积'], slope * df_all['面积'] + intercept, color='red', linewidth=1, label='y = {}x {} {}'.format(slope, '+' if intercept >= 0 else '-', intercept if intercept >= 0 else -intercept))  # 绘制直线
plt.xlabel('房屋面积/平方米')
plt.ylabel('房屋总价/万元')
plt.legend(loc='upper left')  # 图例位置
plt.title('北京市(部分城区)二手房面积和总价线性回归')
# plt.show()
plt.savefig(os.path.join('图表', '线性回归', '总.png'), bbox_inches='tight')
lines = lines.append({'城区': '总', 'slope': slope, 'intercept': intercept}, ignore_index=True)


通过绘制该图表,发现数据略为分散,结合初步分析,认为主要是由于各城区地理位置不同所造成的,遂绘制各城区图表

各城区面积和总价线性回归

接上

# 各城区
for each in ['东城', '西城', '海淀', '朝阳', '昌平', '大兴', '房山', '顺义', '通州']:
    plt.cla()  # 清空
    df = df_all[df_all['城区'] == each].reset_index().drop('index', axis=1)
    print(df)
    slope, intercept = fitSLR(df['面积'], df['总价'])  # 拟合,得到直线的斜率和截距
    plt.scatter(df['面积'], df['总价'], color='blue', s=0.1, )  # 绘制散点
    plt.plot(df['面积'], slope * df['面积'] + intercept, color='red', linewidth=1, label='y = {}x {} {}'.format(slope, '+' if intercept >= 0 else '-', intercept if intercept >= 0 else -intercept))  # 绘制直线
    plt.xlabel('房屋面积/平方米')
    plt.ylabel('房屋总价/万元')
    plt.legend(loc='upper left')  # 图例位置
    plt.title('北京市{}区二手房面积和总价线性回归'.format(each))
    # plt.show()
    plt.savefig(os.path.join('图表', '线性回归', '{}区.png'.format(each)), bbox_inches='tight')
    lines = lines.append({'城区': each, 'slope': slope, 'intercept': intercept}, ignore_index=True)

lines.to_csv(os.path.join('图表', '线性回归', 'lines.csv'))  # 保存直线数据

plt.cla()
for i in range(len(lines)):
    print(i)
    plt.plot(df_all['面积'], lines['slope'].iloc[i] * df_all['面积'] + lines['intercept'].iloc[i], linewidth=1, label=lines['城区'].iloc[i])
plt.xlabel('房屋面积/平方米')
plt.ylabel('房屋总价/万元')
plt.legend(loc='upper left')  # 图例位置
plt.title('北京市各城区二手房面积和总价线性回归'.format(each))
# plt.show()
plt.savefig(os.path.join('图表', '线性回归', '各城区.png'.format(each)), bbox_inches='tight')









通过观察可以发现该结果符合常识,各城区数据都更为集中,房屋单位价格受不同城区影响较大。
同时,笔者记录了所有直线,再绘制到同一图中对比。

可以明显观察到区别。

房屋单价的影响因素

通过观察及上述分析,房山区数据分布更为集中,便于分析。于是继续进行下一步分析。

房屋单价与房屋面积(总价)的关系

因面积和总价呈线性相关,故结论应该相近。

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os

# 为了显示中文,需要更换字体
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
# 如果你是macOSX,请使用以下
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


path = 'data.csv'  # 文件名
df = pd.read_csv(path, usecols=['单价', '面积', '总价', '城区', '建成年份', '建筑结构', '近地铁', '满二', '满五', '装修'])  # 读取文件
df = df[df['城区'] == '房山']  # 筛选出房山
df = df.dropna()  # 去除空数据


# 面积
plt.cla()
plt.scatter(df['面积'].astype(int), df['单价'], color='blue', s=0.1)  # 绘制散点
plt.xlabel('面积/平米')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与面积的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '面积.png'))


# 总价
plt.cla()
plt.scatter(df['总价'].astype(int), df['单价'], color='blue', s=0.1)  # 绘制散点
plt.xlabel('总价/万元')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与总价的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '总价.png'))



可见确实存在某种关系,但并均非线性关系。

房屋单价与建筑年限的关系

接上

# 建成年份
plt.cla()
df = df[df['建成年份'] != '']  # 去除空数据
df = df[df['建成年份'] != '暂无']  # 去除问题数据
df = df[df['建成年份'] != '板塔']  # 去除问题数据
df = df.reset_index().drop('index', axis=1)  # 重建索引
plt.scatter(df['建成年份'].astype(int), df['单价'], color='blue', s=0.1)  # 绘制散点
plt.xlabel('建成年份/年')
plt.ylabel('房屋单价/(元/平米)')
plt.legend(loc='upper left')  # 图例位置
plt.title('北京市房山区二手房单价与建成年份的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '建成年份.png'), bbox_inches='tight')


通过观察发现确实存在某种关系,但是并非线性关系。

房屋单价与建筑结构的关系

接上

# 建筑结构
plt.cla()
df = df[df['建筑结构'] != '']  # 去除空数据
df = df.reset_index().drop('index', axis=1)  # 重建索引
df.boxplot(by='建筑结构', column='单价')  # 绘制箱型图
plt.suptitle('')  # 隐藏默认的groupby文字
plt.xlabel('建筑结构')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与建筑结构的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '建筑结构.png'))


通过观察,发现建筑结构对单价的影响不大。

房屋单价与近地铁与否的关系

接上

# 近地铁
plt.cla()
df.boxplot(by='近地铁', column='单价')  # 绘制箱型图
plt.suptitle('')  # 隐藏默认的groupby文字
plt.xlabel('近地铁')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与近地铁与否的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '近地铁.png'))


发现与常识相符,确实近地铁的房源单价略高于一般房源。

房屋单价与房本满二或满五的关系

接上

# 房本已满年份
plt.cla()
df.boxplot(by=['满二', '满五'], column='单价', )  # 绘制箱型图
plt.suptitle('')  # 隐藏默认的groupby文字
plt.xlabel('房本已满年份(满二, 满五)')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与房本已满年份的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '房本已满年份.png'))

总所周知,房本满两年或满五年对税款影响较大,预测满五的单价更低,不满二的单价更高。

发现与预测相悖,满五年单价反而最低,而满二年单价略高于不满二年。猜测是由于新房导致单价高,老房单价低。

房屋单价与装修的关系

接上

# 装修
plt.cla()
df = df[df['装修'] != '']  # 去除空数据
df = df.reset_index().drop('index', axis=1)  # 重建索引
df.boxplot(by='装修', column='单价')  # 绘制箱型图
plt.suptitle('')  # 隐藏默认的groupby文字
plt.xlabel('装修')
plt.ylabel('房屋单价/(元/平米)')
plt.title('北京市房山区二手房单价与装修的关系')
# plt.show()
plt.savefig(os.path.join('图表', '影响因素', '装修.png'))


结论很有趣,毛坯略低于精装,但远高于简装。猜测是由于简装房还需要拆除费用,故单价更低。

房屋单价是否会影响关注人数和已发布日

依照常识,房屋单价越低,关注人数越多,已发布日越短,反之亦然。

房屋单价与已发布日的关系

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os

# 为了显示中文,需要更换字体
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
# 如果你是macOSX,请使用以下
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


path = 'data.csv'  # 文件名
df = pd.read_csv(path, usecols=['单价', '城区', '已发布日', '关注人数'])  # 读取文件
df = df[df['城区'] == '房山']  # 筛选出房山
df = df.dropna()  # 去除空数据


# 已发布日
plt.cla()
plt.scatter(df['单价'], df['已发布日'], color='blue', s=0.1)  # 绘制散点
plt.xlabel('房屋单价/(元/平米)')
plt.ylabel('已发布日')
plt.legend(loc='upper left')  # 图例位置
plt.title('北京市房山区二手房已发布日与单价的关系')
# plt.show()
plt.savefig(os.path.join('图表', '房屋单价影响', '已发布日.png'), bbox_inches='tight')

限于网页,一个月内精度为1天,一年内精度为30天,一年以上精度为360天。

可见并无明显关系

房屋单价与关注人数的关系

接上

# 关注人数
plt.cla()
plt.scatter(df['单价'], df['关注人数'], color='blue', s=0.1)  # 绘制散点
plt.xlabel('房屋单价/(元/平米)')
plt.ylabel('关注人数')
plt.legend(loc='upper left')  # 图例位置
plt.title('北京市房山区二手房关注人数与单价的关系')
# plt.show()
plt.savefig(os.path.join('图表', '房屋单价影响', '关注人数.png'), bbox_inches='tight')


难以发现存在的关系

总结

通过对于数据的分析,笔者进行了很多猜测,但是都不足以形成有效结论,此文的目的主要是为了给想进行相关方面研究的人提供研究方向和思路。


文章作者: BoyInTheSun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 BoyInTheSun !
 上一篇
绝对路径VS相对路径 绝对路径VS相对路径
什么是路径路径在计算机中一般指一个文件或者目录的位置。绝对路径的起点大抵分为两类,一类是Windows所使用的基于盘符,以X:\为起点,逻辑上的盘符对应物理的磁盘,更符合普通人的思维方式。另一类是以Linux和UNIX为代表(包括MacOS
2020-05-28
下一篇 
matplotlib实例————线性回归计算房屋面积和总价关系 matplotlib实例————线性回归计算房屋面积和总价关系
目标利用matplotlib(v3.2),在链家二手房平台爬取二手房信息,并使用最小二乘法进行线性回归模型建模。 求解爬取并处理数据参见 https://boyinthesun.cn/post/python-scrapy2/ 绘制图表详见注
2020-05-18
  目录