使用切片(slicers)
你可以使用切片来选择MultiIndex, slice
是python内置的函数(其实是一个类), 他的用法是这样的:
1 | alist = list('abcdefg' * 3) |
['b', 'd', 'f']
我们可以使用slice
来选择MultiIndex。
下面先创建一个DataFrame:
1 | import pandas as pd |
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']), :] |
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 | idx = pd.IndexSlice |
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']] |
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 | mask = (dfmi[('a', 'foo')] < 100) & (dfmi[('b', 'foo')] < 100) |
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 | midx = pd.MultiIndex(levels=[['zero', 'one'], ['x','y']], |
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 | df2 = df.mean(level=0) |
0 | 1 | |
---|---|---|
one | -0.590699 | 0.137378 |
zero | 1.007094 | 0.603446 |
如果我们想用均值替换原先的所有值, 我们可以恢复到原始数据的形状和索引:
1 | df3 = df2.reindex(df.index, level=0) |
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 | df4 = df2.reindex(df.index) |
0 | 1 | ||
---|---|---|---|
one | y | NaN | NaN |
x | NaN | NaN | |
zero | y | NaN | NaN |
x | NaN | NaN |
我们可以使用更直观的方式去对齐数据:
1 | df_a, df2_a = df.align(df2, level=0) |
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 |
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) |
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) |
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 | import random; |
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() |
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) |
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 | s.index.names=['a', 'b'] |
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 | dft = df.T |
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) |
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() |
False
1 | ss = s.sort_index() |
a b
bar one -0.681788
two 0.873609
baz one 0.035299
dtype: float64
注意
本文由jupyter notebook转换而来, 您可以在这里下载notebook
统计咨询请加QQ 2726725926, 微信 mllncn, SPSS统计咨询是收费的
微博上@mlln-cn可以向我免费题问
请记住我的网址: mlln.cn 或者 jupyter.cn