EDA初体验--波士顿房价分析

# !pip install jupyterlab "ipywidgets>=7.5" 
# !pip install npm
# !conda install -c conda-forge nodejs
! pip install plotly
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting plotly
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/2d/6a/2c2e9ed190066646bbf1620c0b67f8e363e0024ee7bf0d3ea1fdcc9af3ae/plotly-5.9.0-py2.py3-none-any.whl (15.2 MB)
Collecting tenacity>=6.2.0
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/f2/a5/f86bc8d67c979020438c8559cc70cfe3a1643fd160d35e09c9cca6a09189/tenacity-8.0.1-py3-none-any.whl (24 kB)
Installing collected packages: tenacity, plotly
Successfully installed plotly-5.9.0 tenacity-8.0.1
# 加载必要库
import pandas as pd # 数据分析库
import numpy as np # 矩阵计算
import matplotlib.pyplot as plt # 画图

import plotly.express as px # 画图
from statsmodels.graphics.gofplots import qqplot # 统计模型
import seaborn as sns # 统计绘图
%matplotlib inline

# 避免产生报警告
import warnings 
warnings.filterwarnings('ignore')

数据加载

# 加载数据
## 参考链接:https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_boston.html#sklearn.datasets.load_boston
# data_url = "http://lib.stat.cmu.edu/datasets/boston" # 数据来源
# raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None) # 用pandas读csv文件
# data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
# target = raw_df.values[1::2, 2]
house = pd.read_csv("./data/boston.csv")
house.head() # 读前五行
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATMEDV
00.0063218.02.3100.5386.57565.24.09001296.015.3396.904.9824.0
10.027310.07.0700.4696.42178.94.96712242.017.8396.909.1421.6
20.027290.07.0700.4697.18561.14.96712242.017.8392.834.0334.7
30.032370.02.1800.4586.99845.86.06223222.018.7394.632.9433.4
40.069050.02.1800.4587.14754.26.06223222.018.7396.905.3336.2

特征:

  • CRIM:按城镇划分的人均犯罪率
  • ZN:划分为超过 25,000 平方英尺地块的住宅用地比例。
  • INDUS:每个城镇的非零售商业用地的比例
  • CHAS:Charles River 虚拟变量(如果区域以河流为界,则为 1;否则为 0)
  • NOX:一氧化氮浓度(每 1000 万分之一)[parts/10M]
  • RM:每户住宅的平均房间数
  • AGE:1940 年之前建造的自住单元的比例
  • DIS:到波士顿五个就业中心的加权距离
  • RAD:通往径向高速公路的指数
  • TAX:全每 10,000 美元的价值财产税率 [$/10k]
  • PTRATIO:按城镇划分的师生比例
  • B:等式 B = 1000 ( B k − 0.63 ) 2 B=1000(Bk - 0.63)^2 B=1000(Bk0.63)2 的结果,其中 B k Bk Bk 是城镇中黑人的比例
  • LSTAT:人口地位较低的百分比

标签:

MEDV:自住房屋的中位数价值 1000 美元 [k$]

数据概况

# 数据的规模
house.shape
(506, 14)

可以看到,本次我们用到的数据集总共有506条样本(行)也就是有506套房子的数据;同时有14列,其中13列是特征,也就是每个房子会有13个属性特征,例如:住宅的房间数、交通方便度等信息,最后一列是房子的标签——房价. 这个数据集主要的一个想法是,利用房子的13个属性特征,对房子的价格进行预测.

# 数据集的每一列的列名
house.columns
Index(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX',
       'PTRATIO', 'B', 'LSTAT', 'MEDV'],
      dtype='object')
