【datawhale-gitmodel】以波士顿房价数据进行数据分析和数据可视化

884 阅读11分钟

以波士顿房价数据进行数据分析

@[toc]

1 总体分析步骤

  1. 加载数据
  2. 概览数据
  3. 缺失值处理
  4. 变量分析
  • 单一变量
  • 多变量关系

2 数据加载

  1. 载入数据 house = pd.read.csv("./data/boston.csv").head()
  2. 数据的规模 house.shape
  3. 数据集的每一列的列名 house.columns
  4. 基本统计描述 house.describe() / house.info()
  • 查看是否有缺失值
  • 查看数据是否正常,例如A是一个处于[0,1]之间的比值,但在展示中A>1,说明数据不正常,需要修正
  • 查看统计数据是否正常,例如方差特别大,在后续的处理中,可以采用一些降维的手段如:主成分分析(PCA)等

3 数据处理

3.1 缺失值处理

  1. 总体查看: null.info() 在这里插入图片描述
  • 查看样本总数为506,非空值不等于506的即为含有缺失值的特征
  1. 缺失值计算:null.isnull().sum()
  2. 缺失值可视化
  • 柱状图
missing = null.isnull().sum()
missing = missing[missing > 0] # 筛选出有缺失值(大于0)的特征
missing.sort_values(inplace = True) # 排序
missing.plot.bar() # 调用pandas内置的条形图绘制

在这里插入图片描述

  • 热力图 sns.heatmap(null.isnull()) 在这里插入图片描述

  • 热力图可以帮助看出缺失值的位置,如果有些样本出现大量特征缺失的情况,在样本足够大的情况下也可以考虑删除

  1. 统计缺失比例
A = [] 
for col in null.columns:
    A.append((col,
             null[col].isnull().sum() * 100 / null.shape[0]))
pd.DataFrame(A, columns=['Features', 'missing rate'])

在这里插入图片描述

  • 缺失值超过95%的特征,会考虑删除,因为意义不大
  • 方差特别小的特征也考虑删除,或者说取值唯一的特征是没有意义的;举例,如果在一次考试中,大家的数学成绩都是100分,那么是没有办法通过数学成绩来区分每位同学的能力的,也就是说数学这门课没有区分性,不具有分析的意义,可以删掉
  1. 统计每个特征会有多少不同取值 df.nunique()
  • 为了更好的呈现,将特征的唯一值个数加入到上面的统计表中,并封装成函数,便于之后使用
def df_stats(df):
    '''
    统计该df的缺失值,比例以及唯一值个数.
    '''
    L = []
    for col in df.columns:
        L.append((col, 
                  df[col].isnull().sum(),
                  df[col].isnull().sum() * 100 / df.shape[0],
                  df[col].nunique()))
    res = pd.DataFrame(L, columns = ['Feature', 
                                     'missing num',
                                     'missing rate',
                                     'unique num']) 
    return res
  1. 缺失值填充 null = null.fillna(null.mean()) # 用均值填充
  • 这是比较简单粗暴的方式,之后会细讲缺失值处理方式

  • 可视化查看 sns.heatmap(null.isnull()) 此时已没有缺失值 在这里插入图片描述

3.2 变量分析

  • 研究思路:首先研究单变量的数据类型、分布情况、是否有离群值等情况,再研究变量之间的关系:相关性分析、分组统计等

3.2.1 单变量分析

3.2.1.1 分析标签

  1. 标签类型
  • 首先是分析数据标签,要明白任务的需求。数据挖掘任务主要分为两类:回归分类
  • 回归指标签值是连续值的任务,例如用到的数据集,要求预测房价信息,房价是一个连续的变量,因此这样的任务属于回归问题(Regression)
  • 分类指标签值离散的任务,例如鸢尾花(Iris)数据集,收集三种鸢尾花的萼片长度,宽度等信息,要求根据这些信息预测鸢尾花属于哪种类别,因此标签就是1,2,3,代表三类鸢尾花。这种任务属于分类任务
  1. house["MEDV"].nunique() 查看标签的取值:因为取值个数比较多,可以大致判断本次任务属于回归型任务。 但这并非判断标准,只是一个判断的参考,主要还是分析题目的任务需求

  2. house["MEDV"].describe() 打印标签的统计信息: 在这里插入图片描述

  • 从统计信息可以大致看出,分布还算正常,没有太大的方差(方差太大说明数据不太可能是正态的,因为不够集中)。对于是否有离群值后面我们需要通过箱线图或者散点图等可视化分析后才能得知
  1. 画密度分布图,看标签取值的总体分布情况
