使用切片(slicers)
你可以使用切片来选择MultiIndex, slice是python内置的函数(其实是一个类), 他的用法是这样的:
1
2
3alist = 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
22import 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的行:
1dfmi.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
3idx = 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的列:
1dfmi.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
3mask = (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
7midx = 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
2df2 = df.mean(level=0)
df2
输出(html):
0
1
one
-0.590699
0.137378
zero
1.007094
0.603446
如果我们想用均值替换原先的所有值, 我们可以恢复到原始数据的形状和索引:
1
2df3 = 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
2df4 = df2.reindex(df.index)
df4
输出(html):
0
1
one
y
NaN
NaN
x
NaN
NaN
zero
y
NaN
NaN
x
NaN
NaN
我们可以使用更直观的方式去对齐数据:
1
2df_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:
1df
输出(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
1df.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的次序:
1df.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
11import 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的层:
1s.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
但是我们可以选择只对某一层排序:
1s.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
2s.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
2dft = 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
1dft.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没有排序, 你可能会遇到错误:
1s.loc[('baz', 'one' ): ('bar', 'one')]
---------------------------------------------------------------------------
UnsortedIndexError Traceback (most recent call last)
in
----> 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来判断是否经过了排序:
1s.index.is_lexsorted()
输出(plain):
False
1
2ss = 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