python链家二手房分析

感觉比上一篇用户行为分析https://www.jianshu.com/p/8e692c9ed702稍微简单一点,用到的groupby分组函数偏多,算法较少,不过有很多细节值得考量,比如我用得不够多的str.extract():用正则从字符数据中抽取匹配的数据,只返回第一个匹配的数据。为什么分组后要用reset_index重置索引?为什么有的地方用的是value_counts有的地方用的是count?还有涉及到布尔数组取值,字典在DataFrame中的应用,缺失值处理很等多基础知识,还是把分析思路和代码记录一下。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']#显示中文

path = r'D:\python_data_file\\链家二手房.csv'
data = pd.read_csv(path)
data.head()
#中文字符的列名在处理的时候比较麻烦,将其转化为英文字符的列名,并查看数据显示前五行
data.rename(columns = {'小区名称':'village','户型':'house_type',
                     '面积':'area','区域':'district','朝向':'orientations','价格(W)':'total_price',
                     '单价(平方米)':'unit_price','建筑时间':'construction_time','楼层':'floor'},inplace = True)
data.head()
#查看数据类型
data.dtypes
village               object
house_type            object
area                 float64
district              object
floor                 object
orientations          object
total_price            int64
unit_price             int64
construction_time     object
dtype: object
#查看数据信息
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 28201 entries, 0 to 28200
Data columns (total 9 columns):
village              28201 non-null object
house_type           28201 non-null object
area                 28201 non-null float64
district             28201 non-null object
floor                28201 non-null object
orientations         26846 non-null object
total_price          28201 non-null int64
unit_price           28201 non-null int64
construction_time    21985 non-null object
dtypes: float64(1), int64(2), object(6)
memory usage: 1.9+ MB
data.isnull().sum()#查看数据缺失情况
village                 0
house_type              0
area                    0
district                0
floor                   0
orientations         1355
total_price             0
unit_price              0
construction_time    6216
dtype: int64

可以看到orientations字段缺失行数为1355,construction_time字段缺失行数为6216,这些缺失值在下面对其进行分析时再加以处理

#参数expand,取True时,会把切割出来的内容各自为列。 如果不需要pandas为你分好列,expand=False就可以了。
# split是分隔符;concat两表的联结,axis = 1按列维度合并
data1 = pd.concat([data,data['floor'].str.split('/',expand=True)],axis = 1)
data1.head()
# 删除floor列,重命名新得到的列
del data1['floor']
data1.rename(columns={0:'floor_level',1:'floor'},inplace=True)
data1.tail()
# 数据清洗:提取construction_time和floor字段中的数字、删除文字部分,查看前5行数据
data1['construction_time'] = data1['construction_time'].str.extract('(\d+)年建')
data1['floor'] = data1['floor'].str.extract('(\d+)层')
data1.head()
#construction_time和floor是字符串的格式,转化为浮点数的格式
data1['construction_time'] = data1['construction_time'].astype(float)
data1['floor'] = data1['floor'].astype(float)
data1.dtypes
village               object
house_type            object
area                 float64
district              object
orientations          object
total_price            int64
unit_price             int64
construction_time    float64
floor_level           object
floor                float64
dtype: object
data1.describe()#描述性统计

可以发现
1.面积平均值为96㎡,面积中位数为85㎡,说明少数大面积的房源拉高了的总体平均水平
2.总价平均值为580万,总价中位数为430万,情况与上面一样,少数总价高的房源拉高了总体的平均水平
3.单价平均数为59725元,单价中位数为58068元,平均值与中位数基本相等,说明房价升高趋势比较线性

数据分析:各城区房源数量分布【找一张上海地区结构图】

上海市区共分为三个等级:
1、黄浦区、长宁区;
2、静安区、徐汇区;
3、杨浦区、虹口区、普陀区。

上海郊区共分为三个等级:
1、宝山区、闵行区;
2、松江区、嘉定区、青浦区;
3、奉贤区、崇明区、金山区。

