在很多应用中,数据通常散落在不同的文件或数据库中,并不方便进行分析。这一章主要关注工具,能帮我们combine, join, rearrange数据。
Hierarchical Indexing
是pandas
中一个重要的特性,能让我们在一个轴(axis
)上有多个index levels
(索引层级)。它可以让我们在低维格式下处理高维数据。这里给出一个简单的例子,构建一个series
,其index
是a list of lists
:
import pandas as pd
import numpy as np
data = pd.Series(np.random.randn(9),
index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
[1, 2, 3, 1, 3, 1, 2, 2, 3]])
data
a 1 0.636082
2 -1.413061
3 -0.530704
b 1 -0.041634
3 -0.042303
c 1 0.429911
2 0.783350
d 2 0.284328
3 -0.360963
dtype: float64
其中我们看到的是把MultiIndex
作为index
(索引)的,美化过后series
。
data.index
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])
对于这种分层索引对象,partial indexing
(部分索引)也是能做到的,这种方法可以让我们简洁地选中数据的一部分:
data['b']
1 -0.041634
3 -0.042303
dtype: float64
data['b': 'c']
b 1 -0.041634
3 -0.042303
c 1 0.429911
2 0.783350
dtype: float64
data.loc[['b', 'd']]
b 1 -0.041634
3 -0.042303
d 2 0.284328
3 -0.360963
dtype: float64
selection
(选中)对于一个内部层级(inner level
)也是可能的:
data.loc[:, 2]
a -1.413061
c 0.783350
d 0.284328
dtype: float64
分层索引的作用是改变数据的形状,以及做一些基于组的操作(group-based
)比如做一个数据透视表(pivot table
)。例子,我们可以用unstack
来把数据进行重新排列,产生一个DataFrame
:
data.unstack()
1 | 2 | 3 | |
---|---|---|---|
a | 0.636082 | -1.413061 | -0.530704 |
b | -0.041634 | NaN | -0.042303 |
c | 0.429911 | 0.783350 | NaN |
d | NaN | 0.284328 | -0.360963 |
相反的操作是stack
:
data.unstack().stack()
a 1 0.636082
2 -1.413061
3 -0.530704
b 1 -0.041634
3 -0.042303
c 1 0.429911
2 0.783350
d 2 0.284328
3 -0.360963
dtype: float64
之后的章节会对unstack
和stack
做更多介绍。
对于dataframe
,任何一个axis
(轴)都可以有一个分层索引:
frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
columns=[['Ohio', 'Ohio', 'Colorado'],
['Green', 'Red', 'Green']])
frame
Ohio | Colorado | |||
---|---|---|---|---|
Green | Red | Green | ||
a | 1 | 0 | 1 | 2 |
2 | 3 | 4 | 5 | |
b | 1 | 6 | 7 | 8 |
2 | 9 | 10 | 11 |
每一层级都可以有一个名字(字符串或任何python
对象)。如果有的话,这些会显示在输出中:
frame.index.names = ['key1', 'key2']
frame.columns.names = ['state', 'color']
frame
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key1 | key2 | |||
a | 1 | 0 | 1 | 2 |
2 | 3 | 4 | 5 | |
b | 1 | 6 | 7 | 8 |
2 | 9 | 10 | 11 |
这里我们要注意区分行标签(row label
)中索引的名字’state
’和’color
’。
如果想要选中部分列(partial column indexing
)的话,可以选中一组列(groups of columns
):
frame['Ohio']
color | Green | Red | |
---|---|---|---|
key1 | key2 | ||
a | 1 | 0 | 1 |
2 | 3 | 4 | |
b | 1 | 6 | 7 |
2 | 9 | 10 |
MultiIndex
能被同名函数创建,而且可以重复被使用;在DataFrame
中给列创建层级名可以通过以下方式:
pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
names=['state', 'color'])
MultiIndex(levels=[['Colorado', 'Ohio'], ['Green', 'Red']],
labels=[[1, 1, 0], [0, 1, 0]],
names=['state', 'color'])
有时候我们需要在一个axis
(轴)上按层级进行排序,或者在一个层级上,根据值来进行排序。swaplevel
会取两个层级编号或者名字,并返回一个层级改变后的新对象(数据本身并不会被改变):
frame.swaplevel('key1', 'key2')
state | Ohio | Colorado | ||
---|---|---|---|---|
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 |
另一方面,sort_index
则是在一个层级上,按数值进行排序。比如在交换层级的时候,通常也会使用sort_index
,来让结果按指示的层级进行排序:
frame.sort_index(level=1)
state | Ohio | Colorado | ||
---|---|---|---|---|
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 |
frame.sort_index(level='color')
frame.sort_index(level='state')
# 这两个语句都会报错
(按照我的理解,level
指的是key1
和key2
,key1
是level=0,key2
是level=1
。可以看到下面的结果和上面是一样的:)
frame.sort_index(level='key2')
state | Ohio | Colorado | ||
---|---|---|---|---|
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 |
frame.swaplevel(0, 1).sort_index(level=0) # 把key1余key2交换后,按key2来排序
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key2 | key1 | |||
1 | a | 0 | 1 | 2 |
b | 6 | 7 | 8 | |
2 | a | 3 | 4 | 5 |
b | 9 | 10 | 11 |
如果index
是按词典顺序那种方式来排列的话(比如从外层到内层按a,b,c
这样的顺序),在这种多层级的index
对象上,数据选择的效果会更好一些。这是我们调用sort_index(level=0) or sort_index()
在DataFrame
和Series
中,一些描述和归纳统计数据都是有一个level
选项的,这里我们可以指定在某个axis
下,按某个level
(层级)来汇总。比如上面的DataFrame
,我们可以按 行 或 列的层级来进行汇总:
frame
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key1 | key2 | |||
a | 1 | 0 | 1 | 2 |
2 | 3 | 4 | 5 | |
b | 1 | 6 | 7 | 8 |
2 | 9 | 10 | 11 |
frame.sum(level='key2')
state | Ohio | Colorado | |
---|---|---|---|
color | Green | Red | Green |
key2 | |||
1 | 6 | 8 | 10 |
2 | 12 | 14 | 16 |
frame.sum(level='color', axis=1)
color | Green | Red | |
---|---|---|---|
key1 | key2 | ||
a | 1 | 2 | 1 |
2 | 8 | 4 | |
b | 1 | 14 | 7 |
2 | 20 | 10 |
把DataFrame
里的一列或多列作为行索引(row index
)是一件很常见的事;另外,我们可能还希望把行索引变为列。这里有一个例子:
frame = pd.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]})
frame
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
:
frame2 = frame.set_index(['c', 'd'])
frame2
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 |
默认删除原先的列,当然我们也可以留着:
frame.set_index(['c', 'd'], drop=False)
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
相反,它会把多层级索引变为列:
frame2.reset_index()
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 |