# 对数据集数据的基本统计描述
# 这个命令非常便捷,呈现了这个数据集的基本统计分布,这是对每一列而言的,统计量包括:最大值,最小值,
house.describe()
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATMEDV
count506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000506.000000
mean3.61352411.36363611.1367790.0691700.5546956.28463468.5749013.7950439.549407408.23715418.455534356.67403212.65306322.532806
std8.60154523.3224536.8603530.2539940.1158780.70261728.1488612.1057108.707259168.5371162.16494691.2948647.1410629.197104
min0.0063200.0000000.4600000.0000000.3850003.5610002.9000001.1296001.000000187.00000012.6000000.3200001.7300005.000000
25%0.0820450.0000005.1900000.0000000.4490005.88550045.0250002.1001754.000000279.00000017.400000375.3775006.95000017.025000
50%0.2565100.0000009.6900000.0000000.5380006.20850077.5000003.2074505.000000330.00000019.050000391.44000011.36000021.200000
75%3.67708312.50000018.1000000.0000000.6240006.62350094.0750005.18842524.000000666.00000020.200000396.22500016.95500025.000000
max88.976200100.00000027.7400001.0000000.8710008.780000100.00000012.12650024.000000711.00000022.000000396.90000037.97000050.000000

df.describe()这个命令非常便捷,呈现了这个数据集的基本统计分布,这是对每一列数值型变量而言的,统计量包括:频数,均值,方差,最大值,最小值,中位数,25,75分位数.

从这个表我们呢可以大致获得一些处理思路,如:

  • 从第一行我们可以看到所有的count也就是数目统计,所有特征都是506等于样本数的,说明这个数据集至少是没有缺失值的.
  • 第一列CRIM是一个犯罪率,是一个在 [ 0 , 1 ] [0,1] [0,1]之间的实数,自然75%分位数,最大值都不可能超过1,所以显然这一列的数据是有异常的,后面我们需要进行一些处理.
  • 对于TAX,我们可以看到它的方差非常的大,因此在后续的处理过程中,我们可以采用一些降维的手段如:主成分分析(PCA)等.

有时候我们也可以通过df.info()去对数据集作一个简单的概述,更多的是看确实情况,以及变量的类型,通过变量类型分析数据处理的方法.

house.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 14 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   CRIM     506 non-null    float64
 1   ZN       506 non-null    float64
 2   INDUS    506 non-null    float64
 3   CHAS     506 non-null    int64  
 4   NOX      506 non-null    float64
 5   RM       506 non-null    float64
 6   AGE      506 non-null    float64
 7   DIS      506 non-null    float64
 8   RAD      506 non-null    int64  
 9   TAX      506 non-null    float64
 10  PTRATIO  506 non-null    float64
 11  B        506 non-null    float64
 12  LSTAT    506 non-null    float64
 13  MEDV     506 non-null    float64
dtypes: float64(12), int64(2)
memory usage: 55.5 KB

数据处理

缺失值处理

为了给大家演示缺失值的处理,我们在Boston房价数据集的基础上手动删除了一些数据,来模拟数据的缺失,存为以boston_null.csv文件,下面以该文件进行讲解.

null = pd.read_csv("./data/boston_null.csv")
null.head() # 读前五行
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATMEDV
00.0063218.02.3100.5386.57565.24.0900129615.3396.904.9824.0
10.027310.07.0700.4696.42178.94.9671224217.8396.909.1421.6
20.027290.07.0700.4697.18561.14.9671224217.8392.834.0334.7
30.032370.02.1800.4586.99845.86.0622322218.7394.632.9433.4
40.069050.02.180NaN7.14754.26.0622322218.7396.905.3336.2
null.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 14 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   CRIM     506 non-null    float64
 1   ZN       506 non-null    float64
 2   INDUS    506 non-null    float64
 3   CHAS     506 non-null    int64  
 4   NOX      504 non-null    float64
 5   RM       504 non-null    float64
 6   AGE      505 non-null    float64
 7   DIS      491 non-null    float64
 8   RAD      506 non-null    int64  
 9   TAX      506 non-null    int64  
 10  PTRATIO  506 non-null    float64
 11  B        453 non-null    float64
 12  LSTAT    506 non-null    float64
 13  MEDV     506 non-null    float64
dtypes: float64(11), int64(3)
memory usage: 55.5 KB