plt.figure()
sns.distplot(house["MEDV"],
            bins=100,#柱子的个数
            hist=True,#是否绘制直方图
            kde=True,#是否绘制密度图
            rug=True,#数据分布标尺
            color='r',#颜色
#             vertical=False,#是否水平绘制
#             norm_hist=False,#标准化,kde为True时自动标准化
            axlabel=None,#x轴标注
            label=None,#图例标签,通过plt.legend()显示
            ax=None,
            )
  • 如图 在这里插入图片描述
  • 看到整个分布稍微有一点左偏,因此在实战中,我们可以采用对数化的方法,让标签分布接近正态
plt.figure()
sns.distplot(np.log(house["MEDV"]),
            bins=100,#柱子的个数
            hist=True,#是否绘制直方图
            kde=True,#是否绘制密度图
            rug=True,#数据分布标尺
            color='r',#颜色
#             vertical=False,#是否水平绘制
#             norm_hist=False,#标准化,kde为True时自动标准化
            axlabel=None,#x轴标注
            label=None,#图例标签,通过plt.legend()显示
            ax=None,
            )

在这里插入图片描述

  • 变换之后,可以将取值小于1.5的取值视为异常值,将其舍弃
  • 对于分类任务而言,可以采用此法画出它的频率分布直方图,或者更直接的画条形统计图,如果分类的标签分布是相似的,那么我们可以直接建模;如果标签出现很明显的一高一低的情况,说明标签的分布不均衡,在建模前需要做一些操作(例如:欠采样、调整损失函数),再进行建模。否则会出现一个问题,模型对出现频率低的样本非常不敏感。好比数学考试前的复习题,大多数都是数列题,只有一个是立体几何题目,大家花了很多精力研究数列,结果一到考试,出现了很多立体几何的题目,这时候我们复习的效果其实会显得很差。模型训练的过程就类似考前复习的过程,所以我们在建模前要尽量保证标签的均衡性。

3.2.1.2 离散型变量

  1. 查看数据 df_stats(null)
  • 对于类别型变量而言,一般采用柱状图的方式观察其分布
  • 从这个表的最后一列可以看出CHAS,RAD这样的变量的取值比较符合分类变量的特征 在这里插入图片描述
  1. 绘制柱状图 house[['CHAS', 'RAD']].hist() 在这里插入图片描述
  2. 类别型变量更大的作用是后续在特征工程中,用于进行特征交叉,继续特征的增强,挖掘更深层次的潜在信息

