seaborn

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


1.1 Seaborn----绘制统计图形

Matplotlib虽然已经是比较优秀的绘图库了,但是它有个今人头疼的问题,那就是API使用过于复杂,它里面有上千个函数和参数,属于典型的那种可以用它做任何事,却无从下手。

Seaborn基于 Matplotlib核心库进行了更高级的API封装,可以轻松地画出更漂亮的图形,而Seaborn的漂亮主要体现在配色更加舒服,以及图形元素的样式更加细腻。

不过,使用Seaborn绘制图表之前,需要安装和导入绘图的接口,具体代码如下:

# 安装 
pip3 install seaborn
# 导入
import seaborn as sns

接下来,我们正式进入 Seaborn库的学习

1 可视化数据的分布

当处理一组数据时,通常先要做的就是了解变量是如何分布的。

  • 对于单变量的数据来说 采用直方图或核密度曲线是个不错的选择,
  • 对于双变量来说,可采用多面板图形展现,比如 散点图、二维直方图、核密度估计图形等。

针对这种情况, Seaborn库提供了对单变量和双变 量分布的绘制函数,如 displot()函数、 jointplot()函数,下面来介绍这些函数的使用,具体内容如下:

2 绘制单变量分布

可以采用最简单的直方图描述单变量的分布情况。 Seaborn中提供了 distplot()函数,它默认绘制的是一个带有核密度估计曲线的直方图。 distplot()函数的语法格式如下。

seaborn.distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, color=None)

上述函数中常用参数的含义如下:

  • (1) a:表示要观察的数据,可以是 Series、一维数组或列表。

  • (2) bins:用于控制条形的数量。

  • (3) hist:接收布尔类型,表示是否绘制(标注)直方图。

  • (4) kde:接收布尔类型,表示是否绘制高斯核密度估计曲线。

  • (5) rug:接收布尔类型,表示是否在支持的轴方向上绘制rugplot。

通过 distplot())函数绘制直方图的示例如下。

import numpy as np

sns.set()
np.random.seed(0)  # 确定随机数生成器的种子,如果不使用每次生成图形不一样
arr = np.random.randn(100)  # 生成随机数组

ax = sns.distplot(arr, bins=10, hist=True, kde=True, rug=True)  # 绘制直方图

上述示例中,首先导入了用于生成数组的numpy库,然后使用 seaborn调用set()函数获取默认绘图,并且调用 random模块的seed函数确定随机数生成器的种子,保证每次产生的随机数是一样的,接着调用 randn()函数生成包含100个随机数的数组,最后调用 distplot()函数绘制直方图。

运行结果如下图所示。

从上图中看出:

  • 直方图共有10个条柱,每个条柱的颜色为蓝色,并且有核密度估计曲线。
  • 根据条柱的高度可知,位于-1-1区间的随机数值偏多,小于-2的随机数值偏少。

通常,采用直方图可以比较直观地展现样本数据的分布情况,不过,直方图存在一些问题,它会因为条柱数量的不同导致直方图的效果有很大的差异。为了解决这个问题,可以绘制核密度估计曲线进行展现。

  • 核密度估计是在概率论中用来估计未知的密度函数,属于非参数检验方法之一,可以比较直观地看出数据样本本身的分布特征。

通过 distplot()函数绘制核密度估计曲线的示例如下。

# 创建包含500个位于[0,100]之间整数的随机数组
array_random = np.random.randint(0, 100, 500)
# 绘制核密度估计曲线
sns.distplot(array_random, hist=False, rug=True)

上述示例中,首先通过 random.randint()函数返回一个最小值不低于0、最大值低于100的500个随机整数数组然后调用 displot()函数绘制核密度估计曲线。

运行结果如图所示。

从上图中看出,图表中有一条核密度估计曲线,并且在x轴的上方生成了观测数值的小细条。

3 绘制双变量分布

两个变量的二元分布可视化也很有用。在 Seaborn中最简单的方法是使用 jointplot()函数,该函数可以创建一个多面板图形,比如散点图、二维直方图、核密度估计等,以显示两个变量之间的双变量关系及每个变量在单坐标轴上的单变量分布。

jointplot()函数的语法格式如下。

seaborn.jointplot(x, y, data=None, 
                  kind='scatter', stat_func=None, color=None, 
                  ratio=5, space=0.2, dropna=True)

上述函数中常用参数的含义如下:

  • (1) kind:表示绘制图形的类型。
  • (2) stat_func:用于计算有关关系的统计量并标注图。
  • (3) color:表示绘图元素的颜色。
  • (4) size:用于设置图的大小(正方形)。
  • (5) ratio:表示中心图与侧边图的比例。该参数的值越大,则中心图的占比会越大。
  • (6) space:用于设置中心图与侧边图的间隔大小。

下面以散点图、二维直方图、核密度估计曲线为例,为大家介绍如何使用 Seaborn绘制这些图形。

3.1 绘制散点图

调用 seaborn.jointplot()函数绘制散点图的示例如下。

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

# 创建DataFrame对象
dataframe_obj = pd.DataFrame({"x": np.random.randn(500),"y": np.random.randn(500)})
# 绘制散布图
sns.jointplot(x="x", y="y", data=dataframe_obj)

上述示例中,首先创建了一个 DataFrame对象 dataframe_obj作为散点图的数据,其中x轴和y轴的数据均为500个随机数,接着调用 jointplot0函数绘制一个散点图,散点图x轴的名称为“x”,y轴的名称为“y”。

运行结果如图所示。

3.2 绘制二维直方图

二维直方图类似于“六边形”图,主要是因为它显示了落在六角形区域内的观察值的计数,适用于较大的数据集。当调用 jointplot()函数时,只要传入kind="hex",就可以绘制二维直方图,具体示例代码如下。

# 绘制二维直方图
sns.jointplot(x="x", y="y", data=dataframe_obj, kind="hex")

运行结果如图所示。

从六边形颜色的深浅,可以观察到数据密集的程度,另外,图形的上方和右侧仍然给出了直方图。注意,在绘制二维直方图时,最好使用白色背景。

3.3 绘制核密度估计图形

利用核密度估计同样可以查看二元分布,其用等高线图来表示。当调用jointplot()函数时只要传入ind="kde",就可以绘制核密度估计图形,具体示例代码如下。

sns.jointplot(x="x", y="y", data=dataframe_obj, kind="kde")

上述示例中,绘制了核密度的等高线图,另外,在图形的上方和右侧给出了核密度曲线图。

运行结果如图所示。

通过观等高线的颜色深浅,可以看出哪个范围的数值分布的最多,哪个范围的数值分布的最少

4 绘制成对的双变量分布