显然可以看到样本总数为506,非空值不等于506的即为含有缺失值的特征.

对于发现缺失值有几种方式:

  1. 直接对null进行计数
null.isnull().sum()
CRIM        0
ZN          0
INDUS       0
CHAS        0
NOX         2
RM          2
AGE         1
DIS        15
RAD         0
TAX         0
PTRATIO     0
B          53
LSTAT       0
MEDV        0
dtype: int64
  1. 缺失值可视化
# 缺失值可视化
missing = null.isnull().sum()
missing = missing[missing > 0] # 筛选出有缺失值(大于0)的特征
missing.sort_values(inplace = True) # 排序
missing.plot.bar() # 调用pandas内置的条形图绘制
<AxesSubplot:>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3BZNsOt-1657979538293)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_20_1.png)]

对于缺失值我们还可以采用热力图进行可视化,

sns.heatmap(null.isnull())
<AxesSubplot:>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CN05MPuK-1657979538294)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_22_1.png)]

这种方式还可以帮助我们看出缺失的位置,如果有些样本出现大量特征缺失的情况,在样本足够大的情况下也可以考虑删除.

  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']).sort_values(by=['missing rate'], ascending= False)
Featuresmissing rate
11B10.474308
7DIS2.964427
4NOX0.395257
5RM0.395257
6AGE0.197628
0CRIM0.000000
1ZN0.000000
2INDUS0.000000
3CHAS0.000000
8RAD0.000000
9TAX0.000000
10PTRATIO0.000000
12LSTAT0.000000
13MEDV0.000000
# 不使用for循环
lst = pd.DataFrame(null.isnull().sum() * 100/ null.shape[0]).reset_index()
lst.columns = ['features', 'missing rate']
lst.sort_values(by=['missing rate'], ascending=False)
featuresmissing rate
11B10.474308
7DIS2.964427
4NOX0.395257
5RM0.395257
6AGE0.197628
0CRIM0.000000
1ZN0.000000
2INDUS0.000000
3CHAS0.000000
8RAD0.000000
9TAX0.000000
10PTRATIO0.000000
12LSTAT0.000000
13MEDV0.000000

从上表,我们统计了每一个特征的缺失比例,一般来说,如果缺失值超过95%的特征,我们会考虑删除它,因为意义不大. 此外,另一类特征也可以考虑删除,那就是方差特别小,或者说取值唯一的特征是没有意义的. 举个例子,如果在一次考试中,大家的数学成绩都是100分,那么其实我们是没有办法通过数学成绩来区分每位同学的能力的,也就是说数学这门课没有区分性,所以不具有分析的意义,可以删掉.

在Pandas中,我们可以采用df.nunique()来统计每个每个特征会有多少个不同取值.

null.nunique()
CRIM       504
ZN          26
INDUS       76
CHAS         2
NOX         81
RM         445
AGE        356
DIS        401
RAD          9
TAX         66
PTRATIO     46
B          326
LSTAT      455
MEDV       229
dtype: int64

为了更完美的呈现,我们将特征的唯一值个数一并加入到上面的统计表中,并且封装成函数,方便后续直接使用.

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
df_stats(null)
Featuremissing nummissing rateunique num
0CRIM00.000000504
1ZN00.00000026
2INDUS00.00000076
3CHAS00.0000002
4NOX20.39525781
5RM20.395257445
6AGE10.197628356
7DIS152.964427401
8RAD00.0000009
9TAX00.00000066
10PTRATIO00.00000046
11B5310.474308326
12LSTAT00.000000455
13MEDV00.000000229
# 不封装函数以及使用for循环
lst2 = pd.DataFrame(null.isnull().sum()).assign(missing_rate = null.isnull().sum()*100/ null.shape[0]).assign(unique_sum=null.nunique()).reset_index()
lst2.columns = ['features', 'missing num','missing rate','unique num']
lst2
featuresmissing nummissing rateunique num
0CRIM00.000000504
1ZN00.00000026
2INDUS00.00000076
3CHAS00.0000002
4NOX20.39525781
5RM20.395257445
6AGE10.197628356
7DIS152.964427401
8RAD00.0000009
9TAX00.00000066
10PTRATIO00.00000046
11B5310.474308326
12LSTAT00.000000455
13MEDV00.000000229

