pandas练习-多层索引的创建和各种操作(multiindex)第二部分

分享时@该用户已经被封, 我就能回答你的问题奥!

文章目录
  1. 1. 使用切片(slicers)
  2. 2. 按索引聚合数据和数据对齐
  3. 3. 交换多层索引的层序
  4. 4. 排序


使用切片(slicers)

你可以使用切片来选择MultiIndex, slice是python内置的函数(其实是一个类), 他的用法是这样的:

1
2
3
alist = list('abcdefg' * 3)
selector = slice(1, 6, 2)
alist[selector]
输出(plain):
['b', 'd', 'f']

我们可以使用slice来选择MultiIndex。

下面先创建一个DataFrame:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np
def mklbl(prefix,n):
return ["%s%s" % (prefix,i) for i in range(n)]


miindex = pd.MultiIndex.from_product([mklbl('A',4),
mklbl('B',2),
mklbl('C',4),
mklbl('D',2)])


micolumns = pd.MultiIndex.from_tuples([('a','foo'),('a','bar'),
('b','foo'),('b','bah')],
names=['lvl0', 'lvl1'])


dfmi = pd.DataFrame(np.arange(len(miindex)*len(micolumns)).reshape((len(miindex),len(micolumns))),
index=miindex,
columns=micolumns).sort_index().sort_index(axis=1)

dfmi.head()
输出(html):
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 9 8 11 10
D1 13 12 15 14
C2 D0 17 16 19 18

下面我们需要选择出MultiIndex第一层为A1或A2或A3, 第二层不做选择, 第三层只包括C1和C3的行:

1
dfmi.loc[(slice('A1', 'A3'), slice(None), ['C1', 'C3']), :]
输出(html):
lvl0 a b
lvl1 bar foo bah foo
A1 B0 C1 D0 73 72 75 74
D1 77 76 79 78
C3 D0 89 88 91 90
D1 93 92 95 94
B1 C1 D0 105 104 107 106
D1 109 108 111 110
C3 D0 121 120 123 122
D1 125 124 127 126
A2 B0 C1 D0 137 136 139 138
D1 141 140 143 142
C3 D0 153 152 155 154
D1 157 156 159 158
B1 C1 D0 169 168 171 170
D1 173 172 175 174
C3 D0 185 184 187 186
D1 189 188 191 190
A3 B0 C1 D0 201 200 203 202
D1 205 204 207 206
C3 D0 217 216 219 218
D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254

你还可以使用pandas.IndexSlic类来实现类似的选择:

1
2
3
idx = pd.IndexSlice

dfmi.loc[idx['A1': 'A3', :, ['C1', 'C3']], :]
输出(html):
lvl0 a b
lvl1 bar foo bah foo
A1 B0 C1 D0 73 72 75 74
D1 77 76 79 78
C3 D0 89 88 91 90
D1 93 92 95 94
B1 C1 D0 105 104 107 106
D1 109 108 111 110
C3 D0 121 120 123 122
D1 125 124 127 126
A2 B0 C1 D0 137 136 139 138
D1 141 140 143 142
C3 D0 153 152 155 154
D1 157 156 159 158
B1 C1 D0 169 168 171 170
D1 173 172 175 174
C3 D0 185 184 187 186
D1 189 188 191 190
A3 B0 C1 D0 201 200 203 202
D1 205 204 207 206
C3 D0 217 216 219 218
D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254

同样是上面的例子, 我们可以选择出列索引第二层为bar的列:

1
dfmi.loc[idx['A1': 'A3', :, ['C1', 'C3']], idx[:, 'foo']]
输出(html):
lvl0 a b
lvl1 foo foo
A1 B0 C1 D0 72 74
D1 76 78
C3 D0 88 90
D1 92 94
B1 C1 D0 104 106
D1 108 110
C3 D0 120 122
D1 124 126
A2 B0 C1 D0 136 138
D1 140 142
C3 D0 152 154
D1 156 158
B1 C1 D0 168 170
D1 172 174
C3 D0 184 186
D1 188 190
A3 B0 C1 D0 200 202
D1 204 206
C3 D0 216 218
D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254

另外, 我们可以使用布尔的蒙版来配合IndexSlice选择数据, 下面我们选择出foo列的数值小于100的行:

1
2
3
mask = (dfmi[('a', 'foo')] < 100) & (dfmi[('b', 'foo')] < 100)

dfmi.loc[idx[mask, :, ['C1', 'C2']], idx[:, 'foo']]
输出(html):
lvl0 a b
lvl1 foo foo
A0 B0 C1 D0 8 10
D1 12 14
C2 D0 16 18
D1 20 22
B1 C1 D0 40 42
D1 44 46
C2 D0 48 50
D1 52 54
A1 B0 C1 D0 72 74
D1 76 78
C2 D0 80 82
D1 84 86

按索引聚合数据和数据对齐

在多层索引中, 我们可以依据某一层进行数据聚合, 比如求和, 求均值, 下面我们先来创建一个dataframe:

1
2
3
4
5
6
7
midx = pd.MultiIndex(levels=[['zero', 'one'], ['x','y']],
labels=[[1,1,0,0],[1,0,1,0]])


df = pd.DataFrame(np.random.randn(4,2), index=midx)

df
输出(html):
0 1
one y -0.634407 0.272985
x -0.546991 0.001771
zero y 1.801089 -1.132311
x 0.213100 2.339203

求第一层索引的均值:

1
2
df2 = df.mean(level=0)
df2
输出(html):
0 1
one -0.590699 0.137378
zero 1.007094 0.603446

如果我们想用均值替换原先的所有值, 我们可以恢复到原始数据的形状和索引:

1
2
df3 = df2.reindex(df.index, level=0)
df3
输出(html):
0 1
one y -0.590699 0.137378
x -0.590699 0.137378
zero y 1.007094 0.603446
x 1.007094 0.603446

上面就是一个数据对齐的过程, df2的索引和df的索引按照第一层对齐, 也就是[one, zero]对齐, 假如不对齐, 我们会得到什么结果?

1
2
df4 = df2.reindex(df.index)
df4
输出(html):
0 1
one y NaN NaN
x NaN NaN
zero y NaN NaN
x NaN NaN

我们可以使用更直观的方式去对齐数据:

1
2
df_a, df2_a = df.align(df2, level=0)
df2_a
输出(html):
0 1
one y -0.590699 0.137378
x -0.590699 0.137378
zero y 1.007094 0.603446
x 1.007094 0.603446

需要注意的是, 上面的方法可能会更改df和df2, 所以有两个返回值。

交换多层索引的层序

直接看例子就好了, 对比交换前后的index:

1
df
输出(html):
0 1
one y -0.634407 0.272985
x -0.546991 0.001771
zero y 1.801089 -1.132311
x 0.213100 2.339203
1
df.swaplevel(0, 1, axis=0)
输出(html):
0 1
y one -0.634407 0.272985
x one -0.546991 0.001771
y zero 1.801089 -1.132311
x zero 0.213100 2.339203

另外, 可以使用reorder_levels达到相同的目的, 只不过它可以一次性修改多层index的次序:

1
df.reorder_levels([1, 0], axis=0)
输出(html):
0 1
y one -0.634407 0.272985
x one -0.546991 0.001771
y zero 1.801089 -1.132311
x zero 0.213100 2.339203

排序

我们可以使用sort_index对索引进行排序。

1
2
3
4
5
6
7
8
9
10
11
import random; 

arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]


tuples = list(zip(*arrays))
random.shuffle(tuples)
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
s = pd.Series(np.random.randn(8), index=pd.MultiIndex.from_tuples(tuples))
s
输出(plain):
baz one 0.035299
foo one -1.021257
baz two -0.225705
foo two -0.369259
bar one -0.681788
two 0.873609
qux two 0.325956
one -1.330222
dtype: float64

默认情况下, sort_index可以逐层排序, 首先排level=0的层:

1
s.sort_index()
输出(plain):
bar one -0.681788
two 0.873609
baz one 0.035299
two -0.225705
foo one -1.021257
two -0.369259
qux one -1.330222
two 0.325956
dtype: float64

但是我们可以选择只对某一层排序:

1
s.sort_index(level=1)
输出(plain):
bar one -0.681788
baz one 0.035299
foo one -1.021257
qux one -1.330222
bar two 0.873609
baz two -0.225705
foo two -0.369259
qux two 0.325956
dtype: float64

如果多层索引设置了names属性, 我们可以使用名称作为参数:

1
2
s.index.names=['a', 'b']
s.sort_index(level='b')
输出(plain):
a b
bar one -0.681788
baz one 0.035299
foo one -1.021257
qux one -1.330222
bar two 0.873609
baz two -0.225705
foo two -0.369259
qux two 0.325956
dtype: float64

除了对索引进行排序, 我们还可以对DataFrame.columns排序, 先来看一下我们的数据:

1
2
dft = df.T
dft
输出(html):
one zero
y x y x
0 -0.634407 -0.546991 1.801089 0.213100
1 0.272985 0.001771 -1.132311 2.339203
1
dft.sort_index(level=1, axis=1)
输出(html):
one zero one zero
x x y y
0 -0.546991 0.213100 -0.634407 1.801089
1 0.001771 2.339203 0.272985 -1.132311

index排序后有一个好处, 就是你可以使用切片来选择数据, 但是如果index没有排序, 你可能会遇到错误:

1
s.loc[('baz', 'one' ): ('bar', 'one')]
---------------------------------------------------------------------------

UnsortedIndexError                        Traceback (most recent call last)

<ipython-input-62-4b4f60d21813> in <module>
----> 1 s.loc[('baz', 'one' ): ('bar', 'one')]


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexing.py in __getitem__(self, key)
   1476 
   1477             maybe_callable = com._apply_if_callable(key, self.obj)
-> 1478             return self._getitem_axis(maybe_callable, axis=axis)
   1479 
   1480     def _is_scalar_access(self, key):


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexing.py in _getitem_axis(self, key, axis)
   1864         if isinstance(key, slice):
   1865             self._validate_key(key, axis)
-> 1866             return self._get_slice_axis(key, axis=axis)
   1867         elif com.is_bool_indexer(key):
   1868             return self._getbool_axis(key, axis=axis)


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexing.py in _get_slice_axis(self, slice_obj, axis)
   1509         labels = obj._get_axis(axis)
   1510         indexer = labels.slice_indexer(slice_obj.start, slice_obj.stop,
-> 1511                                        slice_obj.step, kind=self.name)
   1512 
   1513         if isinstance(indexer, slice):


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexes\base.py in slice_indexer(self, start, end, step, kind)
   4105         """
   4106         start_slice, end_slice = self.slice_locs(start, end, step=step,
-> 4107                                                  kind=kind)
   4108 
   4109         # return a slice


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexes\multi.py in slice_locs(self, start, end, step, kind)
   2144         # This function adds nothing to its parent implementation (the magic
   2145         # happens in get_slice_bound method), but it adds meaningful doc.
-> 2146         return super(MultiIndex, self).slice_locs(start, end, step, kind=kind)
   2147 
   2148     def _partial_tup_index(self, tup, side='left'):


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexes\base.py in slice_locs(self, start, end, step, kind)
   4306         start_slice = None
   4307         if start is not None:
-> 4308             start_slice = self.get_slice_bound(start, 'left', kind)
   4309         if start_slice is None:
   4310             start_slice = 0


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexes\multi.py in get_slice_bound(self, label, side, kind)
   2088         if not isinstance(label, tuple):
   2089             label = label,
-> 2090         return self._partial_tup_index(label, side=side)
   2091 
   2092     def slice_locs(self, start=None, end=None, step=None, kind=None):


d:\mysites\deeplearning.ai-master\.env\lib\site-packages\pandas\core\indexes\multi.py in _partial_tup_index(self, tup, side)
   2151                 'Key length (%d) was greater than MultiIndex'
   2152                 ' lexsort depth (%d)' %
-> 2153                 (len(tup), self.lexsort_depth))
   2154 
   2155         n = len(tup)


UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (0)'

我们可以使用is_lexsorted来判断是否经过了排序:

1
s.index.is_lexsorted()
输出(plain):
False
1
2
ss = s.sort_index()
ss.loc[('bar', 'one' ): ('baz', 'one')]
输出(plain):
a b
bar one -0.681788
two 0.873609
baz one 0.035299
dtype: float64

注意
本文由jupyter notebook转换而来, 您可以在这里下载notebook第二部分.ipynb)
有问题可以直接在下方留言
或者给我发邮件675495787[at]qq.com
请记住我的网址: mlln.cn 或者 jupyter.cn