要想在数据集中绘制多个成对的双变量分布,则可以使用pairplot()函数实现,该函数会创建一个坐标轴矩阵,并且显示Datafram对象中每对变量的关系。另外,pairplot()函数也可以绘制每个变量在对角轴上的单变量分布。

接下来,通过 sns.pairplot()函数绘制数据集变量间关系的图形,示例代码如下

# 加载seaborn中的数据集
dataset = sns.load_dataset("iris")

dataset.head()

上述示例中,通过 load_dataset0函数加载了seaborn中内置的数据集,根据iris数据集绘制多个双变量分布。

# 绘制多个成对的双变量分布
sns.pairplot(dataset)

结果如下图所示。

5 小结

  • seaborn的基本使用【了解】
  • 绘制单变量分布图形【知道】
    • seaborn.distplot()
  • 绘制双变量分布图形【知道】
    • seaborn.jointplot()
  • 绘制成对的双变量分布图形【知道】
    • Seaborn.pairplot()

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

绘制单变量分布

In [13]:

np.random.seed(0)  # 确定随机数生成器生成的数据是一样的

arr = np.random.randn(100)

sns.distplot(arr, bins=10, hist=True, kde=True, rug=True)

 

绘制双变量分布

绘制散点图

In [16]:

df = pd.DataFrame({"x":np.random.randn(500), "y":np.random.randn(500)})

In [18]:

df.head()

sns.jointplot("x", "y", data=df, kind="scatter", stat_func=None, color="r", size=10, ratio=5, space=1)

绘制二维直方图

In [35]:

sns.jointplot("x", "y", data=df, kind="hex")

绘制核密度估计图形

In [36]:

sns.jointplot("x", "y", data=df, kind="kde")

绘制成对的双变量分布

In [37]:

dataset = sns.load_dataset("iris")

dataset.head()

sns.pairplot(dataset)

类别散点图

In [41]:

data = sns.load_dataset("tips")

In [42]:

data.head()

sns.stripplot(x="day", y="total_bill", data=data, hue="time")

sns.stripplot(x="day", y="total_bill", data=data, hue="time", jitter=True)

sns.swarmplot("day", "total_bill", data=data)

类别内数据分布

箱型图

In [57]:

sns.boxplot("day", "total_bill", data=data, hue="time", palette=["g", "r"], saturation=0.9)

绘制提琴图

In [58]:

sns.violinplot("day", "total_bill", data=data)

类别内的估计统计

In [59]:

sns.barplot("day", "total_bill", data=data)

sns.pointplot("day", "total_bill", data=data)


1.2 用分类数据绘图

数据集中的数据类型有很多种,除了连续的特征变量之外,最常见的就是类别型的数据了,比如人的性别、学历、爱好等,这些数据类型都不能用连续的变量来表示,而是用分类的数据来表示。

Seaborn针对分类数据提供了专门的可视化函数,这些函数大致可以分为如下三种:

  • 分类数据散点图: swarmplot()与 stripplot()。
  • 类数据的分布图: boxplot() 与 violinplot()。
  • 分类数据的统计估算图:barplot() 与 pointplot()。

下面两节将针对分类数据可绘制的图形进行简单介绍,具体内容如下

1 类别散点图

通过 stripplot()函数可以画一个散点图, stripplot0函数的语法格式如下。

seaborn.stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, jitter=False)

上述函数中常用参数的含义如下

  • (1) x,y,hue:用于绘制长格式数据的输入。
  • (2) data:用于绘制的数据集。如果x和y不存在,则它将作为宽格式,否则将作为长格式。
  • (3) jitter:表示抖动的程度(仅沿类別轴)。当很多数据点重叠时,可以指定抖动的数量或者设为Tue使用默认值。

为了让大家更好地理解,接下来,通过 stripplot()函数绘制一个散点图,示例代码如下。

# 获取tips数据
tips = sns.load_dataset("tips")

sns.stripplot(x="day", y="total_bill", data=tips)

运行结果如下图所示。

从上图中可以看出,图表中的横坐标是分类的数据,而且一些数据点会互相重叠,不易于观察。为了解决这个问题,可以在调用striplot()函数时传入jitter参数,以调整横坐标的位置,改后的示例代码如下。

sns.stripplot(x="day", y="total_bill", data=tips, jitter=True)

运行结果如下图所示。

除此之外,还可调用 swarmplot0函数绘制散点图,该函数的好处是所有的数据点都不会重叠,可以很清晰地观察到数据的分布情况,示例代码如下。

sns.swarmplot(x="day", y="total_bill", data=tips)

运行结果如图所示。

2 类别内的数据分布

要想查看各个分类中的数据分布,显而易见,散点图是不满足需求的,原因是它不够直观。针对这种情况,我们可以绘制如下两种图形进行查看:

  • 箱形图:
    • 箱形图(Box-plot)又称为盒须图、盒式图或箱线图,是一种用作显示一组数据分散情况资料的统计图。因形状如箱子而得名。
    • 箱形图于1977年由美国著名统计学家约翰·图基(John Tukey)发明。它能显示出一组数据的最大值、最小值、中位数、及上下四分位数。

小提琴图:

  • 小提琴图 (Violin Plot) 用于显示数据分布及其概率密度。
  • 这种图表结合了箱形图和密度图的特征,主要用来显示数据的分布形状。
  • 中间的黑色粗条表示四分位数范围,从其延伸的幼细黑线代表 95% 置信区间,而白点则为中位数。
  • 箱形图在数据显示方面受到限制,简单的设计往往隐藏了有关数据分布的重要细节。例如使用箱形图时,我们不能了解数据分布。虽然小提琴图可以显示更多详情,但它们也可能包含较多干扰信息。

接下来,针对 Seaborn库中箱形图和提琴图的绘制进行简单的介绍。

2.1 绘制箱形图

seaborn中用于绘制箱形图的函数为 boxplot(),其语法格式如下:

seaborn.boxplot(x=None, y=None, hue=None, data=None, orient=None, color=None,  saturation=0.75, width=0.8)

常用参数的含义如下:

  • (1) palette:用于设置不同级别色相的颜色变量。---- palette=["r","g","b","y"]
  • (2) saturation:用于设置数据显示的颜色饱和度。---- 使用小数表示

使用 boxplot()函数绘制箱形图的具体示例如下。

sns.boxplot(x="day", y="total_bill", data=tips)

上述示例中,使用 seaborn中内置的数据集tips绘制了一个箱形图,图中x轴的名称为day,其刻度范围是 Thur~Sun(周四至周日),y轴的名称为 total_bill,刻度范围为10-50左右

运行结果如图所示。