这下一目了然了!在这里我们呢简单粗暴的用均值填充,后面有机会我们会总结一下缺失值的处理方式.

null = null.fillna(null.mean()) # 用均值填充
sns.heatmap(null.isnull())
<AxesSubplot:>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYGkAe5i-1657979538294)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_35_1.png)]

可见现在已经没有缺失值了.

变量分析

在变量分析中,我们延续了在概率论与数理统计中的研究思想.在概率论与数理统计中,我们首先研究了单变量随机变量的性质与特征刻画,研究透了之后,我们转而研究随机变量之间的关系. 在数据分析中亦是如此,我们会先分析一个变量的情况,包括但不限于:数据类型、分布情况、是否有离群值······;研究完单变量之后,转而研究变量之间的关系:相关性分析、分组统计···

单变量分析
  1. 分析标签

首先是对于数据集标签的分析,你要弄清楚所拿到的任务要求你做什么?说白了就是明白任务的需求,简单来说数据挖掘任务主要分为两类:回归分类.回归是指标签值是连续值的任务,例如我们用到的这个数据集,要求预测房价信息,房价是一个连续的变量,因此这样的任务属于回归问题(Regression).数据科学中另外一个经典的数据集是——鸢尾花(Iris)数据集,它收集了三种鸢尾花的萼片长度,宽度等信息,要求你根据这些信息预测鸢尾花属于哪种类别,因此它的标签就是1,2,3,代表三类鸢尾花. 像这种标签是离散的数据分析任务就属于分类任务.

house["MEDV"].nunique()
229

从标签的取值上,我们可以大致判断出本次任务属于回归型任务,因为它的取值个数比较多. 但这不是判断的标准,只是一个判断的参考,主要还是要分析题目的任务需求.

# 打印标签的统计信息
house["MEDV"].describe()
count    506.000000
mean      22.532806
std        9.197104
min        5.000000
25%       17.025000
50%       21.200000
75%       25.000000
max       50.000000
Name: MEDV, dtype: float64

从标签列的统计信息我们可以大致看出,其分布还算比较正常,没有太大的方差(方差太大说明数据不太可能是正态的,因为够不集中). 也没有出现我们在前面提到的,在定义域之外的值出现(对于是否有离群值后面我们需要通过箱线图或者散点图等可视化分析后才能得知). 接下来我们来画出其密度分布图,看看标签取值的总体分布情况:

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,
            )
<AxesSubplot:xlabel='MEDV', ylabel='Density'>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XEhrCRY-1657979538294)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_43_1.png)]

我们看到整个分布稍微有一点右偏,因此在实战中,我们可以采用对数化的方法,让标签分布接近正态.

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,
            )
<AxesSubplot:xlabel='MEDV', ylabel='Density'>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9k45pp5r-1657979538295)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_45_1.png)]

这样的变换之后,我们到时候可以将取值小于1.5的取值视为异常值,将其抛弃掉.

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

  1. 离散型变量

对于类别型变量而言,一般观察其分布我们采用柱状图的方式.

df_stats(null)
Featuremissing nummissing rateunique num
0CRIM00.0504
1ZN00.026
2INDUS00.076
3CHAS00.02
4NOX00.082
5RM00.0446
6AGE00.0357
7DIS00.0402
8RAD00.09
9TAX00.066
10PTRATIO00.046
11B00.0327
12LSTAT00.0455
13MEDV00.0229

从这个表的最后一列可以看出CHAS,RAD这样的变量的取值比较符合分类变量的特征,同时阅读特征说明亦可得以确认,我们将其柱状图进行刻画