#按地区计数,重置索引,按数量降序排序
district_number = data1['district'].value_counts().reset_index().sort_values(by='district',ascending=False)
district_number
#折线图可视化各区域房源数量
plt.figure(figsize=(9,4))
district_number.plot(x = 'index',y = 'district',kind = 'line')
plt.title('各城区房源数量')
#柱状图可视化各区域房源数量
plt.figure(figsize=(9,4))
plt.bar(district_number['index'],district_number['district'])
plt.title('各城区房源数量')

由以上柱状图分析可知,除了静安区、金山区和崇明区房源数量不到50以外,其他14个城区二手房数量均在1300套以上,数量最多的为浦东区,共有2599套

各城区总面积和平均面积的分布

3.1各城区总面积分布
因为静安、金山、崇明区的房源较少无法代表整体,所以在这里删除这三个区的数据

data1.district.drop_duplicates()#从原始数据中拿到所有区域的名字(就是把重复的列名去除后)
#使用unique的和后面要起的作用其实是一样的 data1.district.unique()
dis_list = list(data1.district.drop_duplicates())
dis_list.remove('崇明')
dis_list.remove('金山')
dis_list.remove('静安')
dis_list
['浦东',
 '闵行',
 '宝山',
 '徐汇',
 '普陀',
 '杨浦',
 '长宁',
 '松江',
 '嘉定',
 '黄浦',
 '闸北',
 '虹口',
 '奉贤',
 '青浦']
#删除上述的三个区,并查看删除后的所有数据前五行
data2 = data1[data1.district.isin(dis_list)]
data2.head()
#计算各城区二手房源总面积
#按区域分组对所有指标求和,按求和总面积大小降序排序,最后重置索引
group_total_area = data2.groupby('district').sum().sort_values(by='area',ascending=False).reset_index()
group_total_area
#各城区总面积折线图
plt.figure(figsize=(9,4))
group_total_area.plot(x='district',y='area',kind='line')
plt.title('各城区总面积折线图')

由上图可知,总面积排名前三位的分别是:青浦、松江和浦东,这得益于它们较大的面积

#3.2各城区平均面积分布
#按城区分组对面积求平均值,重置索引,对面积的均值按降序排排序
group_area= data2.groupby('district').area.mean().reset_index().sort_values(by='area',ascending = False)
group_area
#画出各城区平均面积折线图
plt.figure(figsize=(15,7))
group_area.plot(x='district',y='area',kind = 'line')
plt.title('各城区平均面积折线图')
#或者等价于以下方法
plt.figure(figsize=(8,4))
plt.plot(group_area.district,group_area.area)
plt.title('各城区平均面积折线图')

这两种画图方法,可能后者更详细一点
以上折线图显示,青浦区总面积和平均面积远远大于其他城区平均面积,青浦区共有房源1769套,数量较大,不太可能出现异常,故分析可能原因为:不在中心位置,建设成本较低,房屋设计面积较大。
各城区总面积和平均面积趋势大致一样,各城区平均面积在[77,116]之间,其中,面积在[100,116]之间的为松江、黄埔和金山,面积在[90,100)之间的为奉贤、长宁、闵行和静安,面积在[80,90)之间的为嘉定、虹口、崇明、浦东、闸北、宝山、普陀和徐汇,杨浦区平均面积为77.8㎡。

3.3全市平均面积分布
以区间[0,50)、[50,100)、[100,150)、[150,200)、[200,+∞)为划分标准,将面积划分为tinysmall、small、medium、big、huge五个等级,分别对应极小户型、小户型、中等户型、大户型和巨大户型。