从图中可以看出,

  • Thur列大部分数据都小于30,不过有5个大于30的异常值,
  • Fri列中大部分数据都小于30,只有一个异常值大于40,
  • Sat一列中有3个大于40的异常值,
  • Sun列中有两个大于40的异常值

2.2 绘制提琴图

seaborn中用于绘制提琴图的函数为violinplot(),其语法格式如下

seaborn.violinplot(x=None, y=None, hue=None, data=None)

通过violinplot()函数绘制提琴图的示例代码如下

sns.violinplot(x="day", y="total_bill", data=tips)

上述示例中,使用seaborn中内置的数据集绘制了一个提琴图,图中x轴的名称为day,y轴的名称为total_bill

运行结果如图所示。

从图中可以看出,

  • Thur一列中位于5~25之间的数值较多,
  • Fri列中位于5-30之间的较多,
  • Sat-列中位于5-35之间的数值较多,
  • Sun一列中位于5-40之间的数值较多。

3 类别内的统计估计

要想查看每个分类的集中趋势,则可以使用条形图和点图进行展示。 Seaborn库中用于绘制这两种图表的具体函数如下

  • barplot()函数:绘制条形图。
  • pointplot()函数:绘制点图。

这些函数的API与上面那些函数都是一样的,这里只讲解函数的应用,不再过多对函数的语法进行讲解了。

3.1 绘制条形图

最常用的查看集中趋势的图形就是条形图。默认情况下, barplot函数会在整个数据集上使用均值进行估计。若每个类别中有多个类别时(使用了hue参数),则条形图可以使用引导来计算估计的置信区间(是指由样本统计量所构造的总体参数的估计区间),并使用误差条来表示置信区间。

使用 barplot()函数的示例如下

sns.barplot(x="day", y="total_bill", data=tips)

运行结果如图所示。

3.2 绘制点图

另外一种用于估计的图形是点图,可以调用 pointplot()函数进行绘制,该函数会用高度低计值对数据进行描述,而不是显示完整的条形,它只会绘制点估计和置信区间

通过 pointplot()函数绘制点图的示例如下。

sns.pointplot(x="day", y="total_bill", data=tips)

运行结果如图所示。

4 小结

  • 类别散点图
    • seaborn.stripplot()
  • 类别内的数据分布
    • 箱线图
      • seaborn.boxplot()
    • 小提琴图
      • seaborn.violinplot()
  • 类别内的统计估计
    • 条形图
      • barplot()
    • 点图
      • pointplot()

1.3 案例:NBA球员数据分析

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

获取数据

In [2]:

data = pd.read_csv("./data/nba_2017_nba_players_with_salary.csv")

In [3]:

data.head()

data.shape

Out[4]:

(342, 38)

In [5]:

data.describe()

数据分析

数据相关性

In [7]:

data_cor = data.loc[:, ['RPM', 'AGE', 'SALARY_MILLIONS', 'ORB', 'DRB', 'TRB',
                       'AST', 'STL', 'BLK', 'TOV', 'PF', 'POINTS', 'GP', 'MPG', 'ORPM', 'DRPM']]

data_cor.head()

corr = data_cor.corr()

In [10]:

# 获取两列数据之间的相关性
corr.head()

plt.figure(figsize=(20, 8), dpi=100)

sns.heatmap(corr, square=True, linewidths=0.1, annot=True)

基本数据排名分析

In [19]:

# 按照效率值排名

data.loc[:, ["PLAYER", "RPM", "AGE"]].sort_values(by="RPM", ascending=False).head()

# 按照球员薪资排名

data.loc[:, ["PLAYER", "RPM", "AGE", "SALARY_MILLIONS"]].sort_values(by="SALARY_MILLIONS", ascending=False).head()

Seaborn常用的三个数据可视化方法

单变量:

In [24]:

# 利用seaborn中的distplot绘图来分别看一下球员薪水、效率值、年龄这三个信息的分布情况

sns.set_style("darkgrid")

plt.figure(figsize=(10, 10))

plt.subplot(3, 1, 1)
sns.distplot(data["SALARY_MILLIONS"])
plt.ylabel("salary")

plt.subplot(3, 1, 2)
sns.distplot(data["RPM"])
plt.ylabel("RPM")

plt.subplot(3, 1, 3)
sns.distplot(data["AGE"])
plt.ylabel("AGE")

双变量

In [26]:

sns.jointplot(data.AGE, data.SALARY_MILLIONS, kind="hex")

多变量

In [27]:

multi_data = data.loc[:, ['RPM','SALARY_MILLIONS','AGE','POINTS']]
multi_data.head()

 

sns.pairplot(multi_data)

衍生变量的一些可视化实践-以年龄为例

In [29]:

def age_cut(df):
    """年龄划分"""
    if df.AGE <= 24:
        return "young"
    elif df.AGE >= 30:
        return "old"
    else:
        return "best"

In [30]:

# 使用apply对年龄进行划分
data["age_cut"] = data.apply(lambda x:age_cut(x), axis=1)

In [31]:

data.head()

# 方便计数
data["cut"] = 1

In [36]:

data.loc[data.age_cut == "best"].SALARY_MILLIONS.head()

# 基于年龄段对球员薪水和效率值进行分析
sns.set_style("darkgrid")
plt.figure(figsize=(10,10), dpi=100)
plt.title("RPM and Salary")

x1 = data.loc[data.age_cut == "old"].SALARY_MILLIONS
y1 = data.loc[data.age_cut == "old"].RPM
plt.plot(x1, y1, "^")

x2 = data.loc[data.age_cut == "best"].SALARY_MILLIONS
y2 = data.loc[data.age_cut == "best"].RPM
plt.plot(x2, y2, "^")

x3 = data.loc[data.age_cut == "young"].SALARY_MILLIONS
y3 = data.loc[data.age_cut == "young"].RPM
plt.plot(x3, y3, ".")

multi_data2 = data.loc[:, ['RPM','POINTS','TRB','AST','STL','BLK','age_cut']]

sns.pairplot(multi_data2, hue="age_cut")

球队数据分析

球队薪资排行

In [43]:

data.groupby(by="age_cut").agg({"SALARY_MILLIONS":np.max})

data_team = data.groupby(by="TEAM").agg({"SALARY_MILLIONS":np.mean})

data_team.sort_values(by="SALARY_MILLIONS", ascending=False).head(10)

# 按照分球队分年龄段,上榜球员降序排列,如上榜球员数相同,则按效率值降序排列。
data_rpm = data.groupby(by=["TEAM", "age_cut"]).agg({"SALARY_MILLIONS":np.mean,  "RPM":np.mean, "PLAYER":np.size})

In [51]:

data_rpm.head()

data_rpm.sort_values(by=["PLAYER", "RPM"], ascending=False).head()