house[['CHAS', 'RAD']].hist()
array([[<AxesSubplot:title={'center':'CHAS'}>,
        <AxesSubplot:title={'center':'RAD'}>]], dtype=object)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6QgeXkb-1657979538295)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_51_1.png)]

当然类别型变量更大的作用是用于后续在特征工程中,用于进行特征交叉,继续特征的增强,挖掘更深层次的潜在信息.

  1. 连续型变量

同上,我们先绘制出连续型变量的分布情况.

house[['CRIM', 'ZN', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']].hist(figsize = (10, 15), bins = 100)
array([[<AxesSubplot:title={'center':'CRIM'}>,
        <AxesSubplot:title={'center':'ZN'}>,
        <AxesSubplot:title={'center':'INDUS'}>],
       [<AxesSubplot:title={'center':'NOX'}>,
        <AxesSubplot:title={'center':'RM'}>,
        <AxesSubplot:title={'center':'AGE'}>],
       [<AxesSubplot:title={'center':'DIS'}>,
        <AxesSubplot:title={'center':'TAX'}>,
        <AxesSubplot:title={'center':'PTRATIO'}>],
       [<AxesSubplot:title={'center':'B'}>,
        <AxesSubplot:title={'center':'LSTAT'}>, <AxesSubplot:>]],
      dtype=object)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Noqpz5JE-1657979538295)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_54_1.png)]

从上面的图像可以看出,有些变量实际上是有相似(A高B高)的分布情况的,或者相反的分布情况的(A高B低),这样的性质称为变量之间的相关性. 相关性一般有两种情况:

  • 正相关:如果特征A的增加(减少)会导致特征B增加(减少),即特征A与特征B变化趋势相同.
  • 负相关:如果特征A的增加(减少)会导致特征B减少(增加),即特征A与特征B变化趋势相反.

当两个变量呈正相关或者负相关时,我们称两个变量是相关的. 如果两个变量有较强的相关性时,如果将这些变量一起用的话,会有很多是冗余的. 因为这样变量之间有较强的相关性,那么变量与变量之间是可以相互表示的,所以在建模时,需要尽可能消除特征之间的共线性,也就是尽量使用相关性比较小的特征,这样可以减少训练时间,使得模型的学习效果更好.那么下面我们讲讲如何去刻画特征间的相关性.

多变量分析

相关性

首先我们先从可视化的角度来研究变量之间的相关性,对于可视化而言,最简单的方式就是将两个特征对应的坐标点在坐标系下描出来,研究他们的变化趋势,也就是说每一个点的坐标试一个二元组 ( x A ( i ) , y B ( i ) ) (x^{(i)}_A, y^{(i)}_B) (xA(i),yB(i)),其中 x A ( i ) x^{(i)}_A xA(i)表示第 i i i个样本,特征 A A A的取值; y B ( i ) y^{(i)}_B yB(i)表示第 i i i个样本,特征 B B B的取值. 我们用seaborn库可以很直观的任意两个特征之间的散点图.

⚠️ 如下的代码,密集恐惧症患者谨慎运行!

# 任意两个变量间的相关性散点图
sns.set()
cols = house.columns # 罗列出数据集的所有列
sns.pairplot(house[cols], size = 2.5) # 成对画出任意两列的散点图
plt.show() # 显示函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-agHx6UVl-1657979538295)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_57_0.png)]

大家如果第一次见到上面的图,可能会被震惊到,并且一时间不知道该看哪里.

  • 首先,我们先看图像呈“对角线分布的”,即/或者\这两种类型分布的图像,这样的图像说明这两个变量间有较强的相关性,是可以被消除共线性的“嫌疑对象”. 如图中的DISAGE,尽管他不是严格的直线分布,但至少其分布呈现带状;
sns.pairplot(house[["AGE", "DIS"]], size = 3) # 成对画出任意两列的散点图, size是点的大小
<seaborn.axisgrid.PairGrid at 0x13b4cf09070>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rlEMVrXK-1657979538296)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_59_1.png)]

  • 其次,我们可以看那些趋势比较明显单调,但是不太像直线,而是类似于“对数函数”的图像,例如图中的DISNOX,因为这类图像,只要做一下对数化,马上就可以得到类似直线的效果,那么也是可以被处理的.