data2.loc[(data2['area'] >= 0) & (data2['area'] < 50),'area_level'] = 'tinysmall'
data2.loc[(data2['area'] >= 50) & (data2['area'] < 100),'area_level'] = 'small'
data2.loc[(data2['area'] >= 100) & (data2['area'] < 150),'area_level'] = 'medium'
data2.loc[(data2['area'] >= 150) & (data2['area'] < 200),'area_level'] = 'big'
data2.loc[(data2['area'] >= 200),'area_level'] = 'huge'
data2.head()
#求出各户型的数量
#这里千万不能用value_counts,还有total_price列不是指价格,而是该种户型的数量,最后按惯例把索引解放出来
grouped_area_level = data2.groupby('area_level').total_price.count().reset_index()
grouped_area_level##如果不reset_index重置索引,返回的会是一个Series
plt.bar(grouped_area_level.area_level,grouped_area_level.total_price)
plt.title('各户型数量分布柱状图')

由上图可知:
1.平均面积为small的房源数量占总数的50%,即一半房源的面积在区间[50,100]㎡,
2.平均面积为medium的房源数量占总数的25%,即四分之一的房源面积在区间[100,150]㎡,
3.平均面积为tinysmall的房源数量占总数的17%,即17%的房源面积在区间[0,50]㎡

4、数据分析:各城区总价和单价分布

4.1总价分布

list(data2.groupby('district').total_price)
#把分区后的总价组成一个字典,以便下面将它转化为dataframe
dictionary = dict(list(data2.groupby('district').total_price))
dict_data = pd.DataFrame(dictionary)
dict_data
# 作出总价箱线图
plt.figure(figsize=(12,5))
dict_data.boxplot()
plt.ylim(0,3000)
plt.title('各城区总价箱线图')
#各城区总价平均值排名
#这里未指定默认升序排序
total_price_mean = data2.groupby('district').total_price.mean().reset_index().sort_values(by='total_price')
total_price_mean
image.png

由以上结果可知
总价No.1的为黄浦区,平均值为1059.3万,总价区间跨越大,总体价格价格区间为[500,1300]万,中位数小于平均值,故少数价格高的拉高总体平均值的现象比较严重
总价No.2的为长宁区,平均值为760.8万,总体价格区间为[400,1000]万,中位数接近平均值,总价上升比较线性
总价No.3的为徐汇区,平均值为671.8万,总体价格区间为[400,800]万,中位数小于平均值,故少数价格高的拉高了总体平均值
总价No.4的为青浦区,平均值为676.0万,总价价格区间为[300,750]万,中位数远小于平均值,故少数价格高的拉高总体平均值的现象比较严重

total_price_mean.total_price.hist(bins = 20)
plt.title('平均总价分布图')
data2.total_price.hist(bins=10)
plt.title('总价分布图')

房屋总价是一个左凸的直方图,90%的数据集中在[500,1000]的区间,即90%的房源总价在500万到1000万之间,只有大概10%的房源属于极高的价格,这个结果符合现实的房价分布

#各城区单价分布
good1=dict(list(data2.groupby('district')['unit_price']))
good1df=pd.DataFrame(good1)
plt.figure(figsize=(12,5))
good1df.boxplot()
plt.ylim(0,150000)
plt.title('各城区单价分布箱线图')
#各城区单价排名
unit_price_mean = data2.groupby('district')['district','unit_price'].mean().reset_index().sort_values('unit_price',ascending = False)
unit_price_mean

单价No.1的是黄浦,平均值为90666元,大体价格区间为[78000,100000]元
单价No.2的是徐汇,平均值为79157元,大体价格区间为[69111,90000]元
单价No.3的是长宁,平均值为75081元,大体价格区间为[66000,84000]元

#全市单价分布
data2.unit_price.hist(bins = 20)
plt.title('全市单价分布直方图')#接近正态分布,该图显示70%的单价集中在区间[30000,90000]元,符合房价的现实分布规律
# 4.4、总价和单价排名前十小区
# 对小区进行分组,计算出各小区房源数量,并按照房源数量降序的顺序排序
data3 = data2.groupby('village').total_price.count().reset_index().sort_values('total_price',ascending = False)
data3.head()
# 因为很多小区房源数量太少,其统计值不具有代表性,故在此过滤掉房源数量小于20的小区
data4 = data3[data3.total_price > 20]
data4.head()
# 提取房源数大于20的全部数据
data5 = data2[data2.village.isin(data4.village)]
data5.head()
# 4.5、计算各小区总价排名前15的小区:
# 对小区进行分组,计算出各小区房源数量,并按照房源数量降序的顺序排序
data3 = data2.groupby('village').total_price.count().reset_index().sort_values('total_price',ascending = False)
data3.head(20)#这个自己运行

