• 8.1 层次化索引
    • 重排与分级排序
    • 根据级别汇总统计
    • 使用DataFrame的列进行索引

    8.1 层次化索引

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

    1. In [9]: data = pd.Series(np.random.randn(9),
    2. ...: index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
    3. ...: [1, 2, 3, 1, 3, 1, 2, 2, 3]])
    4. In [10]: data
    5. Out[10]:
    6. a 1 -0.204708
    7. 2 0.478943
    8. 3 -0.519439
    9. b 1 -0.555730
    10. 3 1.965781
    11. c 1 1.393406
    12. 2 0.092908
    13. d 2 0.281746
    14. 3 0.769023
    15. dtype: float64

    看到的结果是经过美化的带有MultiIndex索引的Series的格式。索引之间的“间隔”表示“直接使用上面的标签”:

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

    对于一个层次化索引的对象,可以使用所谓的部分索引,使用它选取数据子集的操作更简单:

    1. In [12]: data['b']
    2. Out[12]:
    3. 1 -0.555730
    4. 3 1.965781
    5. dtype: float64
    6. In [13]: data['b':'c']
    7. Out[13]:
    8. b 1 -0.555730
    9. 3 1.965781
    10. c 1 1.393406
    11. 2 0.092908
    12. dtype: float64
    13. In [14]: data.loc[['b', 'd']]
    14. Out[14]:
    15. b 1 -0.555730
    16. 3 1.965781
    17. d 2 0.281746
    18. 3 0.769023
    19. dtype: float64

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

    1. In [15]: data.loc[:, 2]
    2. Out[15]:
    3. a 0.478943
    4. c 0.092908
    5. d 0.281746
    6. dtype: float64

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

    1. In [16]: data.unstack()
    2. Out[16]:
    3. 1 2 3
    4. a -0.204708 0.478943 -0.519439
    5. b -0.555730 NaN 1.965781
    6. c 1.393406 0.092908 NaN
    7. d NaN 0.281746 0.769023

    unstack的逆运算是stack:

    1. In [17]: data.unstack().stack()
    2. Out[17]:
    3. a 1 -0.204708
    4. 2 0.478943
    5. 3 -0.519439
    6. b 1 -0.555730
    7. 3 1.965781
    8. c 1 1.393406
    9. 2 0.092908
    10. d 2 0.281746
    11. 3 0.769023
    12. dtype: float64

    stack和unstack将在本章后面详细讲解。

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

    1. In [18]: frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
    2. ....: index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
    3. ....: columns=[['Ohio', 'Ohio', 'Colorado'],
    4. ....: ['Green', 'Red', 'Green']])
    5. In [19]: frame
    6. Out[19]:
    7. Ohio Colorado
    8. Green Red Green
    9. a 1 0 1 2
    10. 2 3 4 5
    11. b 1 6 7 8
    12. 2 9 10 11

    各层都可以有名字(可以是字符串,也可以是别的Python对象)。如果指定了名称,它们就会显示在控制台输出中:

    1. In [20]: frame.index.names = ['key1', 'key2']
    2. In [21]: frame.columns.names = ['state', 'color']
    3. In [22]: frame
    4. Out[22]:
    5. state Ohio Colorado
    6. color Green Red Green
    7. key1 key2
    8. a 1 0 1 2
    9. 2 3 4 5
    10. b 1 6 7 8
    11. 2 9 10 11

    注意:小心区分索引名state、color与行标签。

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

    1. In [23]: frame['Ohio']
    2. Out[23]:
    3. color Green Red
    4. key1 key2
    5. a 1 0 1
    6. 2 3 4
    7. b 1 6 7
    8. 2 9 10

    可以单独创建MultiIndex然后复用。上面那个DataFrame中的(带有分级名称)列可以这样创建:

    1. MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
    2. names=['state', 'color'])

    重排与分级排序

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

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

    而sort_index则根据单个级别中的值对数据进行排序。交换级别时,常常也会用到sort_index,这样最终结果就是按照指定顺序进行字母排序了:

    1. In [25]: frame.sort_index(level=1)
    2. Out[25]:
    3. state Ohio Colorado
    4. color Green Red Green
    5. key1 key2
    6. a 1 0 1 2
    7. b 1 6 7 8
    8. a 2 3 4 5
    9. b 2 9 10 11
    10. In [26]: frame.swaplevel(0, 1).sort_index(level=0)
    11. Out[26]:
    12. state Ohio Colorado
    13. color Green Red Green
    14. key2 key1
    15. 1 a 0 1 2
    16. b 6 7 8
    17. 2 a 3 4 5
    18. b 9 10 11

    根据级别汇总统计

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

    1. In [27]: frame.sum(level='key2')
    2. Out[27]:
    3. state Ohio Colorado
    4. color Green Red Green
    5. key2
    6. 1 6 8 10
    7. 2 12 14 16
    8. In [28]: frame.sum(level='color', axis=1)
    9. Out[28]:
    10. color Green Red
    11. key1 key2
    12. a 1 2 1
    13. 2 8 4
    14. b 1 14 7
    15. 2 20 10

    这其实是利用了pandas的groupby功能,本书稍后将对其进行详细讲解。

    使用DataFrame的列进行索引

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

    1. In [29]: frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
    2. ....: 'c': ['one', 'one', 'one', 'two', 'two',
    3. ....: 'two', 'two'],
    4. ....: 'd': [0, 1, 2, 0, 1, 2, 3]})
    5. In [30]: frame
    6. Out[30]:
    7. a b c d
    8. 0 0 7 one 0
    9. 1 1 6 one 1
    10. 2 2 5 one 2
    11. 3 3 4 two 0
    12. 4 4 3 two 1
    13. 5 5 2 two 2
    14. 6 6 1 two 3

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

    1. In [31]: frame2 = frame.set_index(['c', 'd'])
    2. In [32]: frame2
    3. Out[32]:
    4. a b
    5. c d
    6. one 0 0 7
    7. 1 1 6
    8. 2 2 5
    9. two 0 3 4
    10. 1 4 3
    11. 2 5 2
    12. 3 6 1

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

    1. In [33]: frame.set_index(['c', 'd'], drop=False)
    2. Out[33]:
    3. a b c d
    4. c d
    5. one 0 0 7 one 0
    6. 1 1 6 one 1
    7. 2 2 5 one 2
    8. two 0 3 4 two 0
    9. 1 4 3 two 1
    10. 2 5 2 two 2
    11. 3 6 1 two 3

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

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