sns.pairplot(np.log(house[["NOX", "DIS"]]), size = 3) # 成对画出任意两列的散点图, size是点的大小
<seaborn.axisgrid.PairGrid at 0x13b4cdfebb0>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5wWJa49l-1657979538296)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_61_1.png)]

  • 我们还需要关注最后一行(列)与其他行(列)的关系,也就是特征与标签的关系,如果这两者出现了较强相关性,那么这些特征我们需要留意,因为这些特征对标签有着比较直接的关系,例如RMLSTAT.
sns.pairplot(np.log(house[["RM", "LSTAT", "MEDV"]]), size = 3) # 成对画出任意两列的散点图, size是点的大小
<seaborn.axisgrid.PairGrid at 0x13b4eb4d340>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkNPsLMJ-1657979538296)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_63_1.png)]

但是仅仅从图像上去看还是太过于感性了,只能大致的判断出趋势,那么有没有办法刻画“两个特征非常相关”,又或者“两个变量没那么相关”呢?答案是肯定的,大家可以回想一下我们在《概率论与数理统计》中学到的刻画两个随机变量的相关性的方法:协方差相关系数.

协方差 : σ i j = cov ⁡ ( X i , X j ) = E [ ( X i − μ i ) ( X j − μ j ) ] 协方差: \sigma_{ij}=\operatorname{cov}\left(X_{i}, X_{j}\right)= E \left[\left(X_{i}-\mu_{i}\right)\left(X_{j}-\mu_{j}\right)\right] 协方差:σij=cov(Xi,Xj)=E[(Xiμi)(Xjμj)]

其中, X i , X j X_{i},X_{j} Xi,Xj是第 i , j i, j i,j个随机变量,因此我们可以诱导出协方差矩阵
Σ = ( σ i j ) \Sigma = (\sigma_{ij}) Σ=(σij)

回顾协方差的定义,若变量 X i X_{i} Xi的较大值主要与另一个变量 X j X_{j} Xj的较大值相对应,而两者的较小值也相对应,则可称两变量倾向于表现出相似的行为,协方差为正。在相反的情况下,当一个变量的较大值主要对应于另一个变量的较小值时,则两变量倾向于表现出相反的行为,协方差为负。即协方差之正负号显示著变量的相关性。

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

# 对数据集直接计算协方差
# 注意 只有数值型变量才能算,类别或文本需要编码后才能计算
house.cov()
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATMEDV
CRIM73.986578-40.21595623.992339-0.1221090.419594-1.32503885.405322-6.87672246.847761844.8215385.399331-302.38181627.986168-30.718508
ZN-40.215956543.936814-85.412648-0.252925-1.3961485.112513-373.90154832.629304-63.348695-1236.453735-19.776571373.721402-68.78303777.315176
INDUS23.992339-85.41264847.0644420.1096690.607074-1.887957124.513903-10.22809735.549971833.3602905.692104-223.57975629.580270-30.520823
CHAS-0.122109-0.2529250.1096690.0645130.0026840.0162850.618571-0.053043-0.016296-1.523367-0.0668191.131325-0.0978160.409409
NOX0.419594-1.3961480.6070740.0026840.013428-0.0246032.385927-0.1876960.61692913.0462860.047397-4.0205700.488946-0.455412
RM-1.3250385.112513-1.8879570.016285-0.0246030.493671-4.7519290.303663-1.283815-34.583448-0.5407638.215006-3.0797414.493446
AGE85.405322-373.901548124.5139030.6185712.385927-4.751929792.358399-44.329379111.7708462402.69012215.936921-702.940328121.077725-97.589017
DIS-6.87672232.629304-10.228097-0.053043-0.1876960.303663-44.3293794.434015-9.068252-189.664592-1.05977556.040356-7.4733294.840229
RAD46.847761-63.34869535.549971-0.0162960.616929-1.283815111.770846-9.06825275.8163661335.7565778.760716-353.27621930.385442-30.561228
TAX844.821538-1236.453735833.360290-1.52336713.046286-34.5834482402.690122-189.6645921335.75657728404.759488168.153141-6797.911215654.714520-726.255716
PTRATIO5.399331-19.7765715.692104-0.0668190.047397-0.54076315.936921-1.0597758.760716168.1531414.686989-35.0595275.782729-10.110657
B-302.381816373.721402-223.5797561.131325-4.0205708.215006-702.94032856.040356-353.276219-6797.911215-35.0595278334.752263-238.667516279.989834
LSTAT27.986168-68.78303729.580270-0.0978160.488946-3.079741121.077725-7.47332930.385442654.7145205.782729-238.66751650.994760-48.447538
MEDV-30.71850877.315176-30.5208230.409409-0.4554124.493446-97.5890174.840229-30.561228-726.255716-10.110657279.989834-48.44753884.586724

