做数据分析时,对数据越了解,越有助于我们去分析,本文介绍如何使用Pandas探索数据。

1. Pandas简介

Pandas是Python中一个高性能库,提供了非常易用的数据结构以及丰富的函数工具用来做数据分析,所以该库在数据分析领域用的非常广泛。使用Pandas的关键点在于理解Pandas的两个核心数据结构:SeriesDataFrame

1.1 Series

Series表示带有标签的一维数组(one-dimensional labeled array):

  • 数组内的元素可以是任意类型;
  • label称为index,index可以不唯一,但必须是可哈希的类型(hashable type)。

构造Series结构最常用的方式就是使用pandas.Series类,参数及说明如下:

pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)
  • data:数组中的数据,参数可以是数组、字典或标量值
  • index:标签或索引,可以重复,但必须是可哈希类型。参数可以是数组或一维的Index类型,长度必须和数据的长度相同;如果数据是字典类型,又显式的传了index,会覆盖字典的key;默认的index是(0, 1, 2, ..., len(data)-1)
  • dtype:numpy.dtype或者None,如果传None的话,会自动检测类型。
  • copy:布尔值,是否拷贝输入的值,默认是False。

下面列举几个创建Series的例子。

In [1]: import pandas as pd
In [2]: import numpy as np

# 使用数组创建Series,并指定index
In [3]: arr = np.array([1,2,3,4,5])
In [4]: s = pd.Series(arr, index=['a','b','c','d','a'])
In [5]: s
Out[5]:
a    1
b    2
c    3
d    4
a    5
dtype: int64

In [6]: s.index
Out[6]: Index(['a', 'b', 'c', 'd', 'a'], dtype='object')

# 使用数组创建Series,且使用默认的index
In [7]: pd.Series(np.random.randn(5))
Out[7]:
0   -1.316523
1   -1.211854
2   -0.532214
3   -0.143331
4    0.335070
dtype: float64

# 通过字典创建Series
In [8]: d = {'b':1, 'a':0, 'c':2}
In [9]: pd.Series(d)
Out[9]:
a    0
b    1
c    2
dtype: int64

# 使用标量创建Series,要注意此时标量重复len(index)次
In [10]: pd.Series(5., index=['a','b','c','d','e'])
Out[10]:
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64

在时间序列预测中Series数据结构将非常有用。

1.2 DataFrame

DataFrame和Series类似,但DataFrame是二维的,非常类似于关系型数据库里面的表。我们一般把列标签依旧称为index,而把行标签称为Column,对应的也有着两个参数去指定行列标签。DataFrame对象的构造方法和Series极其类似,参数情况如下:

pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)

这里不再赘述了,看以下一些例子:

In [15]: d = { 'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
    ...:       'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
    ...:

In [16]: d
Out[16]:
{'one': a    1.0
 b    2.0
 c    3.0
 dtype: float64, 'two': a    1.0
 b    2.0
 c    3.0
 d    4.0
 dtype: float64}

In [17]: df = pd.DataFrame(d)

In [18]: df
Out[18]:
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In [19]: pd.DataFrame(d, index=['d', 'b', 'a'])
Out[19]:
   one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In [20]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[20]:
   two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN

In [21]: df.index
Out[21]: Index(['a', 'b', 'c', 'd'], dtype='object')

In [22]: df.columns
Out[22]: Index(['one', 'two'], dtype='object')

注:在Pandas中,缺失值一般用NaN表示。

会创建Series和Pandas对象只是学习Pandas的第一步,其实Pandas更加强大的地方在于它在这两个核心数据结构之上提供了非常丰富的函数和方法,让我们可以更专注于数据分析本身。本文的重点不是介绍Pandas,所以Pandas的介绍到此为止,有兴趣的可以看起官方文档:http://pandas.pydata.org/pandas-docs/stable/index.html。里面有一个10分钟的入门教程,对于只想快速了解一下的同学是个非常不错的选择。

下面我们言归正传,看一下如何利用Pandas提供的各种工具来探索我们的数据。

2. 探索数据

数据说明:本次示例所用的数据来自于https://archive.ics.uci.edu/ml/datasets/bank+marketing,是葡萄牙银行做的一次电话调查,旨在判断用户对于行方定期存储产品的认购情况。数据包含了很多列,有些列的值是标量型的,有些是数值型的。本文的重点在于如何利用Pandas工具去探索数据(分布)情况,所以列的含义不是那么重要,这里就不解释了,有兴趣的可以去上面给出的链接处查看,有详细的说明。

导入相关的包并读入数据:

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: import matplotlib.pyplot as plt

In [4]: df = pd.read_csv('SampleData/banking.csv')

2.1 统计信息

1,查看数据基本信息。

# head默认显示5条数据,可以指定显示几条
In [5]: df.head()
Out[5]:
   age          job  marital          education  default housing loan  \
0   44  blue-collar  married           basic.4y  unknown     yes   no
1   53   technician  married            unknown       no      no   no
2   28   management   single  university.degree       no     yes   no
3   39     services  married        high.school       no      no   no
4   55      retired  married           basic.4y       no     yes   no

    contact month day_of_week ...  campaign  pdays  previous     poutcome  \
0  cellular   aug         thu ...         1    999         0  nonexistent
1  cellular   nov         fri ...         1    999         0  nonexistent
2  cellular   jun         thu ...         3      6         2      success
3  cellular   apr         fri ...         2    999         0  nonexistent
4  cellular   aug         fri ...         1      3         1      success

  emp_var_rate  cons_price_idx  cons_conf_idx  euribor3m  nr_employed  y
0          1.4          93.444          -36.1      4.963       5228.1  0
1         -0.1          93.200          -42.0      4.021       5195.8  0
2         -1.7          94.055          -39.8      0.729       4991.6  1
3         -1.8          93.075          -47.1      1.405       5099.1  0
4         -2.9          92.201          -31.4      0.869       5076.2  1

[5 rows x 21 columns]

# 查看数据维度
In [6]: df.shape
Out[6]: (41188, 21)

# 查看数据类型
In [7]: df.dtypes
Out[7]:
age                 int64
job                object
marital            object
education          object
default            object
housing            object
loan               object
contact            object
month              object
day_of_week        object
duration            int64
campaign            int64
pdays               int64
previous            int64
poutcome           object
emp_var_rate      float64
cons_price_idx    float64
cons_conf_idx     float64
euribor3m         float64
nr_employed       float64
y                   int64
dtype: object

通过查看基本信息,我们可以查看到一些示例数据,以及数据总条数、维度数,各个维度的类型等,这些都是我们探索数据的第一步。

2,通过Pandas的describe方法查看一些汇总信息,探索各个维度值的分布情况:

In [11]: df.describe()
Out[11]:
               age      duration      campaign         pdays      previous  \
count  41188.00000  41188.000000  41188.000000  41188.000000  41188.000000
mean      40.02406    258.285010      2.567593    962.475454      0.172963
std       10.42125    259.279249      2.770014    186.910907      0.494901
min       17.00000      0.000000      1.000000      0.000000      0.000000
25%       32.00000    102.000000      1.000000    999.000000      0.000000
50%       38.00000    180.000000      2.000000    999.000000      0.000000
75%       47.00000    319.000000      3.000000    999.000000      0.000000
max       98.00000   4918.000000     56.000000    999.000000      7.000000

       emp_var_rate  cons_price_idx  cons_conf_idx     euribor3m  \
count  41188.000000    41188.000000   41188.000000  41188.000000
mean       0.081886       93.575664     -40.502600      3.621291
std        1.570960        0.578840       4.628198      1.734447
min       -3.400000       92.201000     -50.800000      0.634000
25%       -1.800000       93.075000     -42.700000      1.344000
50%        1.100000       93.749000     -41.800000      4.857000
75%        1.400000       93.994000     -36.400000      4.961000
max        1.400000       94.767000     -26.900000      5.045000

        nr_employed             y
count  41188.000000  41188.000000
mean    5167.035911      0.112654
std       72.251528      0.316173
min     4963.600000      0.000000
25%     5099.100000      0.000000
50%     5191.000000      0.000000
75%     5228.100000      0.000000
max     5228.100000      1.000000

可以看到该方法默认只计算了数值型维度的5个方面的信息:

  • count:总数
  • mean:均值
  • std:标准差
  • min:最小值
  • 25%:第一四分位数或较小四分位数(Q1):等于该样本中所有数值由小到大排列后第25%的数字。
  • 50%:第二四分位数或中位数(Q2):等于该样本中所有数值由小到大排列后第50%的数字。
  • 75%:第三四分位数或较大四分位数(Q3):等于该样本中所有数值由小到大排列后第75%的数字。
  • max:最大值。

当然,对于数值型数据describe方法会统计上面5个方面的信息,对于标称型数据则统计另外4个方面的信息:

In [13]: df.describe(exclude=[np.number])
Out[13]:
           job  marital          education default housing   loan   contact  \
count    41188    41188              41188   41188   41188  41188     41188
unique      12        4                  8       3       3      3         2
top     admin.  married  university.degree      no     yes     no  cellular
freq     10422    24928              12168   32588   21576  33950     26144

        month day_of_week     poutcome
count   41188       41188        41188
unique     10           5            3
top       may         thu  nonexistent
freq    13769        8623        35563

这4方面的信息包括:

  • count:总数
  • unique:去重后的个数
  • top:出现频率最高的值
  • freq:出现最高频率的值的频率

对于Timestamp类型还会有firstlast两个信息。

3,通过groupby查看各个标称型维度值的分布情况。describe方法对于探索数值型数据的分布非常有用,但对于标称型数据虽然也有统计,但还不够。往往我们更多的使用groupby方法,该方法与SQL中的groupby类似,这里举几个例子:

In [14]: df.groupby('education').size()
Out[14]:
education
basic.4y                4176
basic.6y                2292
basic.9y                6045
high.school             9515
illiterate                18
professional.course     5243
university.degree      12168
unknown                 1731
dtype: int64

In [15]: df.groupby('y').size()
Out[15]:
y
0    36548
1     4640
dtype: int64

4,有很多算法要求所选取的维度之间要没有相关性,所以探索维度之间的相关性也非常重要。Pandas提供了corr方法可以计算相关系数(默认计算皮尔逊相关系数):

In [16]: df.corr()
Out[16]:
                     age  duration  campaign     pdays  previous  \
age             1.000000 -0.000866  0.004594 -0.034369  0.024365
duration       -0.000866  1.000000 -0.071699 -0.047577  0.020640
campaign        0.004594 -0.071699  1.000000  0.052584 -0.079141
pdays          -0.034369 -0.047577  0.052584  1.000000 -0.587514
previous        0.024365  0.020640 -0.079141 -0.587514  1.000000
emp_var_rate   -0.000371 -0.027968  0.150754  0.271004 -0.420489
cons_price_idx  0.000857  0.005312  0.127836  0.078889 -0.203130
cons_conf_idx   0.129372 -0.008173 -0.013733 -0.091342 -0.050936
euribor3m       0.010767 -0.032897  0.135133  0.296899 -0.454494
nr_employed    -0.017725 -0.044703  0.144095  0.372605 -0.501333
y               0.030399  0.405274 -0.066357 -0.324914  0.230181

                emp_var_rate  cons_price_idx  cons_conf_idx  euribor3m  \
age                -0.000371        0.000857       0.129372   0.010767
duration           -0.027968        0.005312      -0.008173  -0.032897
campaign            0.150754        0.127836      -0.013733   0.135133
pdays               0.271004        0.078889      -0.091342   0.296899
previous           -0.420489       -0.203130      -0.050936  -0.454494
emp_var_rate        1.000000        0.775334       0.196041   0.972245
cons_price_idx      0.775334        1.000000       0.058986   0.688230
cons_conf_idx       0.196041        0.058986       1.000000   0.277686
euribor3m           0.972245        0.688230       0.277686   1.000000
nr_employed         0.906970        0.522034       0.100513   0.945154
y                  -0.298334       -0.136211       0.054878  -0.307771

                nr_employed         y
age               -0.017725  0.030399
duration          -0.044703  0.405274
campaign           0.144095 -0.066357
pdays              0.372605 -0.324914
previous          -0.501333  0.230181
emp_var_rate       0.906970 -0.298334
cons_price_idx     0.522034 -0.136211
cons_conf_idx      0.100513  0.054878
euribor3m          0.945154 -0.307771
nr_employed        1.000000 -0.354678
y                 -0.354678  1.000000

皮尔逊相关系数取值范围为[-1, 1],-1表示(线性)负相关,0表示(线性)无关,1表示(线性)正相关。通过这个(对角)矩阵,我们可以很容易的看出各个属性间的相关性。

5,pandas还提供了skew来计算偏度(skew):衡量实数随机变量概率分布的不对称性。偏度的值可以为正,可以为负或者甚至是无法定义。在数量上,偏度为负(左偏态或负偏态,Negative Skew)就意味着在概率密度函数左侧的尾部比右侧的长,绝大多数的值(包括中位数在内)位于平均值的右侧。偏度为正(右偏态或正偏态,Positive Skew)就意味着在概率密度函数右侧的尾部比左侧的长,绝大多数的值(但不一定包括中位数)位于平均值的左侧。如图:

偏度

偏度为零就表示数值相对均匀地分布在平均值的两侧,但不一定意味着其为对称分布。如果分布对称,那么平均值=中位数,偏度为零(此外,如果分布为单峰分布,那么平均值=中位数=众数)。

In [17]: df.skew()
Out[17]:
age               0.784697
duration          3.263141
campaign          4.762507
pdays            -4.922190
previous          3.832042
emp_var_rate     -0.724096
cons_price_idx   -0.230888
cons_conf_idx     0.303180
euribor3m        -0.709188
nr_employed      -1.044262
y                 2.450330
dtype: float64

2.2 可视化

统计信息虽然非常精确,但看上去并不是非常直观,我们可以通过一些可视化的手段来非常直观的探索数据分布。

1,直方图(histogram):

In [20]: %matplotlib
Using matplotlib backend: MacOSX

In [21]: df.hist(figsize=(12, 8))

直方图

2,密度图(density plot):

In [22]: df.plot(kind='density', subplots=True, figsize=(12,8), layout=(3,4), sharex=False)

密度图

3,箱型图(boxplots):

In [23]: df.plot(kind='box', subplots=True, figsize=(12,8), layout=(3,4), sharex=False, sharey=False)

箱型图

箱型图的含义参见:箱型图介绍

4,相关系数矩阵图:

corr = df.corr()
figure = plt.figure(figsize=(12,8))
ax = figure.add_subplot(111)
cax = ax.matshow(corr, vmin=-1, vmax=1)
figure.colorbar(cax)
plt.show()  # 0~10,依次对应age~y

关系矩阵图

5,通过散点矩阵图(scatter matrix)查看各维度相关性:

from pandas.plotting import scatter_matrix

scatter_matrix(df, figsize=(18, 12))

散点矩阵图

关于scatter matrix需要解释一下,和上面的相关系数矩阵图类似,该图也描述了各维度间的相关性。而对角线上是各个维度自己与自己的相关性,显然都是完全相关的,也就是一条直线(皮尔逊相关系数为1),这没什么用。所以Pandas就把对角线上面的改为展示维度的密度图了。这里再附上皮尔逊相关系数的值与其图形的关系(图片来自皮尔逊相关系数维基百科):

相关系数

以及一个比较泛(不是非常的严格和通用)的判断相关性强弱的标准:

相关性
-0.09~0.00.0~0.09
-0.3~-0.10.1~0.3
-0.5~-0.30.3~0.5
-1.0~-0.50.5~1.0

可视化可以帮助我们“定性的”了解数据,统计信息则帮助我们“定量的”了解数据,二者结合可以使我们更加了解自己的数据,从而选择下一步的数据处理流程。除了本文已经介绍的之外,Pandas还有非常非常多的工具帮助我们分析数据,可以查看其文档慢慢积累。

本文源码见这里

References

  • 维基百科(具体各个链接见博客内容)。
  • Pandas官方文档.
  • Machine Learning Mastery with Python.