一.层次化索引

层次化索引(hierarchical indexing)是pandas的一项重要功能,它使我们能在一个轴上拥有多个(两个以上)索引级别。抽象点说,它使我们能以低维度形式处理高维度数据。我们先来看一个简单的例子:创建一个Series,并用一个由列表或数组组成的列表作为索引。

In [8]: from pandas import Series,DataFrame

In [9]: import numpy as np

In [10]: data=Series(np.random.randn(10),index=[['a','a','a','b','b','b','c','c
...: ','d','d'],[1,2,3,1,2,3,1,2,2,3]])

In [11]: data
Out[11]:
a 1 1.101073
   2 -2.389625
    3 1.423501
b 1 -0.340037
   2 0.052699
   3 1.587835
c 1 0.566140
   2 -1.122921
d 2 -0.137567
   3 -1.518676
dtype: float64

这就是带有MultiIndex索引的Series的格式化输出形式。索引之间的“间隔”表示“直接使用上面的标签”:

In [12]: data.index
Out[12]:
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2
]])

对于一个层次化索引的对象,选取数据子集的操作很简单:

In [13]: data['a']
Out[13]:
1 1.101073
2 -2.389625
3 1.423501
dtype: float64

In [14]: data['b':'c']
Out[14]:
b 1 -0.340037
    2 0.052699
    3 1.587835
c 1 0.566140
   2 -1.122921
dtype: float64

In [16]: data.loc[['b','d']]
Out[16]:
b 1 -0.340037
   2 0.052699
   3 1.587835
d 2 -0.137567
   3 -1.518676
dtype: float64

有时甚至还可以在“内层”中进行选取:

In [17]: data[:,2]     #输出所有外部索引中索引序号为2的数据
Out[17]:
a -2.389625
b 0.052699
c -1.122921
d -0.137567
dtype: float64

层次化索引在数据重塑和基于分组的操作(如透视表生成)中扮演者重要的角色。比如说,这段数据可以通过其unstack方法被重新安排到一个DataFrame中:

In [18]: data.unstack()
Out[18]:
          1             2              3
a 1.101073 -2.389625 1.423501
b -0.340037 0.052699 1.587835
c 0.566140 -1.122921 NaN
d NaN -0.137567 -1.518676

unstack的逆运算是stack:

In [19]: data.unstack().stack()
Out[19]:
a  1   1.101073
    2  -2.389625
    3   1.423501
b  1  -0.340037
    2   0.052699
    3   1.587835
c  1    0.566140
    2  -1.122921
d  2   -0.137567
    3   -1.518676
dtype: float64

对于一个DataFrame,每条轴都可以有分层索引:

In [23]: frame=DataFrame(np.arange(12).reshape((4,3)),index=[['a','a','b','b'],
...: [1,2,1,2]],columns=[['CA','FL','NY'],['Green','Red','Green']])

In [24]: frame
Out[24]:
         CA    FL     NY
      Green Red Green
a 1    0        1         2
   2    3        4         5
b 1    6        7         8
   2    9       10       11

各层都可以有名字(可以是字符串,也可以是别的Python对象)。如果指定了名称,它们就会显示在控制台输出中(不要将索引名称跟轴标签混为一谈!):

In [25]: frame.index.names=['key1','key2']

In [26]: frame.columns.names=['state','color']

In [27]: frame
Out[27]:
state           CA   FL     NY
color        Green Red Green
key1 key2
a       1       0         1      2
         2       3         4      5
b       1       6         7      8
         2       9        10    11

由于有了分部的列索引,因此可以轻松选取列分组:

In [30]: frame['CA']
Out[30]:
color         Green
key1 key2
a        1         0
          2         3
b        1         6
          2         9

二.重排分级顺序

有时,我们需要重新调整某条轴上各级别的顺序,或根据指定级别上的值对数据进行排序。swaplevel接受两个级别编号或名称,并返回一个互换了级别的新对象(但数据不会发生变化):