但是协方差只能看出正相关和负相关,其大小并没有意义,这是因为每个特征的量纲不同,直接乘积后求期望,所得到的量纲又是不尽相同,因此我们需要对其做“无量纲化”处理,也就有了相关系数
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]}} r(X,Y)=Var[X]Var[Y] Cov(X,Y)
其中, Cov ⁡ ( X , Y ) \operatorname{Cov}( X , Y ) Cov(X,Y) X X X Y Y Y 的协方差, Var ⁡ [ X ] \operatorname{Var}[ X ] Var[X] X X X 的方差, Var ⁡ [ Y ] \operatorname{Var}[Y] Var[Y] Y Y Y 的方差.

  1. ∣ r ( X , Y ) ∣ ≤ 1 \left|r(X, Y)\right| \leq 1 r(X,Y)1
  2. ∣ r ( X , Y ) ∣ = 1 \left|r(X, Y)\right|=1 r(X,Y)=1 的充要条件是, 存在常数 a a a, b b b, 使得 P { Y = a + b X } = 1 P\{Y=a+b X\}=1 P{Y=a+bX}=1

也就是说相关系数定量地刻画了 X X X Y Y Y 的相关程度,即 ∣ r ( X , Y ) ∣ \left|r(X, Y)\right| r(X,Y) 越大,相关程度越大; ∣ r ( X , Y ) ∣ = 0 \left|r(X, Y)\right|=0 r(X,Y)=0 对应相关程度最低; X X X Y Y Y 完全相关的含义是在概率为1的意义下存在线性关系, 于是 ∣ r ( X , Y ) ∣ \left|r(X, Y)\right| r(X,Y) 是一个可以表征 X X X Y Y Y 之间线性关系紧密程度量。当 ∣ r ( X , Y ) ∣ \left|r(X, Y)\right| r(X,Y) 较大时, 通常说 X X X Y Y Y 相关程度较好;当 ∣ r ( X , Y ) ∣ \left|r(X, Y)\right| r(X,Y) 较小时, 通常说 X X X Y Y Y 相关程度较差; 当 X X X Y Y Y不相关, 通常认为 X X X Y Y Y 之间不存在线性关系, 但并不能排除 X X X Y Y Y 之间可能存在其他关系。

# 请你尝试实现相关系数的计算
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[0,1] #--------- # 填写相应的索引形式
    