按照球队综合实力排名

In [56]:

data_rpm2 = data.groupby(by=['TEAM'], as_index=False).agg({'SALARY_MILLIONS': np.mean,
                                                          'RPM': np.mean,
                                                          'PLAYER': np.size,
                                                          'POINTS': np.mean,
                                                          'eFG%': np.mean,
                                                          'MPG': np.mean,
                                                          'AGE': np.mean})

In [58]:

data_rpm2.head()

data_rpm2.sort_values(by="RPM", ascending=False).head()

利用箱线图和小提琴图进行数据分析

data.TEAM.isin(['GS', 'CLE', 'SA', 'LAC', 'OKC', 'UTAH', 'CHA', 'TOR', 'NO', 'BOS']).head()

sns.set_style("whitegrid")
plt.figure(figsize=(20, 10))
# 获取需要的数据
data_team2 = data[data.TEAM.isin(['GS', 'CLE', 'SA', 'LAC', 'OKC', 'UTAH', 'CHA', 'TOR', 'NO', 'BOS'])]

# 进行相应的绘图
plt.subplot(3,1,1)
sns.boxplot(x="TEAM", y="SALARY_MILLIONS", data = data_team2)

plt.subplot(3,1,2)
sns.boxplot(x="TEAM", y="AGE", data = data_team2)

plt.subplot(3,1,3)
sns.boxplot(x="TEAM", y="MPG", data = data_team2)

data.head()

# 绘制小提琴图
sns.set_style("whitegrid")
plt.figure(figsize=(20, 10))

plt.subplot(3,1,1)
sns.violinplot(x="TEAM", y="3P%", data=data_team2)

plt.subplot(3,1,2)
sns.violinplot(x="TEAM", y="eFG%", data=data_team2)

plt.subplot(3,1,3)
sns.violinplot(x="TEAM", y="POINTS", data=data_team2)


2.1 数据分析实战----北京租房数据统计分析

近年来随着经济的快速发展,一线城市的资源和就业机会吸引了很多外来人口,使其逐渐成为人口密集的城市之一。据统计,2017年北京市常住外来人口已经达到了2170.7万人,其中绝大多数人是以租房的形式解决居住问题。

本文将租房网站上北京地区的租房数据作为参考,运用前面所学到的数据分析知识,带领大家一起来分析真实数据,并以图表的形式得到以下统计指标:

  • (1)统计每个区域的房源总数量,并使用热力图分析房源位置分布情况
  • (2)使用条形图分析哪种户型的数量最多、更受欢迎。
  • (3)统计每个区域的平均租金,并结合柱状图和折线图分析各区域的房源数量和租金情况。
  • (4)统计面积区间的市场占有率,并使用饼图绘制各区间所占的比例。

1 数据基本介绍

目前网络上有很多的租房平台,比如自如、爱屋吉屋、房天下、链家等,其中,链家是目前市场占有率最高的公司,通过链家平台可以便捷且全面地提供可靠的房源信息。如下图所示:

通过网络爬虫技术,爬取链家网站中列出的租房信息(爬取结束时间为2018年9月10日),具体包括所属区域、小区名称、房屋、价格、房屋面积、户型。需要说明的是,链家官网上并没有提供平谷、怀柔、密云、延庆等偏远地区的租房数据,所以本案例的分析不会涉及这四个地区。

将爬到的数据下载到本地,并保存在“链家北京租房数据.csv”文件中,打开该文件后可以看到里面有很多条(本案例爬取的数据共计8224条)信息,具体如下图所示。

2 数据读取

准备好数据后,我们便可以使用 Pandas读取保存在CSV文件的数据,并将其转换成DataFrame对象展示,便于后续操作这些数据。

首先,读取数据:

import pandas as pd
import numpy as np

# 读取链家北京租房信息
file_data = pd.read_csv('./data/链家北京租房数据.csv')
file_data.head()

3 数据预处理

尽管从链家官网上直接爬取下来的数据大部分是比较规整的,但或多或少还是会存在一些问题,不能直接用做数据分析。为此,在使用前需要对这些数据进行一系列的检测与处理,包括处理重复值和缺失值、统一数据类型等,以保证数据具有更高的可用性。

3.1重复值和空值处理

预处理的前两步就是检查缺失值和重复值。如果希望检查准备的数据中是否存在重复的数据,则可以通过 Pandas中的 duplicated()方法完成。接下来,通过 duplicated()方法对北京租房数据进行检测,只要有重复的数据就会映射为True,具体代码如下。

# 重复数据检测
file_data.duplicated()

由于数据量相对较多,所以在 Jupyter NoteBook工具中有一部分数据会省略显示,但是从输出结果中仍然可以看到有多条返回结果为True的数据,这表明有重复的数据。这里,处理重复数据的方式是将其删除。接下来,使用 drop_duplicates()方法直接删除重复的数据,具体代码如下。

# 删除重复数据
file_data = file_data.drop_duplicates()

与上一次输出的行数相比,可以很明显地看到减少了很多条数据,只剩下了5773条数据。

对数据重复检测完成之后,便可以检测数据中是否存在缺失值,我们可以直接使用 dropna()方法检测并删除缺失的数据,具体代码如下。

# 删除缺失数据
file_data = file_data.dropna()

经过缺失数据检测之后,可以发现当前数据的总行数与之前相比没有发生任何变化。因此我们断定准备好的数据中并不存在缺失的数据。

3.2 数据转换类型

在这套租房数据中,“面积(m^2m​2​​)”一列的数据里面有中文字符,说明这一列数据都是字符串类型的。为了方便后续对面积数据进行数学运算,所以需要将“面积(m)”一列的数据类型转换为float类型,具体代码如下。

# 创建一个空数组
data_new = np.array([])
# 取出“面积”一列数据,将每个数据末尾的中文字符去除  fild_data.info()
data = file_data['面积(㎡)'].values
for i in data:
              data_new = np.append(data_new, np.array(i[:-2]))
# 通过astype()方法将str类型转换为float64类型
data = data_new.astype(np.float64)
# 用新的数据替换
file_data.loc[:,'面积(㎡)']= data

除此之外,在“户型”一列中,大部分数据显示的是“室厅”,只有个别数据显示的是"\房间*卫”(比如索引8219对应的一行)。为了方便后期的使用,需要将“房间"替换成"室",以保证数据的一致性。

接下来,使用 Pandas的 replace()方法完成替换数据的操作,具体代码如下。

# 获取“户型”一列数据
housetype_data = file_data['户型']
temp_list = []
# 通过replace()方法进行替换
for i in housetype_data:
    new_info = i.replace('房间','室')
    temp_list.append(new_info)