。。。

5.1、房价与户型的关系

按户型进行分组,计算出每个户型的房源数量,过滤掉房源数量小于100的户型
grouped_house_type = data2.groupby('house_type').total_price.count().sort_values(ascending = False).reset_index()
grouped_house_type2 = grouped_house_type.loc[grouped_house_type.total_price> 100]
grouped_house_type2
#可视化:作出各户型的数量柱状图
plt.figure(figsize = (10,5))
plt.bar(grouped_house_type2.house_type,grouped_house_type2.total_price)
plt.title('各户型数量柱状图')
# 户型与房屋均价的柱状图
grouped_house_type3 = data2.groupby('house_type').unit_price.mean().sort_values(ascending = False).reset_index()
grouped_house_type3
plt.figure(figsize = (10,5))
plt.bar(grouped_house_type3.house_type,grouped_house_type3.unit_price)
plt.title('各户型数量柱状图')

由上图可知,单价排名前三的户型分别为9室3厅,9室9厅,9室2厅

5.2、房价与楼层的关系
计算各个楼层等级的房源数量,注意这里是按照地区高中中区划分的,并不是按照floor划分的

  a_list=['高区','中区','低区']
data7 = data2[data2.floor_level.isin(a_list)]
grouped_floor_level = data7.groupby('floor_level').unit_price.count().reset_index()
grouped_floor_level.head()
floor_level     unit_price
0   中区  8852
1   低区  8372
2   高区  10163
# 可视化:并作出其柱状图
plt.title('楼层等级-数量')
plt.bar(grouped_floor_level.floor_level,grouped_floor_level.unit_price)

由上图可知,低、中、高区的房源数量基本相等,都在8000套以上,其中高区最多,数量超过10000套

# 计算各楼层等级的平均单价
grouped_floor_level1 = data7.groupby('floor_level').unit_price.mean().reset_index()
grouped_floor_level1
    floor_level     unit_price
0   中区  60236.249661
1   低区  60989.035834
2   高区  58958.393486
#画出柱状图
plt.title('楼层等级—平均单价')
plt.bar(grouped_floor_level1.floor_level,grouped_floor_level1.unit_price)
# 计算各楼层等级的平均总价
grouped_floor_level2 = data7.groupby('floor_level').total_price.mean().reset_index()
grouped_floor_level2
    floor_level     total_price
0   中区  554.796543
1   低区  590.397396
2   高区  525.550723
#画出柱状图
plt.title('楼层等级—平均总价')
plt.bar(grouped_floor_level2.floor_level,grouped_floor_level2.total_price)

由上图可知,低、中、高区楼层平均总价均超过500万,其中低区总价最高,为590万

#查看缺失值
data2.isnull().sum()
village                 0
house_type              0
area                    0
district                0
orientations         1350
total_price             0
unit_price              0
construction_time    6191
floor_level             0
floor                 755
area_level              0
dtype: int64
#缺失值的处理
# 填充floor列中的空值为999
data2.floor=data2.floor.fillna('999')
# 找出floor列中值为999的行号
c_list=data2[data2.floor=='999'].index.tolist()
# 删除上面查找到的行
data8=data2.drop(c_list)
data8.isnull().sum()
village                 0
house_type              0
area                    0
district                0
orientations         1251
total_price             0
unit_price              0
construction_time    5956
floor_level             0
floor                   0
area_level              0
dtype: int64

由上可知,floor字段中空值所在的行已被全部删除