# (np.cov(house['AGE'], house['DIS'])/ ((np.std(house['AGE']) * np.std(house['DIS']))))[0,1]
Corr(house['AGE'], house['DIS'])
-0.7493614924347081
# 直接计算相关性系数
Corr_Matrix = house.corr()
Corr_Matrix
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATMEDV
CRIM1.000000-0.2004690.406583-0.0558920.420972-0.2192470.352734-0.3796700.6255050.5827640.289946-0.3850640.455621-0.388305
ZN-0.2004691.000000-0.533828-0.042697-0.5166040.311991-0.5695370.664408-0.311948-0.314563-0.3916790.175520-0.4129950.360445
INDUS0.406583-0.5338281.0000000.0629380.763651-0.3916760.644779-0.7080270.5951290.7207600.383248-0.3569770.603800-0.483725
CHAS-0.055892-0.0426970.0629381.0000000.0912030.0912510.086518-0.099176-0.007368-0.035587-0.1215150.048788-0.0539290.175260
NOX0.420972-0.5166040.7636510.0912031.000000-0.3021880.731470-0.7692300.6114410.6680230.188933-0.3800510.590879-0.427321
RM-0.2192470.311991-0.3916760.091251-0.3021881.000000-0.2402650.205246-0.209847-0.292048-0.3555010.128069-0.6138080.695360
AGE0.352734-0.5695370.6447790.0865180.731470-0.2402651.000000-0.7478810.4560220.5064560.261515-0.2735340.602339-0.376955
DIS-0.3796700.664408-0.708027-0.099176-0.7692300.205246-0.7478811.000000-0.494588-0.534432-0.2324710.291512-0.4969960.249929
RAD0.625505-0.3119480.595129-0.0073680.611441-0.2098470.456022-0.4945881.0000000.9102280.464741-0.4444130.488676-0.381626
TAX0.582764-0.3145630.720760-0.0355870.668023-0.2920480.506456-0.5344320.9102281.0000000.460853-0.4418080.543993-0.468536
PTRATIO0.289946-0.3916790.383248-0.1215150.188933-0.3555010.261515-0.2324710.4647410.4608531.000000-0.1773830.374044-0.507787
B-0.3850640.175520-0.3569770.048788-0.3800510.128069-0.2735340.291512-0.444413-0.441808-0.1773831.000000-0.3660870.333461
LSTAT0.455621-0.4129950.603800-0.0539290.590879-0.6138080.602339-0.4969960.4886760.5439930.374044-0.3660871.000000-0.737663
MEDV-0.3883050.360445-0.4837250.175260-0.4273210.695360-0.3769550.249929-0.381626-0.468536-0.5077870.333461-0.7376631.000000

更进一步,我们可以对上面的相关性系数,采用热力图进行可视化,这样就会更加直观.

#correlation matrix
f, ax = plt.subplots(figsize=(12, 9)) # 设置画布
sns.heatmap(Corr_Matrix, vmax=.8, square=True # 画热力图
             , annot=True # s是否显示数值
           )
<AxesSubplot:>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vmHazPRw-1657979538297)(EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_files/EDA%E5%88%9D%E4%BD%93%E9%AA%8C–%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E5%88%86%E6%9E%90_71_1.png)]

参考链接

  • kaggle可视化
  • kaggle方案
  • plotly安装
  • Kaggle高分方案
    /td> PTRATIO 0.289946 -0.391679 0.383248 -0.121515 0.188933 -0.355501 0.261515 -0.232471 0.464741 0.460853 1.000000 -0.177383 0.374044 -0.507787 B -0.385064 0.175520 -0.356977 0.048788 -0.380051 0.128069 -0.273534 0.291512 -0.444413 -0.441808 -0.177383 1.000000 -0.366087 0.333461 LSTAT 0.455621 -0.412995 0.603800 -0.053929 0.590879 -0.613808 0.602339 -0.496996 0.488676 0.543993 0.374044 -0.366087 1.000000 -0.737663 MEDV -0.388305 0.360445 -0.483725 0.175260 -0.427321 0.695360 -0.376955 0.249929 -0.381626 -0.468536 -0.507787 0.333461 -0.737663 1.000000

更进一步,我们可以对上面的相关性系数,采用热力图进行可视化,这样就会更加直观.

#correlation matrix
f, ax = plt.subplots(figsize=(12, 9)) # 设置画布
sns.heatmap(Corr_Matrix, vmax=.8, square=True # 画热力图
             , annot=True # s是否显示数值
           )
<AxesSubplot:>

[外链图片转存中…(img-vmHazPRw-1657979538297)]

参考链接

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值