file_data.loc[:,'户型'] = temp_list

通过比较处理前与处理后的数据可以发现,索引为8219的户型数据已经由“4房间2卫”变成“4室2卫”,说明数据替换成功。

4 图表分析

数据经过预处理以后,便可以用它们来做分析了,为了能够更加直观地看到数据的变化,这里,我们采用图表的方式来辅助分析。

4.1房源数量、位置分布分析

如果希望统计各个区域的房源数量,以及查看这些房屋的分布情况,则需要先获取各个区的房源。为了实现这个需求,可以将整个数据按照“区域”一列进行分组。

为了能够准确地看到各区域的房源数量,这里只需要展示“区域”与“数量”这两列的数据即可。因此,先创建一个空的 DataFrame对象,然后再将各个区域计算的总数量作为该对象的数据进行展示,具体代码如下。

# 创建一个DataFrame对象,该对象只有两列数据:区域和数量

new_df = pd.DataFrame({'区域':file_data['区域'].unique(),'数量':[0]*13})

接下来,通过 Pandas的 groupby()方法将 file data对象按照“区域”一列进行分组,并利用count()方法统计每个分组的数量,具体代码如下。

# 按“区域”列将file_data进行分组,并统计每个分组的数量

groupy_area = file_data.groupby(by='区域').count()
new_df['数量'] = groupy_area.values

通过 sort_values()方法对new_df对象排序,按照从大到小的顺序进行排列,具体代码如下。

# 按“数量”一列从大到小排列

new_df.sort_values(by=['数量'], ascending=False)

通过输出的排序结果可以看出,房源数量位于前的区域分别是朝阳区、海淀区、丰台区。

4.2 户型数量分析

随着人们生活水平的提高,以及各住户的生活需求,开发商设计出了各种各样的户型供人们居住。接下来,我们来分析一下户型,统计租房市场中哪种户型的房源数量偏多,并筛选出数量大于50的户型。

首先,我们定义一个函数来计算各种户型的数量,具体代码如下。

# 定义函数,用于计算各户型的数量
def all_house(arr):
    key = np.unique(arr)
    result = {}
    for k in key:
        mask = (arr == k)
        arr_new = arr[mask]
        v = arr_new.size
        result[k] = v
    return result

# 获取户型数据
house_array = file_data['户型']
house_info = all_house(house_array)

程序输出了一个字典,其中,字典的键表示户型的种类,值表示该户型的数量。

使用字典推导式将户型数量大于50的元素筛选出来,并将筛选后的结果转换成 DataFrame对象,具体代码如下。

# 使用字典推导式
house_type = dict((key, value) for key, value 
in house_info.items() if value > 50)
show_houses = pd.DataFrame({'户型':[x for x in  house_type.keys()],
                  '数量':[x for x in house_type.values()]})

为了能够更直观地看到户型数量间的差异,我们可以使用条形图进行展示,其中,条形图纵轴坐标代表户型种类,横坐标代表数量体代码如下

import matplotlib.pyplot as plt

house_type = show_houses['户型']
house_type_num = show_houses['数量']
plt.barh(range(11), house_type_num, height=0.7, color='steelblue', alpha=0.8)     
plt.yticks(range(11), house_type)
plt.xlim(0,2500)  # 把x轴坐标延长到2500
plt.xlabel("数量")
plt.ylabel("户型种类")
plt.title("北京地区各户型房屋数量")
for x, y in enumerate(house_type_num):
    plt.text(y + 0.2, x - 0.1, '%s' % y)
plt.show()

运行结果如下图所示。

通过图可上以清晰地看出,整个租房市场中户型数量较多分别为“2室1厅”、“1室1厅”、“3室1厅”的房屋,其中,“2室1厅”户型的房屋在整个租房市场中是数量最多的。

4.3 平均租金分析

为了进一步剖析房屋的情况,接下来,我们来分析一下各地区目前的平均租金情况。计算各区域房租的平均价格与计算各区域户型数量的方法大同小异,首先创建一个 DataFrame对象,具体代码如下。

# 新建一个DataFrame对象,设置房租总金额和总面积初始值为0

df_all = pd.DataFrame({'区域':file_data['区域'].unique(),
                         '房租总金额':[0]*13,
                         '总面积(㎡)':[0]*13})

接下来,按照“区域”一列进行分组,然后调用sum()方法分别对房租金额和房屋面积执行求和计算,具体代码如下:

# 求总金额和总面积

sum_price = file_data['价格(元/月)'].groupby(file_data['区域']).sum()
sum_area = file_data['面积(㎡)'].groupby(file_data['区域']).sum()
df_all['房租总金额'] = sum_price.values
df_all['总面积(㎡)'] = sum_area.values

计算出各区域房租总金额和总面积之后,便可以对每平方米的租金进行计算。在df_all对象的基础上增加一列,该列的名称为“每平方米租金(元)”,数据为求得的每平方米的平均价格,具体代码如下。

# 计算各区域每平米房租价格,并保留两位小数

df_all['每平米租金(元)'] = round(df_all['房租总金额'] / df_all ['总面积(㎡)'], 2)

为了能更加全面地了解到各个区域的租房数量与平均租金,我们可以将之前创建的 new_df对象(各区域房源数量)与df_all对象进行合并展示,由于这两个对象中都包含“区域”一列,所以这里可以采用主键的方式进行合并,也就是说通过 merge()函数来实现,具体代码如下。

# 合并new_df与df_all

df_merge = pd.merge(new_df, df_all)

合并完数据以后,就可以借用图表来展示各地区房屋的信息,其中,房源的数量可以用柱状图中的条柱表示,每平方米租金可以用折线图中的点表示,具体代码如下。

num= df_merge['数量']   # 数量
price=df_merge['每平米租金(元)'] # 价格
l=[i for i in range(13)]

lx=df_merge['区域']
fig = plt.figure(figsize=(10, 8), dpi=100)

# 显示折线图
ax1 = fig.add_subplot(111) 
ax1.plot(l, price,'or-',label='价格')  # "or-" 显示那个小红圆点
for i,(_x,_y) in enumerate(zip(l,price)):
    plt.text(_x,_y,price[i])  
ax1.set_ylim([0, 200])
ax1.set_ylabel('价格')
plt.legend(loc='upper left') 

# 显示条形图
ax2 = ax1.twinx()  # 显示次坐标轴ax2=ax1.twinx()
plt.bar(l,num,alpha=0.3,color='green',label='数量')
ax2.set_ylabel('数量')
plt.legend(loc="upper right")
plt.xticks(l,lx)

plt.show()

运行结果如下:

从图中可以看出,西城区、东城区、海淀区、朝阳区的房租价格相对较高,这主要是因为东城区和西城区作为北京市的中心区,租金相比其他几个区域自然偏高一些,而海淀区租金较高的原因推测可能是海淀区名校较多,也是学区房最火热的地带,朝阳区内的中央商务区聚集了大量的世界500强公司,因此这四个区域的房租相对其他区域较高。

4.4 面积区间分析

下面我们将房屋的面积数据按照一定的规则划分成多个区间,看一下各面积区间的上情况,便于分析租房市场中哪种房屋类型更好出租,哪个面积区间的相房人数最多

要想将数据划分为若干个区间,则可以使用Pame中的cut()函数来实现,首先,使用max()与min()方法分别计算出房屋面积的最大值和最小值,具体代码如下。

# 查看房屋的最大面积和最小面积
print('房屋最大面积是%d平米'%(file_data['面积(㎡)'].max()))
print('房屋最小面积是%d平米'%(file_data['面积(㎡)'].min()))

# 查看房租的最高值和最小值
print('房租最高价格为每月%d元'%(file_data['价格(元/月)'].max()))
print('房屋最低价格为每月%d元'%(file_data['价格(元/月)'].min()))

在这里,我们参照链家网站的面积区间来定义,将房屋面积划分为8个区间。然后使用describe()方法显示各个区间出现的次数( counts表示)以及频率(freps表示),具体代码如下。

# 面积划分
area_divide = [1, 30, 50, 70, 90, 120, 140, 160, 1200]
area_cut = pd.cut(list(file_data['面积(㎡)']), area_divide)
area_cut_data = area_cut.describe()

接着,使用饼图来展示各面积区间的分布情况,具体代码如下。

area_percentage = (area_cut_data['freqs'].values)*100

labels  = ['30平米以下', '30-50平米', '50-70平米', '70-90平米',
'90-120平米','120-140平米','140-160平米','160平米以上']

plt.figure(figsize=(20, 8), dpi=100)
plt.axes(aspect=1)  # 显示的是圆形,如果不加,是椭圆形
plt.pie(x=area_percentage, labels=labels, autopct='%.2f %%', shadow=True)
plt.legend(loc='upper right')
plt.show()

运行结果如图所示:

通过上图可以看出,50-70平方米的房屋在租房市场中占有率最大。总体看来,租户主要以120平方米以下的房屋为租住对象,其中50~70平方米以下的房屋为租户的首选对象。


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

获取数据
file_data = pd.read_csv("./data/链家北京租房数据.csv")
file_data
区域小区名称户型面积(㎡)价格(元/月)
0东城万国城MOMA1室0厅59.11平米10000
1东城北官厅胡同2号院3室0厅56.92平米6000
2东城和平里三区1室1厅40.57平米6900
3东城菊儿胡同2室1厅57.09平米8000
4东城交道口北二条35号院1室1厅42.67平米5500
5东城西营房2室1厅54.48平米7200
6东城地坛北门1室1厅33.76平米6000
7东城安外东河沿1室1厅37.62平米5600
8东城清水苑1室1厅45.61平米6200
9东城李村东里2室1厅57.35平米5700
10东城幸福北里2室1厅51.15平米6500
11东城保利蔷薇2室1厅97.11平米10000
12东城东板桥西巷2室1厅52.86平米5800
13东城本家润园三期2室1厅63.09平米7800
14东城营房西街2室1厅62.95平米7500
15东城新景家园西区1室1厅57.24平米7500
16东城东花市北里东区2室1厅85.36平米8800
17东城幸福家园一期5室2厅226.86平米29000
18东城景泰西里西区1室1厅60.3平米6200
19东城海晟名苑北区1室1厅70.86平米12000
20东城和平新城一期2室1厅122.76平米14500
21东城太华公寓2室2厅152.24平米17000
22东城官书院2室1厅92.01平米16000
23东城幸福家园二期2室1厅65.25平米7800
24东城安外大街3号院1室1厅33.77平米5500
25东城中海紫御公馆2室2厅90.15平米13000
26东城海晟名苑北区1室0厅45.62平米9000
27东城凯景铭座3室1厅156.2平米16000
28东城永定门东街西里2室1厅53.26平米5000
29东城西青年沟2室1厅51.88平米7300
..................
8193顺义南竺园3室1厅90.47平米4200
8194顺义石园东苑2室2厅102.94平米4000
8195顺义建新北区2室1厅52.44平米3200
8196顺义仓上小区3室1厅108.03平米3900
8197顺义石园东区2室1厅91.93平米4100
8198顺义裕龙三区1室1厅69.04平米3800
8199顺义建新北区2室1厅50.04平米3600
8200顺义东兴二区2室1厅81.98平米4000
8201顺义万科四季花城2室1厅98.71平米6000
8202顺义石园南区18号院1房间1卫58.7平米4000
8203顺义建新北区2室1厅49.06平米3600
8204顺义香悦四季3室1厅87.92平米4200
8205顺义香悦四季3室2厅117.5平米6000
8206顺义旭辉26街区3室1厅59平米4500
8207顺义江山赋2室1厅97.38平米8800
8208顺义樱花园六区2室2厅94.78平米4200
8209顺义樱花园二区2室1厅68.76平米3500
8210顺义樱花园六区2室2厅94.78平米3900
8211顺义胜利小区2室1厅58.05平米3800
8212顺义樱花园一区2室1厅79.59平米3900
8213顺义江山赋2室1厅74.62平米5200
8214顺义江山赋3室1厅104.03平米9500
8215顺义恒华安纳湖2室1厅90.43平米3500
8216顺义石园北区2室2厅90.67平米3700
8217顺义江山赋3室2厅146.92平米17000
8218顺义怡馨家园3室1厅114.03平米5500
8219顺义旭辉26街区4房间2卫59平米5000
8220顺义前进花园玉兰苑3室1厅92.41平米5800
8221顺义双裕小区2室1厅71.81平米4200
8222顺义樱花园二区1室1厅35.43平米2700

8223 rows × 5 columns

file_data.shape
(8223, 5)
file_data.info()

 
file_data.describe()

价格(元/月)
count8223.000000
mean9512.297823
std9186.752612
min566.000000
25%4800.000000
50%6800.000000
75%10000.000000
max150000.000000
数据基本处理
重复值和空值处理
# 重复值
# file_data.duplicated()

file_data = file_data.drop_duplicates()
file_data.shape
(5773, 5)
# 空值处理
file_data = file_data.dropna()
file_data.shape
(5773, 5)
数据转换类型
面积数据类型转换
file_data.head()

# 单个值实现
file_data["面积(㎡)"].values[0][:-2]
'59.11'
# 创建一个空的数组
data_new = np.array([])