In [33]: frame.swaplevel('key1','key2')
Out[33]:
state                CA      FL      NY
color              Green Red Green
key2 key1
1       a              0        1        2
2       a              3        4        5
1       b              6        7        8
2       b              9       10      11

In [34]: frame
Out[34]:
state             CA     FL     NY
color         Green Red Green
key1 key2
a        1         0       1      2
          2         3       4      5
b        1         6       7      8
          2         9      10    11

而sort_index(level=level级别)则根据单个级别中的值对数据进行排序(稳定的)。交换级别时,常常会用到sort_index(level=level级别),这样最终结果就是有序的了:

In [40]: frame.sort_index(level=1)    #根据level级别为1的索引(即内层索引)排序
Out[40]:
state                CA      FL     NY
color              Green Red Green
key1 key2
a          1          0         1        2
b          1          6         7        8
a          2          3         4        5
b          2          9        10       11

In [41]: frame.swaplevel(0,1).sort_index(level=0)
Out[41]:
state                CA   FL     NY
color           Green Red Green
key2 key1
1        a              0     1         2
          b               6    7         8
2        a               3    4          5
          b               9   10         11

In [42]: frame
Out[42]:
state               CA      FL   NY
color             Green Red Green
key1 key2
a          1           0         1      2
             2          3         4      5
b           1          6          7     8
             2          9         10    11

注意:在层次化索引的对象上,如果索引时按字典方式从外到内排序(即调用sort_index(level=...)的结果),数据选取操作的性能要好很多。

三.根据级别汇总统计

许多对DataFrame和Series的描述和汇总统计都有一个level选项,它用于指定在某条轴上求和的级别。再以上面那个DataFrame为例,我们可以根据行或列上的级别来进行求和,如下所示:

In [43]: frame.sum(level='key2')
Out[43]:
state CA       FL    NY
color Green Red Green
key2
1         6        8    10
2        12      14   16

In [44]: frame.sum(level='key1')
Out[44]:
state       CA FL   NY
color Green Red Green
key1
a           3       5      7
b          15     17    19

In [45]: frame.sum(level='color',axis=1)
Out[45]:
color           Green Red
key1 key2
a        1          2        1
          2          8        4
b        1         14       7
          2         20      10

这其实就是利用了pamdas的groupby功能,以后会对其进行详解

 

四.使用DataFrame的列

我们经常想要将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame的列。以下面这个DataFrame为例:

In [47]: frame=DataFrame({'a':range(7),'b':range(7,0,-1),'c':['one','one','one'
...: ,'two','two','two','two'],'d':[0,1,2,0,1,2,3]})

In [48]: frame
Out[48]:
   a b   c   d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3

DataFrame的set_index函数会将其一个或多个列转换为行索引,并创建一个新的DataFrame:

In [49]: frame2=frame.set_index(['c','d'])

In [50]: frame2
Out[50]:
           a   b
c     d
one 0  0   7
       1  1   6
       2  2   5
two 0   3  4
       1   4 3
       2   5 2
       3   6 1

默认情况下,那些列会从DataFrame中移除,但也可以将其保留下来:

In [51]: frame2=frame.set_index(['c','d'],drop=False)

In [52]: frame2
Out[52]:
             a b c d
c    d
one 0    0 7 one 0
       1    1 6 one 1
       2    2 5 one 2
two 0    3 4 two 0
      1     4 3 two 1
      2     5 2 two 2
      3     6 1 two 3

reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:

In [53]: frame2=frame.set_index(['c','d'])

In [54]: frame2
Out[54]:
          a b
c     d
one 0 0 7
       1 1 6
       2 2 5
two 0 3 4
       1 4 3
       2 5 2
       3 6 1

In [55]: frame2.reset_index()
Out[55]:
    c    d a b
0 one 0 0 7
1 one 1 1 6
2 one 2 2 5
3 two 0 3 4
4 two 1 4 3
5 two 2 5 2
6 two 3 6 1