pandas 分层索引
一、分层索引基础
Pandas提供了Panel和Panel4D对象解决三维和四维数据的处理需求,但更常用的还是分层索引。分层索引是Pandas的重要特性,允许我们在一个轴向上拥有多个索引层级,它提供了一种在更低维度的形式中处理更高维度数据的方式。也就是如何用Series、DataFrame处理三维、四维等等高维度的数据。
比如有下面的数据:
In [168]: s = 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]])
In [169]: s
Out[169]:
a 1 0.283490
2 0.295529
3 0.277676
b 1 0.487573
3 0.091161
c 1 0.285157
2 -0.806851
d 2 -0.287969
3 -0.696511
dtype: float64
In [170]: s.index
Out[170]:
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]])
MultiIndex就是一个分层索引对象,在打印的时候会进行规整的美化。
下面看下一些基本的操作:
In [171]: s['b']
Out[171]:
1 0.487573
3 0.091161
dtype: float64
In [172]: s['b':'c']
Out[172]:
b 1 0.487573
3 0.091161
c 1 0.285157
2 -0.806851
dtype: float64
In [173]: s.loc[['b','d']]
Out[173]:
b 1 0.487573
3 0.091161
d 2 -0.287969
3 -0.696511
dtype: float64
In [174]: s.loc['b','d'] # 这样不可以
---------------------------------------------------------------------------
IndexingError Traceback (most recent call last)
IndexingError: Too many indexers
In [175]: s.loc['b',1] # 但是这样可以
Out[175]: 0.48757273896298425
In [176]: s.loc[:, 2] # 或者这样
Out[176]:
a 0.295529
c -0.806851
d -0.287969
dtype: float64
还可以这样来生成MultiIndex,请体会它的不同之处:
In [3]: tup = [('beijing',2000),('beijing',2019),
...: ('shanghai',2000),('shanghai',2019),
...: ('guangzhou',2000),('guangzhou',2019)]
In [4]: values = [10000,100000,6000,60000,4000,40000]
In [7]: index = pd.MultiIndex.from_tuples(tup) # 利用元组生成MultiIndex
In [8]: sss = pd.Series(values, index=index) # 提供一个MultiIndex作为索引
In [9]: sss
Out[9]:
beijing 2000 10000
2019 100000
shanghai 2000 6000
2019 60000
guangzhou 2000 4000
2019 40000
dtype: int64
更多的创建MultiIndex的方法还有:
- 从列表:pd.MultiIndex.from_arrays([['a','a','b','b'],[1,2,1,2]])
- 从元组:pd.MultiIndex.from_tuples([('a',1),('a',2),('b',1),('b',2)])
- 笛卡儿积:pd.MultiIndex.from_product([['a','b'],[1,2]])
- 直接构造:pd.MultiIndex(levels=[['a','b'],[1,2]],labels=[[0,0,1,1],[0,1,0,1]])
生成MultiIndex对象后,就可以将这些对象作为参数,在创建Series或者DataFrame时传递给index。或者通过reindex方法更新已有的Series/DataFrame索引。
分层索引在重塑数据和数组透视表中非常重要。比如,我们可以使用unstack方法将数据在DataFrame中重新排列,也就是展开:
In [178]: s.unstack()
Out[178]:
1 2 3
a 0.283490 0.295529 0.277676
b 0.487573 NaN 0.091161
c 0.285157 -0.806851 NaN
d NaN -0.287969 -0.696511
In [179]: s.unstack().stack() # 反操作stack
Out[179]:
a 1 0.283490
2 0.295529
3 0.277676
b 1 0.487573
3 0.091161
c 1 0.285157
2 -0.806851
d 2 -0.287969
3 -0.696511
dtype: float64
对于DataFrame对象,每个轴都可以有分层索引,给index或columns提供一个多维数组,就可以分层:
In [3]: df = pd.DataFrame(np.arange(12).reshape((4, 3)),
...: index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
...: columns=[['Ohio', 'Ohio', 'Colorado'],
...: ['Green', 'Red', 'Green']])
In [4]: df
Out[4]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
可以给分层索引自定义名字:
In [5]: df.index.names
Out[5]: FrozenList([None, None])
In [6]: df.index.names = ['key1','key2']
In [7]: df.columns.names
Out[7]: FrozenList([None, None])
In [8]: df.columns.names = ['state','color']
In [9]: df
Out[9]:
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
In [10]: df['Ohio']
Out[10]:
color Green Red
key1 key2
a 1 0 1
2 3 4
b 1 6 7
2 9 10
二、分层索引进阶
1、重排序和层级排序
有时候,我们需要重新排列轴上的层级顺序,或者按照特定的层级的值对数据进行排序。 Pandas的swaplevel方法用于这一功能,层级发生变更,但原数据不变。
In [11]: df.swaplevel('key1', 'key2')
Out[11]:
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
可以使得结果按照层级进行字典排序:
In [12]: df.sort_index(level=1)
Out[12]:
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
In [13]: df.swaplevel(0, 1).sort_index(level=0)
Out[13]:
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
sort_index(level=1)
意思是在第2个层级上进行索引的排序。
swaplevel(0, 1)
的意思是将第0层和第1层的行索引进行交换。
2、层级的汇总统计
使用level参数可以指定你想要在某个特定的轴上进行聚合。
In [15]: df.sum(level='key2')
Out[15]:
state Ohio Colorado
color Green Red Green
key2
1 6 8 10
2 12 14 16
In [16]: df.sum(level='color', axis=1)
Out[16]:
color Green Red
key1 key2
a 1 2 1
2 8 4
b 1 14 7
2 20 10
3、使用DataFrame的列进行索引
在DataFarme的索引操作中,还有一个比较特殊的场景,就是将一些列转换成层级行索引:
In [17]: df= 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]})
...:
In [18]: df
Out[18]:
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
In [19]: df2 = df.set_index(['c','d'])
In [20]: df2
Out[20]:
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 [21]: df.set_index(['c','d'],drop=False)
Out[21]:
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
In [22]: df2.reset_index()
Out[22]:
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
set_index(['c','d'])
,将c列和d列变成了分层的行索引drop=False
则保留了原来的列数据reset_index
是set_index
的反向操作
4、分层索引的取值与切片
先看针对Series的操作:
In [3]: tup = [('beijing',2000),('beijing',2019),
...: ('shanghai',2000),('shanghai',2019),
...: ('guangzhou',2000),('guangzhou',2019)]
In [4]: values = [10000,100000,6000,60000,4000,40000]
In [7]: index = pd.MultiIndex.from_tuples(tup)
In [8]: s = pd.Series(values, index=index)
In [9]: s
Out[9]:
beijing 2000 10000
2019 100000
shanghai 2000 6000
2019 60000
guangzhou 2000 4000
2019 40000
dtype: int64
In [10]: s['beijing',2019]
Out[10]: 100000
In [11]: s['shanghai']
Out[11]:
2000 6000
2019 60000
dtype: int64
In [15]: s[:,2000]
Out[15]:
beijing 10000
shanghai 6000
guangzhou 4000
dtype: int64
In [17]: s[s>5000]
Out[17]:
beijing 2000 10000
2019 100000
shanghai 2000 6000
2019 60000
guangzhou 2019 40000
dtype: int64
In [18]: s[['shanghai','guangzhou']]
Out[18]:
shanghai 2000 6000
2019 60000
guangzhou 2000 4000
2019 40000
dtype: int64
再看看DataFrame,需要记住和理解的核心是:
- 这是一个分层索引DataFrame对象,不是单级的
- 默认以列为操作对象
In [19]: df = pd.DataFrame(np.arange(12).reshape((4, 3)),
...: index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
...: columns=[['Ohio', 'Ohio', 'Colorado'],
...: ['Green', 'Red', 'Green']])
...:
In [20]: df
Out[20]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
In [23]: df['Ohio','Colorado'] # 不能这么做,因为列索引分层了
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
In [24]: df[['Ohio','Colorado']] # 这样可以
Out[24]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
In [25]: df['Ohio','Green'] # 每层提供一个参数
Out[25]:
a 1 0
2 3
b 1 6
2 9
Name: (Ohio, Green), dtype: int32
In [26]: df.iloc[:2,:2]
Out[26]:
Ohio
Green Red
a 1 0 1
2 3 4
In [28]: df.loc[:,('Ohio','Red')]
Out[28]:
a 1 1
2 4
b 1 7
2 10
Name: (Ohio, Red), dtype: int32
另外最后要提醒的是:如果MultiIndex不是有序的索引,那么大多数切片操作都会失败!这时候可以使用前面介绍过的sort_index
方法先排下序。