一.层次化索引
层次化索引(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