data_area = file_data["面积(㎡)"].values

for i in data_area:
    data_new = np.append(data_new, np.array(i[:-2]))
data_area
array(['59.11平米', '56.92平米', '40.57平米', ..., '92.41平米', '71.81平米',
       '35.43平米'], dtype=object)
data_new
array(['59.11', '56.92', '40.57', ..., '92.41', '71.81', '35.43'],
      dtype='<U32')
# 转换data_new中的数据类型
data_new = data_new.astype(np.float64)
data_new
array([59.11, 56.92, 40.57, ..., 92.41, 71.81, 35.43])
file_data.loc[:, "面积(㎡)"] = data_new
file_data.head()

户型表达方式替换
file_data
区域小区名称户型面积(㎡)价格(元/月)
0东城万国城MOMA1室0厅59.1110000
1东城北官厅胡同2号院3室0厅56.926000
2东城和平里三区1室1厅40.576900
3东城菊儿胡同2室1厅57.098000
4东城交道口北二条35号院1室1厅42.675500
5东城西营房2室1厅54.487200
6东城地坛北门1室1厅33.766000
7东城安外东河沿1室1厅37.625600
8东城清水苑1室1厅45.616200
9东城李村东里2室1厅57.355700
10东城幸福北里2室1厅51.156500
11东城保利蔷薇2室1厅97.1110000
12东城东板桥西巷2室1厅52.865800
13东城本家润园三期2室1厅63.097800
14东城营房西街2室1厅62.957500
15东城新景家园西区1室1厅57.247500
16东城东花市北里东区2室1厅85.368800
17东城幸福家园一期5室2厅226.8629000
18东城景泰西里西区1室1厅60.306200
19东城海晟名苑北区1室1厅70.8612000
20东城和平新城一期2室1厅122.7614500
21东城太华公寓2室2厅152.2417000
22东城官书院2室1厅92.0116000
23东城幸福家园二期2室1厅65.257800
24东城安外大街3号院1室1厅33.775500
25东城中海紫御公馆2室2厅90.1513000
26东城海晟名苑北区1室0厅45.629000
27东城凯景铭座3室1厅156.2016000
28东城永定门东街西里2室1厅53.265000
29东城西青年沟2室1厅51.887300
..................
8173顺义智地香蜜湾4房间2卫75.878000
8174顺义香花畦家园4室2厅179.6512000
8175顺义龙湖香醍漫步四区南区1室0厅38.962300
8177顺义莫奈花园4室2厅241.7820000
8181顺义裕龙三区3室1厅152.729000
8183顺义万科城市花园4室2厅218.4415500
8192顺义胜利小区1室1厅43.803000
8193顺义南竺园3室1厅90.474200
8200顺义东兴二区2室1厅81.984000
8201顺义万科四季花城2室1厅98.716000
8202顺义石园南区18号院1房间1卫58.704000
8203顺义建新北区2室1厅49.063600
8204顺义香悦四季3室1厅87.924200
8205顺义香悦四季3室2厅117.506000
8206顺义旭辉26街区3室1厅59.004500
8207顺义江山赋2室1厅97.388800
8208顺义樱花园六区2室2厅94.784200
8209顺义樱花园二区2室1厅68.763500
8210顺义樱花园六区2室2厅94.783900
8211顺义胜利小区2室1厅58.053800
8212顺义樱花园一区2室1厅79.593900
8214顺义江山赋3室1厅104.039500
8215顺义恒华安纳湖2室1厅90.433500
8216顺义石园北区2室2厅90.673700
8217顺义江山赋3室2厅146.9217000
8218顺义怡馨家园3室1厅114.035500
8219顺义旭辉26街区4房间2卫59.005000
8220顺义前进花园玉兰苑3室1厅92.415800
8221顺义双裕小区2室1厅71.814200
8222顺义樱花园二区1室1厅35.432700

5773 rows × 5 columns

house_data = file_data["户型"]
temp_list = []

for i in house_data:
    # print(i)
    new_info = i.replace("房间", "室")
    temp_list.append(new_info)
file_data.loc[:, "户型"] = temp_list
file_data
区域小区名称户型面积(㎡)价格(元/月)
0东城万国城MOMA1室0厅59.1110000
1东城北官厅胡同2号院3室0厅56.926000
2东城和平里三区1室1厅40.576900
3东城菊儿胡同2室1厅57.098000
4东城交道口北二条35号院1室1厅42.675500
5东城西营房2室1厅54.487200
6东城地坛北门1室1厅33.766000
7东城安外东河沿1室1厅37.625600
8东城清水苑1室1厅45.616200
9东城李村东里2室1厅57.355700
10东城幸福北里2室1厅51.156500
11东城保利蔷薇2室1厅97.1110000
12东城东板桥西巷2室1厅52.865800
13东城本家润园三期2室1厅63.097800
14东城营房西街2室1厅62.957500
15东城新景家园西区1室1厅57.247500
16东城东花市北里东区2室1厅85.368800
17东城幸福家园一期5室2厅226.8629000
18东城景泰西里西区1室1厅60.306200
19东城海晟名苑北区1室1厅70.8612000
20东城和平新城一期2室1厅122.7614500
21东城太华公寓2室2厅152.2417000
22东城官书院2室1厅92.0116000
23东城幸福家园二期2室1厅65.257800
24东城安外大街3号院1室1厅33.775500
25东城中海紫御公馆2室2厅90.1513000
26东城海晟名苑北区1室0厅45.629000
27东城凯景铭座3室1厅156.2016000
28东城永定门东街西里2室1厅53.265000
29东城西青年沟2室1厅51.887300
..................
8173顺义智地香蜜湾4室2卫75.878000
8174顺义香花畦家园4室2厅179.6512000
8175顺义龙湖香醍漫步四区南区1室0厅38.962300
8177顺义莫奈花园4室2厅241.7820000
8181顺义裕龙三区3室1厅152.729000
8183顺义万科城市花园4室2厅218.4415500
8192顺义胜利小区1室1厅43.803000
8193顺义南竺园3室1厅90.474200
8200顺义东兴二区2室1厅81.984000
8201顺义万科四季花城2室1厅98.716000
8202顺义石园南区18号院1室1卫58.704000
8203顺义建新北区2室1厅49.063600
8204顺义香悦四季3室1厅87.924200
8205顺义香悦四季3室2厅117.506000
8206顺义旭辉26街区3室1厅59.004500
8207顺义江山赋2室1厅97.388800
8208顺义樱花园六区2室2厅94.784200
8209顺义樱花园二区2室1厅68.763500
8210顺义樱花园六区2室2厅94.783900
8211顺义胜利小区2室1厅58.053800
8212顺义樱花园一区2室1厅79.593900
8214顺义江山赋3室1厅104.039500
8215顺义恒华安纳湖2室1厅90.433500
8216顺义石园北区2室2厅90.673700
8217顺义江山赋3室2厅146.9217000
8218顺义怡馨家园3室1厅114.035500
8219顺义旭辉26街区4室2卫59.005000
8220顺义前进花园玉兰苑3室1厅92.415800
8221顺义双裕小区2室1厅71.814200
8222顺义樱花园二区1室1厅35.432700