3.2.1.3 连续型变量

  1. 先绘制连续型变量的分布情况 house[['CRIM', 'ZN', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']].hist(figsize = (10, 15), bins = 100) 在这里插入图片描述
  • 从上面的图像可以看出,有些变量实际上是有相似(A高B高)的分布情况的,或者相反的分布情况的(A高B低),这样的性质称为变量之间的相关性
  1. 相关性一般有两种情况:
  • 正相关:如果特征A的增加(减少)会导致特征B增加(减少),即特征A与特征B变化趋势相同
  • 负相关:如果特征A的增加(减少)会导致特征B减少(增加),即特征A与特征B变化趋势相反
  1. 注意
  • 当两个变量呈正相关或者负相关时,我们称两个变量是相关的。如果两个变量有较强的相关性时,将这些变量一起用,会有很多是冗余的
  • 因为变量之间有较强的相关性,那么变量与变量之间是可以相互表示的,所以在建模时,需要尽可能消除特征之间的共线性,也就是尽量使用相关性比较小的特征,这样可以减少训练时间,使得模型的学习效果更好

3.2.2 多变量分析

3.2.2.1 数据图描述相关性

  1. 从可视化的角度来研究变量之间的相关性
  • 对于可视化而言,最简单的方式就是将两个特征对应的坐标点在坐标系下描出来,研究他们的变化趋势
  • 每一个点的坐标试一个二元组(xA(i),yB(i))\left(x_{A}^{(i)}, y_{B}^{(i)}\right),其中 xA(i)x_{A}^{(i)} 表示第 ii 个样本,特征 AA 的取值; yB(i)y_{B}^{(i)} 表示第 ii 个样本,特征 BB 的取值
  • seaborn库可以很直观的任意两个特征之间的散点图
# 任意两个变量间的相关性散点图
sns.set()
cols = house.columns # 罗列出数据集的所有列
sns.pairplot(house[cols], size = 2.5) # 成对画出任意两列的散点图
plt.show() # 显示函数

在这里插入图片描述

  1. 首先,我们先看图像呈“对角线分布的”,即/或者\这两种类型分布的图像,这样的图像说明这两个变量间有较强的相关性,是可以被消除共线性的“嫌疑对象”。如图中的DISAGE,尽管他不是严格的直线分布,但至少其分布呈现带状
  • 成对画出任意两列的散点图 sns.pairplot(house[["AGE", "DIS"]], size = 3) #size是子图的大小
  • 如图 在这里插入图片描述
  1. 其次,可以看那些趋势明显比较单调,但是不太像直线,而是类似“对数函数”的图像,例如图中的DISNOX,因为这类图像,只要对数化,就可以得到类似直线的效果,那么也可以被处理
  • 成对画出任意两列的散点图(带对数处理) sns.pairplot(np.log(house[["NOX", "DIS"]]), size = 3) #size是子图的大小
  • 如图 在这里插入图片描述
  1. 还需关注最后一行(列)与其他行(列)的关系,也就是特征与标签的关系,如果这两者出现较强相关性,那么我们需要留意这些特征,因为这些特征对标签有着比较直接的关系,例如RMLSTAT
  • 成对画出任意两列的散点图(带对数处理)sns.pairplot(np.log(house[["RM", "LSTAT", "MEDV"]]), size = 3 #size是子图的大小 在这里插入图片描述

3.2.2.2 数据描述相关性

  1. 协方差
  • 公式: σij=cov(Xi,Xj)=E[(Xiμi)(Xjμj)]\sigma_{i j}=\operatorname{cov}\left(X_{i}, X_{j}\right)=E\left[\left(X_{i}-\mu_{i}\right)\left(X_{j}-\mu_{j}\right)\right] 其中, Xi,XjX_{i}, X_{j} 是第 i,ji, j 个随机变量,因此我们可以诱导出协方差矩阵:Σ=(σij)\Sigma=\left(\sigma_{i j}\right)

  • 若变量 XiX_{i} 的较大值主要与另一个变量 XjX_{j} 的较大值相对应,而两者的较小值也相对应,则可称两变量倾向于表现出相似的行为,协方差为正;当一个变量的较大值主要对应于另一个变量的较小值时,则可称两变量倾向于表现出相反的行为,协方差为负

    协方差的正负号显示变量的相关性

  • 当有了一系列数据后,我们可以认为这一系列数据是从对应的分布里面采样得到的,可以当成随机变量的观测值,因此可以直接代入算期望

  1. 计算协方差 house.cov() #只有数值型变量才能算,类别或文本需要编码后才能计算 在这里插入图片描述
  • 但是协方差只能看出正相关和负相关,其大小并没有意义,这是因为每个特征的量纲不同,直接乘积后求期望,所得到的量纲又是不尽相同,因此我们需要对其做“无量纲化”处理
  1. 相关系数
  • 公式:r(X,Y)=Cov(X,Y)Var[X]Var[Y]r(X, Y)=\frac{\operatorname{Cov}(X, Y)}{\sqrt{\operatorname{Var}[X] \operatorname{Var}[Y]}} 其中, Cov(X,Y)\operatorname{Cov}(X, Y)XXYY 的协方差, Var[X]\operatorname{Var}[X]XX 的方差, Var[Y]\operatorname{Var}[Y]YY 的方差

    r(X,Y)1|r(X, Y)| \leq 1

    r(X,Y)=1|r(X, Y)|=1 的充要条件是, 存在常数 a,ba, b, 使得 P{Y=a+bX}=1P\{Y=a+b X\}=1

  • 相关系数定量地刻画了 XXYY 的相关程度,范围是[1,1][-1,1]。绝对值越靠近0,表示不相关,绝对值越靠近1,表示相关性越强

    小于 0 表示负相关;大于 0 表示正相关

  • XXYY 完全相关的含义是在概率为 1 的意义下存在线性关系,于是 r(X,Y)|r(X, Y)| 是一个可以表征 XXYY 之间线性关系紧密程度量

    r(X,Y)|r(X, Y)| 较大时,通常说 XXYY 相关程度较好;当 r(X,Y)|r(X, Y)| 较小时,通常说 XXYY 相 关程度较差; 当 XXYY 不相关,通常认为 XXYY 之间不存在线性关系,但并不能排除 XXYY 之间可能存在其他关系。

  1. 计算相关系数
  • 单项相关系数的计算
# 请你尝试实现相关系数的计算
def Corr(X, Y):
    '''
    计算随机变量X,Y的相关系数.
    X: np.array or pd.Series.
    Y: np.array or pd.Series.
    '''
    COV_XY = np.cov(X, Y)  #协方差矩阵
    std_X = np.std(X)   #X的标准差
    std_Y = np.std(Y)   #Y的标准差
    
    Corr_Matrix = COV_XY / (std_X * std_Y)
    
    return Corr_Matrix
# (np.cov(house['AGE'], house['DIS'])/ ((np.std(house['AGE']) * np.std(house['DIS']))))[0,1]
# 利用自己实现的函数进行单项相关系数的计算
Corr(house['AGE'], house['DIS'])
  • 全表相关系数的计算
# 直接计算相关性系数
Corr_Matrix = house.corr()
Corr_Matrix
  1. 热力图展示相关性系数
#correlation matrix
f, ax = plt.subplots(figsize=(12, 9)) # 设置画布
sns.heatmap(Corr_Matrix, vmax=.8, square=True # 画热力图
#             , annot=True # s是否显示数值
           )

在这里插入图片描述

注:本文整理内容来自datawhale社区和gitmodel开源课程,并补充查找的其他资料。