# 计算楼层平均单价排名前15位
data2.groupby('floor').unit_price.mean().sort_values(ascending = False).reset_index().head(15)
# 计算各楼层单价平均值,并作出其折线图:
plt.figure(figsize=(15,5))
data2.groupby('floor').unit_price.mean().plot()
plt.title('楼层数-单价')

由上图分析可知,随着楼层输的增加,房价也随之升高。在37层之前,房价随楼层数增加而增加的趋势比较线性,但是在这之后房价随楼层数增加而增加的趋势波动很大,可能的原因是楼层数超过40的住宅数量较小,超过40层的数据量比较小,容易产生误差
5.3、房价与朝向的关系
查看房屋的朝向

data2.orientations.drop_duplicates().values.tolist()
['朝南',
 '朝南北',
 nan,
 '朝西',
 '朝东北',
 '朝西南',
 '朝东南',
 '朝东',
 '朝',
 '朝西北',
 '朝北',
 '朝东西',
 '(进门) 北',
 '(进门) 南',
 '(进门) 南北',
 '(进门) 东',
 '(进门) 西',
 '(进门)',
 '(进门) 东西',
 '(进门) 东南',
 '(进门) 西南',
 '(进门) 西北']
# 朝向重新构成一个列表
b_list=list(['朝东','朝东北','朝东南','朝东西','朝北','朝南','朝南北','朝西','朝西北','朝西南'])
data8 = data2[data2.orientations.isin(b_list)]
grouped_orientations = data8.groupby('orientations').unit_price.mean().sort_values(ascending = False).reset_index()
grouped_orientations
#可视化:作出其柱状图
plt.figure(figsize=(12,5))
plt.bar(grouped_orientations.orientations,grouped_orientations.unit_price)
plt.title('朝向—单价')

由上图可知,朝向对房价有一定的影响,朝向南面(南、东南、西南)的房子总体价格会高一点。这一现象也符合生活情景,朝向南面的房屋采光会比较好,这些南北通透的房子单价相对于其他朝向会高一些。

5.4、房价与建筑年代的关系

#还是对data2缺失值的处理
# 填充construction_time字段中的空值为000
data2.construction_time=data2.construction_time.fillna('000')
# 找出construction_time字段中值为123的行号
d_list=data2[data2.construction_time=='000'].index.tolist()
# 删除construction_time列中的空值
data9=data2.drop(d_list)
data9
data9.isnull().sum()
village                 0
house_type              0
area                    0
district                0
orientations         1082
total_price             0
unit_price              0
construction_time       0
floor_level             0
floor                   0
area_level              0
dtype: int64
#计算各个年份建造房屋的数量 ,作出其折线图:
plt.figure(figsize=(15,5))
data9.groupby('construction_time').unit_price.count().plot()
plt.title('建筑时间-数量')

以上折线图呈右侧双峰形态,即分别在1995年和2005年左右出现了两个房屋修建的高峰期,这期间房屋数量陡增

# 计算各个年份平均总价,作出其折线图
plt.figure(figsize=(15,5))
data9.groupby('construction_time').total_price.mean().plot()
plt.title('建筑时间-平均总价图')
#计算各个年份平均单价,作出其折线图
plt.figure(figsize=(15,5))
data9.groupby('construction_time').unit_price.mean().plot()
plt.title('建筑时间-平均单价图')

由以上分析可知,
以上平均总价与建筑时间的趋势和平均单价与建筑时间的趋势大致一致,都呈现左侧单峰形态。在1920年到1950年间建造的房屋,其总价和单价均高于之后建造的房屋,分析其原因为:在1920年到1950年间建造的房屋,因为建造时间早,其建造的位置处于现在上海的核心地带,因为其土地的稀有性,故而导致了现在的高房价。
从2015年以来修建的房屋呈现陡然上升的趋势,这也和目前房价不断升高的趋势契合,这表明房价在短时间内不会出现大的下跌,是否会出现下一个房价高峰,我们拭目以待。

参考https://www.jianshu.com/p/372c6468c18e

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342