5773 rows × 5 columns

图表分析
房源数量、位置分布分析
file_data["区域"].unique()
array(['东城', '丰台', '亦庄开发区', '大兴', '房山', '昌平', '朝阳', '海淀', '石景山', '西城',
       '通州', '门头沟', '顺义'], dtype=object)
new_df = pd.DataFrame({"区域":file_data["区域"].unique(), "数量":[0]*13})
new_df

# 获取每个区域房源数量
area_count = file_data.groupby(by="区域").count()
new_df["数量"] = area_count.values
new_df.sort_values(by="数量", ascending=False)

户型数量分析
house_data = file_data["户型"]
house_data.head()
0    1室0厅
1    3室0厅
2    1室1厅
3    2室1厅
4    1室1厅
Name: 户型, dtype: object
def all_house(arr):
    key = np.unique(arr)
    result = {}
    
    for k in key:
        mask = (arr == k)
        arr_new = arr[mask]
        v = arr_new.size
        result[k] = v
        
    return result    
house_info = all_house(house_data)
house_info
{'0室0厅': 1,
 '1室0卫': 10,
 '1室0厅': 244,
 '1室1卫': 126,
 '1室1厅': 844,
 '1室2厅': 13,
 '2室0卫': 1,
 '2室0厅': 23,
 '2室1卫': 120,
 '2室1厅': 2249,
 '2室2卫': 22,
 '2室2厅': 265,
 '2室3厅': 1,
 '3室0卫': 3,
 '3室0厅': 12,
 '3室1卫': 92,
 '3室1厅': 766,
 '3室2卫': 48,
 '3室2厅': 489,
 '3室3卫': 1,
 '3室3厅': 10,
 '4室1卫': 15,
 '4室1厅': 58,
 '4室2卫': 24,
 '4室2厅': 191,
 '4室3卫': 5,
 '4室3厅': 9,
 '4室5厅': 2,
 '5室0卫': 1,
 '5室0厅': 1,
 '5室1卫': 3,
 '5室1厅': 7,
 '5室2卫': 7,
 '5室2厅': 49,
 '5室3卫': 3,
 '5室3厅': 24,
 '5室4厅': 1,
 '5室5厅': 1,
 '6室0厅': 1,
 '6室1卫': 1,
 '6室1厅': 1,
 '6室2厅': 5,
 '6室3卫': 2,
 '6室3厅': 6,
 '6室4卫': 2,
 '7室1厅': 1,
 '7室2厅': 2,
 '7室3厅': 3,
 '7室4厅': 1,
 '8室4厅': 2,
 '9室1厅': 2,
 '9室2厅': 1,
 '9室5厅': 2}
# 去掉统计数量较少的值
house_data = dict((key, value) for key, value in house_info.items() if value > 50)
show_houses = pd.DataFrame({"户型": [x for x in house_data.keys()],
              "数量": [x for x in house_data.values()]})
show_houses

# 图形展示房屋类型

house_type = show_houses["户型"]
house_type_num = show_houses["数量"]

plt.barh(range(11), house_type_num)

plt.yticks(range(11), house_type)
plt.xlim(0, 2500)

plt.title("北京市各区域租房数量统计")
plt.xlabel("数量")
plt.ylabel("房屋类型")

# 给每个条上面添加具体数字
for x, y in enumerate(house_type_num):
    # print(x, y)
    plt.text(y+0.5, x-0.2, "%s" %y)

plt.show()

平均租金分析
df_all = pd.DataFrame({"区域": file_data["区域"].unique(),
              "房租总金额": [0]*13,
              "总面积": [0]*13})
df_all

file_data.head()

sum_price = file_data["价格(元/月)"].groupby(file_data["区域"]).sum()
sum_area = file_data["面积(㎡)"].groupby(file_data["区域"]).sum()
df_all["房租总金额"] = sum_price.values
df_all["总面积"] = sum_area.values
df_all

# 计算各个区域每平方米的房租
df_all["每平米租金(元)"] = round(df_all["房租总金额"] / df_all["总面积"], 2)
df_all

df_merge = pd.merge(new_df, df_all)
df_merge

# 图形可视化

num = df_merge["数量"]
price = df_merge["每平米租金(元)"]
lx = df_merge["区域"]
l = [i for i in range(13)]

fig = plt.figure(figsize=(10, 8), dpi=100)

# 显示折线图
ax1 = fig.add_subplot(111)
ax1.plot(l, price, "or-", label="价格")
for i, (_x, _y) in enumerate(zip(l, price)):
    plt.text(_x+0.2, _y, price[i])
ax1.set_ylim([0, 160])   
ax1.set_ylabel("价格")
plt.legend(loc="upper right")

# 显示条形图
ax2 = ax1.twinx()
plt.bar(l, num, label="数量", alpha=0.2, color="green")
ax2.set_ylabel("数量")
plt.legend(loc="upper left")
plt.xticks(l, lx)


plt.show()

面积基本分析
# 查看房屋的最大面积和最小面积
print('房屋最大面积是%d平米'%(file_data['面积(㎡)'].max()))
print('房屋最小面积是%d平米'%(file_data['面积(㎡)'].min()))

# 查看房租的最高值和最小值
print('房租最高价格为每月%d元'%(file_data['价格(元/月)'].max()))
print('房屋最低价格为每月%d元'%(file_data['价格(元/月)'].min()))
房屋最大面积是1133平米
房屋最小面积是11平米
房租最高价格为每月150000元
房屋最低价格为每月566元
# 面积划分
area_divide = [1, 30, 50, 70, 90, 120, 140, 160, 1200]
area_cut = pd.cut(list(file_data["面积(㎡)"]), area_divide)
area_cut_num = area_cut.describe()
area_cut_num

area_per = (area_cut_num["freqs"].values)*100

labels  = ['30平米以下', '30-50平米', '50-70平米', '70-90平米',
'90-120平米','120-140平米','140-160平米','160平米以上']

plt.figure(figsize=(20, 8), dpi=100)
# plt.axes(aspect=1)

plt.pie(x=area_per, labels=labels, autopct="%.2f %%")


plt.legend()
plt.show()


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

あずにゃん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值