原文:http://pandas.pydata.org/pandas-docs/stable/advanced.html
校对:(虚位以待)
本节介绍使用MultiIndex
和更高级的索引功能建立索引。
有关一般索引文档,请参见Indexing and Selecting Data。
警告
是否为设置操作返回副本或引用可以取决于上下文。这有时被称为链接 分配
,应该避免。请参见Returning a View versus Copy
警告
在0.15.0 Index
内部被重构为不再子类ndarray
,而是子类化PandasObject
,类似于其余的pandas对象。这应该是一个透明的变化,只有非常有限的API影响(参见Internal Refactoring)
有关某些高级策略,请参阅cookbook
分层/多级索引是非常令人兴奋的,因为它打开了一些非常复杂的数据分析和操作的门,尤其是对于更高维数据的处理。实质上,它使您能够在诸如Series(1d)和DataFrame(2d)的低维数据结构中存储和操作具有任意数量维度的数据。
在本节中,我们将展示“层次化”索引的确切含义,以及它如何与上述和前面章节中描述的所有Pandas索引功能集成。稍后,当讨论group by和pivoting and reshaping data时,我们将展示非平凡的应用程序,以说明它如何帮助构建分析数据。
有关某些高级策略,请参阅cookbook
MultiIndex
对象是标准Index
对象的分层模拟,通常将对象标签存储在pandas对象中。您可以将MultiIndex
视为一个元组数组,其中每个元组都是唯一的。可以从数组列表(使用MultiIndex.from_arrays
),元组数组(使用MultiIndex.from_tuples
)创建MultiIndex
一组交叉的迭代(使用MultiIndex.from_product
)。当传递元组列表时,Index
构造函数将尝试返回MultiIndex
。以下示例演示了初始化MultiIndexes的不同方法。
In [1]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
...: ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
...:
In [2]: tuples = list(zip(*arrays))
In [3]: tuples
Out[3]:
[('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')]
In [4]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
In [5]: index
Out[5]:
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
In [6]: s = pd.Series(np.random.randn(8), index=index)
In [7]: s
Out[7]:
first second
bar one 0.469112
two -0.282863
baz one -1.509059
two -1.135632
foo one 1.212112
two -0.173215
qux one 0.119209
two -1.044236
dtype: float64
当您希望在两个迭代中的每个元素配对时,使用MultiIndex.from_product
函数可以更容易:
In [8]: iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]
In [9]: pd.MultiIndex.from_product(iterables, names=['first', 'second'])
Out[9]:
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
为了方便,可以将数组列表直接传递到Series或DataFrame,以自动构建MultiIndex:
In [10]: arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']),
....: np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])]
....:
In [11]: s = pd.Series(np.random.randn(8), index=arrays)
In [12]: s
Out[12]:
bar one -0.861849
two -2.104569
baz one -0.494929
two 1.071804
foo one 0.721555
two -0.706771
qux one -1.039575
two 0.271860
dtype: float64
In [13]: df = pd.DataFrame(np.random.randn(8, 4), index=arrays)
In [14]: df
Out[14]:
0 1 2 3
bar one -0.424972 0.567020 0.276232 -1.087401
two -0.673690 0.113648 -1.478427 0.524988
baz one 0.404705 0.577046 -1.715002 -1.039268
two -0.370647 -1.157892 -1.344312 0.844885
foo one 1.075770 -0.109050 1.643563 -1.469388
two 0.357021 -0.674600 -1.776904 -0.968914
qux one -1.294524 0.413738 0.276662 -0.472035
two -0.013960 -0.362543 -0.006154 -0.923061
所有MultiIndex
构造函数都接受一个names
参数,该参数存储级别本身的字符串名称。如果没有提供名称,将分配None
:
In [15]: df.index.names
Out[15]: FrozenList([None, None])
此索引可以返回一个pandas对象的任何轴,并且索引的级别数量取决于您:
In [16]: df = pd.DataFrame(np.random.randn(3, 8), index=['A', 'B', 'C'], columns=index)
In [17]: df
Out[17]:
first bar baz foo qux \
second one two one two one two one
A 0.895717 0.805244 -1.206412 2.565646 1.431256 1.340309 -1.170299
B 0.410835 0.813850 0.132003 -0.827317 -0.076467 -1.187678 1.130127
C -1.413681 1.607920 1.024180 0.569605 0.875906 -2.211372 0.974466
first
second two
A -0.226169
B -1.436737
C -2.006747
In [18]: pd.DataFrame(np.random.randn(6, 6), index=index[:6], columns=index[:6])
Out[18]:
first bar baz foo
second one two one two one two
first second
bar one -0.410001 -0.078638 0.545952 -1.219217 -1.226825 0.769804
two -1.281247 -0.727707 -0.121306 -0.097883 0.695775 0.341734
baz one 0.959726 -1.110336 -0.619976 0.149748 -0.732339 0.687738
two 0.176444 0.403310 -0.154951 0.301624 -2.179861 -1.369849
foo one -0.954208 1.462696 -1.743161 -0.826591 -0.345352 1.314232
two 0.690579 0.995761 2.396780 0.014871 3.357427 -0.317441
我们已经“稀化”了高阶索引,使控制台输出更易检视。
值得注意的是,没有什么阻止你使用元组作为轴上的原子标签:
In [19]: pd.Series(np.random.randn(8), index=tuples)
Out[19]:
(bar, one) -1.236269
(bar, two) 0.896171
(baz, one) -0.487602
(baz, two) -0.082240
(foo, one) -2.182937
(foo, two) 0.380396
(qux, one) 0.084844
(qux, two) 0.432390
dtype: float64
MultiIndex
的原因是它可以允许您进行分组,选择和重塑操作,我们将在下面和文档的后续部分中进行介绍。正如您将在后面几节中看到的,您可以发现自己使用分层索引的数据,而不必自己创建MultiIndex
。但是,从文件加载数据时,您可能希望在准备数据集时生成自己的MultiIndex
。
请注意,通过使用pandas.set_printoptions
中的multi_sparse
选项来控制索引的显示方式:
In [20]: pd.set_option('display.multi_sparse', False)
In [21]: df
Out[21]:
first bar bar baz baz foo foo qux \
second one two one two one two one
A 0.895717 0.805244 -1.206412 2.565646 1.431256 1.340309 -1.170299
B 0.410835 0.813850 0.132003 -0.827317 -0.076467 -1.187678 1.130127
C -1.413681 1.607920 1.024180 0.569605 0.875906 -2.211372 0.974466
first qux
second two
A -0.226169
B -1.436737
C -2.006747
In [22]: pd.set_option('display.multi_sparse', True)
方法get_level_values
将返回特定级别每个位置的标签向量:
In [23]: index.get_level_values(0)
Out[23]: Index([u'bar', u'bar', u'baz', u'baz', u'foo', u'foo', u'qux', u'qux'], dtype='object', name=u'first')
In [24]: index.get_level_values('second')
Out[24]: Index([u'one', u'two', u'one', u'two', u'one', u'two', u'one', u'two'], dtype='object', name=u'second')
分层索引的一个重要特征是,您可以通过标识数据中的子组的“部分”标签来选择数据。部分选择以类似于在常规DataFrame中选择列的方式选择“降低”分层索引的级别:
In [25]: df['bar']
Out[25]:
second one two
A 0.895717 0.805244
B 0.410835 0.813850
C -1.413681 1.607920
In [26]: df['bar', 'one']
Out[26]:
A 0.895717
B 0.410835
C -1.413681
Name: (bar, one), dtype: float64
In [27]: df['bar']['one']
Out[27]:
A 0.895717
B 0.410835
C -1.413681
Name: one, dtype: float64
In [28]: s['qux']
Out[28]:
one -1.039575
two 0.271860
dtype: float64
有关如何在较深层次上进行选择,请参阅Cross-section with hierarchical index。
注意
MultiIndex
的repr显示所有定义的索引级别,即使它们没有被实际使用。当切片索引时,您可能会注意到这一点。例如:
# original multi-index
In [29]: df.columns
Out[29]:
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
# sliced
In [30]: df[['foo','qux']].columns
Out[30]:
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
labels=[[2, 2, 3, 3], [0, 1, 0, 1]],
names=[u'first', u'second'])
这样做是为了避免重新计算水平以便使切片具有高性能。如果你想看到实际使用的水平。
In [31]: df[['foo','qux']].columns.values
Out[31]: array([('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')], dtype=object)
# for a specific level
In [32]: df[['foo','qux']].columns.get_level_values(0)
Out[32]: Index([u'foo', u'foo', u'qux', u'qux'], dtype='object', name=u'first')
仅使用已使用的级别重建多索引
In [33]: pd.MultiIndex.from_tuples(df[['foo','qux']].columns.values)
Out[33]:
MultiIndex(levels=[[u'foo', u'qux'], [u'one', u'two']],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
reindex
在轴上具有MultiIndex
的不同索引对象之间的操作将如您所期望的那样工作;数据对齐将与元组的索引相同:
In [34]: s + s[:-2]
Out[34]:
bar one -1.723698
two -4.209138
baz one -0.989859
two 2.143608
foo one 1.443110
two -1.413542
qux one NaN
two NaN
dtype: float64
In [35]: s + s[::2]
Out[35]:
bar one -1.723698
two NaN
baz one -0.989859
two NaN
foo one 1.443110
two NaN
qux one -2.079150
two NaN
dtype: float64
可以使用另一个MultiIndex
或甚至一个元组的列表或数组调用reindex
:
In [36]: s.reindex(index[:3])
Out[36]:
first second
bar one -0.861849
two -2.104569
baz one -0.494929
dtype: float64
In [37]: s.reindex([('foo', 'two'), ('bar', 'one'), ('qux', 'one'), ('baz', 'one')])
Out[37]:
foo two -0.706771
bar one -0.861849
qux one -1.039575
baz one -0.494929
dtype: float64
在.loc/.ix
中将MultiIndex
与高级索引语法结合起来有点具有挑战性,但我们已竭尽全力。例如下面的工作,你会期望:
In [38]: df = df.T
In [39]: df
Out[39]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
two 0.805244 0.813850 1.607920
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
two -0.226169 -1.436737 -2.006747
In [40]: df.loc['bar']
Out[40]:
A B C
second
one 0.895717 0.410835 -1.413681
two 0.805244 0.813850 1.607920
In [41]: df.loc['bar', 'two']
Out[41]:
A 0.805244
B 0.813850
C 1.607920
Name: (bar, two), dtype: float64
“部分”切片也可以很好地工作。
In [42]: df.loc['baz':'foo']
Out[42]:
A B C
first second
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
你可以通过提供一个元组的切片,使用一个“范围”的值。
In [43]: df.loc[('baz', 'two'):('qux', 'one')]
Out[43]:
A B C
first second
baz two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
In [44]: df.loc[('baz', 'two'):'foo']
Out[44]:
A B C
first second
baz two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
传递标签或元组的列表与重建索引类似:
In [45]: df.ix[[('bar', 'two'), ('qux', 'one')]]
Out[45]:
A B C
first second
bar two 0.805244 0.813850 1.607920
qux one -1.170299 1.130127 0.974466
版本0.14.0中的新功能。
在0.14.0中,我们添加了一种新的方法来切割多索引对象。您可以通过提供多个索引器来分割多索引。
您可以提供任何选择器,就像按标签建立索引一样,请参阅Selection by Label,包括切片,标签列表,标签和布尔索引器。
You can use slice(None)
to select all the contents of that level. 您不需要指定所有更深的级别,它们将被暗示为slice(None)
。
通常,包括切片器的两侧,因为这是标签索引。
警告
您应该在.loc
说明符中指定所有轴,这意味着索引和列的索引器。有一些不明确的情况,传递的索引器可能被误解释为两个轴的索引,而不是行的MuliIndex。
你应该做这个:
df.loc[(slice('A1','A3'),.....),:]
而不是这样:
df.loc[(slice('A1','A3'),.....)]
In [46]: def mklbl(prefix,n):
....: return ["%s%s" % (prefix,i) for i in range(n)]
....:
In [47]: miindex = pd.MultiIndex.from_product([mklbl('A',4),
....: mklbl('B',2),
....: mklbl('C',4),
....: mklbl('D',2)])
....:
In [48]: micolumns = pd.MultiIndex.from_tuples([('a','foo'),('a','bar'),
....: ('b','foo'),('b','bah')],
....: names=['lvl0', 'lvl1'])
....:
In [49]: dfmi = pd.DataFrame(np.arange(len(miindex)*len(micolumns)).reshape((len(miindex),len(micolumns))),
....: index=miindex,
....: columns=micolumns).sort_index().sort_index(axis=1)
....:
In [50]: dfmi
Out[50]:
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
D1 21 20 23 22
C3 D0 25 24 27 26
... ... ... ... ...
A3 B1 C0 D1 229 228 231 230
C1 D0 233 232 235 234
D1 237 236 239 238
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 249 248 251 250
D1 253 252 255 254
[64 rows x 4 columns]
使用切片,列表和标签的基本多索引切片。
In [51]: dfmi.loc[(slice('A1','A3'),slice(None), ['C1','C3']),:]
Out[51]:
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
... ... ... ... ...
A3 B0 C1 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
[24 rows x 4 columns]
您可以使用:
而不是使用slice(None)
使用pd.IndexSlice
In [52]: idx = pd.IndexSlice
In [53]: dfmi.loc[idx[:,:,['C1','C3']],idx[:,'foo']]
Out[53]:
lvl0 a b
lvl1 foo foo
A0 B0 C1 D0 8 10
D1 12 14
C3 D0 24 26
D1 28 30
B1 C1 D0 40 42
D1 44 46
C3 D0 56 58
... ... ...
A3 B0 C1 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
[32 rows x 2 columns]
可以在多个轴上同时使用此方法执行相当复杂的选择。
In [54]: dfmi.loc['A1',(slice(None),'foo')]
Out[54]:
lvl0 a b
lvl1 foo foo
B0 C0 D0 64 66
D1 68 70
C1 D0 72 74
D1 76 78
C2 D0 80 82
D1 84 86
C3 D0 88 90
... ... ...
B1 C0 D1 100 102
C1 D0 104 106
D1 108 110
C2 D0 112 114
D1 116 118
C3 D0 120 122
D1 124 126
[16 rows x 2 columns]
In [55]: dfmi.loc[idx[:,:,['C1','C3']],idx[:,'foo']]
Out[55]:
lvl0 a b
lvl1 foo foo
A0 B0 C1 D0 8 10
D1 12 14
C3 D0 24 26
D1 28 30
B1 C1 D0 40 42
D1 44 46
C3 D0 56 58
... ... ...
A3 B0 C1 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
[32 rows x 2 columns]
使用布尔索引器,您可以提供与值相关的选择。
In [56]: mask = dfmi[('a','foo')]>200
In [57]: dfmi.loc[idx[mask,:,['C1','C3']],idx[:,'foo']]
Out[57]:
lvl0 a b
lvl1 foo foo
A3 B0 C1 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
您还可以为.loc
指定axis
参数,以解释单个轴上传递的切片。
In [58]: dfmi.loc(axis=0)[:,:,['C1','C3']]
Out[58]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C1 D0 9 8 11 10
D1 13 12 15 14
C3 D0 25 24 27 26
D1 29 28 31 30
B1 C1 D0 41 40 43 42
D1 45 44 47 46
C3 D0 57 56 59 58
... ... ... ... ...
A3 B0 C1 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
[32 rows x 4 columns]
此外,您可以使用这些方法设置值
In [59]: df2 = dfmi.copy()
In [60]: df2.loc(axis=0)[:,:,['C1','C3']] = -10
In [61]: df2
Out[61]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 -10 -10 -10 -10
D1 -10 -10 -10 -10
C2 D0 17 16 19 18
D1 21 20 23 22
C3 D0 -10 -10 -10 -10
... ... ... ... ...
A3 B1 C0 D1 229 228 231 230
C1 D0 -10 -10 -10 -10
D1 -10 -10 -10 -10
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 -10 -10 -10 -10
D1 -10 -10 -10 -10
[64 rows x 4 columns]
您也可以使用可对齐对象的右侧。
In [62]: df2 = dfmi.copy()
In [63]: df2.loc[idx[:,:,['C1','C3']],:] = df2*1000
In [64]: df2
Out[64]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 9000 8000 11000 10000
D1 13000 12000 15000 14000
C2 D0 17 16 19 18
D1 21 20 23 22
C3 D0 25000 24000 27000 26000
... ... ... ... ...
A3 B1 C0 D1 229 228 231 230
C1 D0 233000 232000 235000 234000
D1 237000 236000 239000 238000
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 249000 248000 251000 250000
D1 253000 252000 255000 254000
[64 rows x 4 columns]
DataFrame
的xs
方法还需要一个级别参数,以便更容易地选择MultiIndex的特定级别上的数据。
In [65]: df
Out[65]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
two 0.805244 0.813850 1.607920
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
two -0.226169 -1.436737 -2.006747
In [66]: df.xs('one', level='second')
Out[66]:
A B C
first
bar 0.895717 0.410835 -1.413681
baz -1.206412 0.132003 1.024180
foo 1.431256 -0.076467 0.875906
qux -1.170299 1.130127 0.974466
# using the slicers (new in 0.14.0)
In [67]: df.loc[(slice(None),'one'),:]
Out[67]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
baz one -1.206412 0.132003 1.024180
foo one 1.431256 -0.076467 0.875906
qux one -1.170299 1.130127 0.974466
您还可以通过提供轴参数在xs()
的列上选择
In [68]: df = df.T
In [69]: df.xs('one', level='second', axis=1)
Out[69]:
first bar baz foo qux
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
# using the slicers (new in 0.14.0)
In [70]: df.loc[:,(slice(None),'one')]
Out[70]:
first bar baz foo qux
second one one one one
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
xs()
还允许使用多个键进行选择
In [71]: df.xs(('one', 'bar'), level=('second', 'first'), axis=1)
Out[71]:
first bar
second one
A 0.895717
B 0.410835
C -1.413681
# using the slicers (new in 0.14.0)
In [72]: df.loc[:,('bar','one')]
Out[72]:
A 0.895717
B 0.410835
C -1.413681
Name: (bar, one), dtype: float64
版本0.13.0中的新功能。
您可以将drop_level=False
传递给xs()
,以保留所选的级别
In [73]: df.xs('one', level='second', axis=1, drop_level=False)
Out[73]:
first bar baz foo qux
second one one one one
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
与drop_level=True
(默认值)的结果相比,
In [74]: df.xs('one', level='second', axis=1, drop_level=True)
Out[74]:
first bar baz foo qux
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
参数level
已添加到pandas对象的reindex
和align
方法中。这对于跨级别广播值很有用。例如:
In [75]: midx = pd.MultiIndex(levels=[['zero', 'one'], ['x','y']],
....: labels=[[1,1,0,0],[1,0,1,0]])
....:
In [76]: df = pd.DataFrame(np.random.randn(4,2), index=midx)
In [77]: df
Out[77]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [78]: df2 = df.mean(level=0)
In [79]: df2
Out[79]:
0 1
zero 1.271532 0.713416
one 1.060074 -0.109716
In [80]: df2.reindex(df.index, level=0)
Out[80]:
0 1
one y 1.060074 -0.109716
x 1.060074 -0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
# aligning
In [81]: df_aligned, df2_aligned = df.align(df2, level=0)
In [82]: df_aligned
Out[82]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [83]: df2_aligned
Out[83]:
0 1
one y 1.060074 -0.109716
x 1.060074 -0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
swaplevel()
swaplevel
函数可以切换两个级别的顺序:
In [84]: df[:5]
Out[84]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [85]: df[:5].swaplevel(0, 1, axis=0)
Out[85]:
0 1
y one 1.519970 -0.493662
x one 0.600178 0.274230
y zero 0.132885 -0.023688
x zero 2.410179 1.450520
reorder_levels()
reorder_levels
函数概括了swaplevel
函数,允许您在一个步骤中置换层次索引级别:
In [86]: df[:5].reorder_levels([1,0], axis=0)
Out[86]:
0 1
y one 1.519970 -0.493662
x one 0.600178 0.274230
y zero 0.132885 -0.023688
x zero 2.410179 1.450520
MultiIndex
要有效地对多索引对象进行索引和分片,需要对它们进行排序。与任何索引一样,您可以使用sort_index
。
In [87]: import random; random.shuffle(tuples)
In [88]: s = pd.Series(np.random.randn(8), index=pd.MultiIndex.from_tuples(tuples))
In [89]: s
Out[89]:
baz two 0.206053
qux one -0.251905
bar two -2.213588
one 1.063327
baz one 1.266143
qux two 0.299368
foo one -0.863838
two 0.408204
dtype: float64
In [90]: s.sort_index()
Out[90]:
bar one 1.063327
two -2.213588
baz one 1.266143
two 0.206053
foo one -0.863838
two 0.408204
qux one -0.251905
two 0.299368
dtype: float64
In [91]: s.sort_index(level=0)
Out[91]:
bar one 1.063327
two -2.213588
baz one 1.266143
two 0.206053
foo one -0.863838
two 0.408204
qux one -0.251905
two 0.299368
dtype: float64
In [92]: s.sort_index(level=1)
Out[92]:
bar one 1.063327
baz one 1.266143
foo one -0.863838
qux one -0.251905
bar two -2.213588
baz two 0.206053
foo two 0.408204
qux two 0.299368
dtype: float64
如果MultiIndex级别命名,您也可以将级别名称传递给sort_index
。
In [93]: s.index.set_names(['L1', 'L2'], inplace=True)
In [94]: s.sort_index(level='L1')
Out[94]:
L1 L2
bar one 1.063327
two -2.213588
baz one 1.266143
two 0.206053
foo one -0.863838
two 0.408204
qux one -0.251905
two 0.299368
dtype: float64
In [95]: s.sort_index(level='L2')
Out[95]:
L1 L2
bar one 1.063327
baz one 1.266143
foo one -0.863838
qux one -0.251905
bar two -2.213588
baz two 0.206053
foo two 0.408204
qux two 0.299368
dtype: float64
对于较高维度的对象,如果具有MultiIndex,您可以按级别对任何其他轴进行排序:
In [96]: df.T.sort_index(level=1, axis=1)
Out[96]:
zero one zero one
x x y y
0 2.410179 0.600178 0.132885 1.519970
1 1.450520 0.274230 -0.023688 -0.493662
即使数据没有排序,索引也会起作用,但是效率相当低(并显示PerformanceWarning
)。它还将返回数据的副本,而不是视图:
In [97]: dfm = pd.DataFrame({'jim': [0, 0, 1, 1],
....: 'joe': ['x', 'x', 'z', 'y'],
....: 'jolie': np.random.rand(4)})
....:
In [98]: dfm = dfm.set_index(['jim', 'joe'])
In [99]: dfm
Out[99]:
jolie
jim joe
0 x 0.490671
x 0.120248
1 z 0.537020
y 0.110968
In [4]: dfm.loc[(1, 'z')]
PerformanceWarning: indexing past lexsort depth may impact performance.
Out[4]:
jolie
jim joe
1 z 0.64094
此外,如果你尝试索引的东西不完全lexsorted,这可以提出:
In [5]: dfm.loc[(0,'y'):(1, 'z')]
KeyError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
Index
上的is_lexsorted()
方法显示索引是否已排序,并且lexsort_depth
属性返回排序深度:
In [100]: dfm.index.is_lexsorted()
Out[100]: False
In [101]: dfm.index.lexsort_depth
Out[101]: 1
In [102]: dfm = dfm.sort_index()
In [103]: dfm
Out[103]:
jolie
jim joe
0 x 0.490671
x 0.120248
1 y 0.110968
z 0.537020
In [104]: dfm.index.is_lexsorted()
Out[104]: True
In [105]: dfm.index.lexsort_depth
Out[105]: 2
现在选择工作正如预期。
In [106]: dfm.loc[(0,'y'):(1, 'z')]
Out[106]:
jolie
jim joe
1 y 0.110968
z 0.537020
与numpy ndarrays类似,pandas Index,Series和DataFrame还提供了take
方法,用于在给定的索引处检索沿给定轴的元素。给定的索引必须是整数索引位置的列表或者ndarray。take
也会接受负整数作为对象结尾的相对位置。
In [107]: index = pd.Index(np.random.randint(0, 1000, 10))
In [108]: index
Out[108]: Int64Index([214, 502, 712, 567, 786, 175, 993, 133, 758, 329], dtype='int64')
In [109]: positions = [0, 9, 3]
In [110]: index[positions]
Out[110]: Int64Index([214, 329, 567], dtype='int64')
In [111]: index.take(positions)
Out[111]: Int64Index([214, 329, 567], dtype='int64')
In [112]: ser = pd.Series(np.random.randn(10))
In [113]: ser.iloc[positions]
Out[113]:
0 -0.179666
9 1.824375
3 0.392149
dtype: float64
In [114]: ser.take(positions)
Out[114]:
0 -0.179666
9 1.824375
3 0.392149
dtype: float64
对于DataFrames,给定的索引应该是指定行或列位置的1d列表或ndarray。
In [115]: frm = pd.DataFrame(np.random.randn(5, 3))
In [116]: frm.take([1, 4, 3])
Out[116]:
0 1 2
1 -1.237881 0.106854 -1.276829
4 0.629675 -1.425966 1.857704
3 0.979542 -1.633678 0.615855
In [117]: frm.take([0, 2], axis=1)
Out[117]:
0 2
0 0.595974 0.601544
1 -1.237881 -1.276829
2 -0.767101 1.499591
3 0.979542 0.615855
4 0.629675 1.857704
重要的是要注意,pandas对象上的take
方法不适用于布尔索引,并且可能返回意想不到的结果。
In [118]: arr = np.random.randn(10)
In [119]: arr.take([False, False, True, True])
Out[119]: array([-1.1935, -1.1935, 0.6775, 0.6775])
In [120]: arr[[0, 1]]
Out[120]: array([-1.1935, 0.6775])
In [121]: ser = pd.Series(np.random.randn(10))
In [122]: ser.take([False, False, True, True])
Out[122]:
0 0.233141
0 0.233141
1 -0.223540
1 -0.223540
dtype: float64
In [123]: ser.ix[[0, 1]]
Out[123]:
0 0.233141
1 -0.223540
dtype: float64
最后,作为对性能的一个小注释,因为take
方法处理较窄的输入范围,它可以提供比花式索引更快的性能。
我们已经在前面的章节讨论了MultiIndex
。DatetimeIndex
和PeriodIndex
在此显示为here。TimedeltaIndex
在此处为here。
在下面的小节中,我们将高亮一些其他索引类型。
版本0.16.1中的新功能。
我们引入了一个CategoricalIndex
,一种新类型的索引对象,用于支持使用重复索引。这是围绕Categorical
(在v0.15.0中引入)的容器,允许对具有大量重复元素的索引进行有效的索引和存储。在0.16.1之前,使用category
dtype设置DataFrame/Series
的索引会将其转换为常规基于对象的Index
。
In [124]: df = pd.DataFrame({'A': np.arange(6),
.....: 'B': list('aabbca')})
.....:
In [125]: df['B'] = df['B'].astype('category', categories=list('cab'))
In [126]: df
Out[126]:
A B
0 0 a
1 1 a
2 2 b
3 3 b
4 4 c
5 5 a
In [127]: df.dtypes
Out[127]:
A int64
B category
dtype: object
In [128]: df.B.cat.categories
Out[128]: Index([u'c', u'a', u'b'], dtype='object')
设置索引,将创建CategoricalIndex
In [129]: df2 = df.set_index('B')
In [130]: df2.index
Out[130]: CategoricalIndex([u'a', u'a', u'b', u'b', u'c', u'a'], categories=[u'c', u'a', u'b'], ordered=False, name=u'B', dtype='category')
使用__getitem__/.iloc/.loc/.ix
索引与Index
类似,具有重复。索引器必须在类别中或操作将增加。
In [131]: df2.loc['a']
Out[131]:
A
B
a 0
a 1
a 5
这些保留CategoricalIndex
In [132]: df2.loc['a'].index
Out[132]: CategoricalIndex([u'a', u'a', u'a'], categories=[u'c', u'a', u'b'], ordered=False, name=u'B', dtype='category')
排序将按类别的顺序排序
In [133]: df2.sort_index()
Out[133]:
A
B
c 4
a 0
a 1
a 5
b 2
b 3
索引上的Groupby操作也将保留索引本质
In [134]: df2.groupby(level=0).sum()
Out[134]:
A
B
c 4
a 6
b 5
In [135]: df2.groupby(level=0).sum().index
Out[135]: CategoricalIndex([u'c', u'a', u'b'], categories=[u'c', u'a', u'b'], ordered=False, name=u'B', dtype='category')
重索引操作将根据传递的索引器的类型返回一个结果索引,这意味着传递一个列表将返回一个普通的Index
;用Categorical
索引将返回CategoricalIndex
,根据PASSED Categorical
dtype的类别索引。这允许任意索引这些甚至与不在类别中的值,类似于如何重新索引任何熊猫索引。
In [136]: df2.reindex(['a','e'])
Out[136]:
A
B
a 0.0
a 1.0
a 5.0
e NaN
In [137]: df2.reindex(['a','e']).index
Out[137]: Index([u'a', u'a', u'a', u'e'], dtype='object', name=u'B')
In [138]: df2.reindex(pd.Categorical(['a','e'],categories=list('abcde')))
Out[138]:
A
B
a 0.0
a 1.0
a 5.0
e NaN
In [139]: df2.reindex(pd.Categorical(['a','e'],categories=list('abcde'))).index
Out[139]: CategoricalIndex([u'a', u'a', u'a', u'e'], categories=[u'a', u'b', u'c', u'd', u'e'], ordered=False, name=u'B', dtype='category')
警告
对CategoricalIndex
的重塑和比较操作必须具有相同的类别,否则会引发TypeError
。
In [9]: df3 = pd.DataFrame({'A' : np.arange(6),
'B' : pd.Series(list('aabbca')).astype('category')})
In [11]: df3 = df3.set_index('B')
In [11]: df3.index
Out[11]: CategoricalIndex([u'a', u'a', u'b', u'b', u'c', u'a'], categories=[u'a', u'b', u'c'], ordered=False, name=u'B', dtype='category')
In [12]: pd.concat([df2, df3]
TypeError: categories must match existing categories when appending
警告
对于具有浮点数的整数索引的索引已经在0.18.0中阐明,对于变化的总结,参见here。
Int64Index
是pandas中的基本基本索引。这是一个不可变数组实现有序,可切分集。在0.18.0之前,Int64Index
将为所有NDFrame
对象提供默认索引。
RangeIndex
是版本0.18.0中添加的Int64Index
的子类,现在为所有NDFrame
对象提供默认索引。RangeIndex
是可以表示单调有序集的Int64Index
的优化版本。这些类似于python 范围类型。
注意
从0.14.0开始,Float64Index
由原生float64
dtype数组支持。在0.14.0之前,Float64Index
由object
dtype数组支持。在后端中使用float64
dtype将算术运算速度提高大约30倍,并且在Float64Index
本身上的布尔索引操作大约快两倍。
版本0.13.0中的新功能。
默认情况下,在索引创建中传递浮动或混合整数浮点值时,将自动创建Float64Index
。这使得纯标签的切片范例使[],ix,loc
用于标量索引和切片工作完全相同。
In [140]: indexf = pd.Index([1.5, 2, 3, 4.5, 5])
In [141]: indexf
Out[141]: Float64Index([1.5, 2.0, 3.0, 4.5, 5.0], dtype='float64')
In [142]: sf = pd.Series(range(5), index=indexf)
In [143]: sf
Out[143]:
1.5 0
2.0 1
3.0 2
4.5 3
5.0 4
dtype: int64
对于[],.ix,.loc
的标量选择将始终以标签为基础。整数将匹配相等的浮点索引(例如,3
等效于3.0
)
In [144]: sf[3]
Out[144]: 2
In [145]: sf[3.0]
Out[145]: 2
In [146]: sf.ix[3]
Out[146]: 2
In [147]: sf.ix[3.0]
Out[147]: 2
In [148]: sf.loc[3]
Out[148]: 2
In [149]: sf.loc[3.0]
Out[149]: 2
唯一的位置索引是通过iloc
In [150]: sf.iloc[3]
Out[150]: 3
未找到的标量索引将引发KeyError
对于[],ix,loc
和ALWAYS位置,iloc
的值始终对索引的值进行切片。
In [151]: sf[2:4]
Out[151]:
2.0 1
3.0 2
dtype: int64
In [152]: sf.ix[2:4]
Out[152]:
2.0 1
3.0 2
dtype: int64
In [153]: sf.loc[2:4]
Out[153]:
2.0 1
3.0 2
dtype: int64
In [154]: sf.iloc[2:4]
Out[154]:
3.0 2
4.5 3
dtype: int64
在浮点索引中,允许使用浮点数进行切片
In [155]: sf[2.1:4.6]
Out[155]:
3.0 2
4.5 3
dtype: int64
In [156]: sf.loc[2.1:4.6]
Out[156]:
3.0 2
4.5 3
dtype: int64
在非浮点索引中,使用浮点数的切片将产生TypeError
In [1]: pd.Series(range(5))[3.5]
TypeError: the label [3.5] is not a proper indexer for this index type (Int64Index)
In [1]: pd.Series(range(5))[3.5:4.5]
TypeError: the slice start [3.5] is not a proper indexer for this index type (Int64Index)
警告
在0.18.0中已经删除了.iloc
的标量浮点索引器,因此下面将会提出TypeError
In [3]: pd.Series(range(5)).iloc[3.0]
TypeError: cannot do positional indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [3.0] of <type 'float'>
此外,在非浮点索引上使用浮点索引器处理.ix
将基于标签,从而强制索引。
In [157]: s2 = pd.Series([1, 2, 3], index=list('abc'))
In [158]: s2
Out[158]:
a 1
b 2
c 3
dtype: int64
In [159]: s2.ix[1.0] = 10
In [160]: s2
Out[160]:
a 1
b 2
c 3
1.0 10
dtype: int64
以下是使用此类型索引的典型用例。想象一下,你有一个有点不规则的timedelta样索引方案,但数据记录为浮点数。这可以是例如毫秒偏移。
In [161]: dfir = pd.concat([pd.DataFrame(np.random.randn(5,2),
.....: index=np.arange(5) * 250.0,
.....: columns=list('AB')),
.....: pd.DataFrame(np.random.randn(6,2),
.....: index=np.arange(4,10) * 250.1,
.....: columns=list('AB'))])
.....:
In [162]: dfir
Out[162]:
A B
0.0 0.997289 -1.693316
250.0 -0.179129 -1.598062
500.0 0.936914 0.912560
750.0 -1.003401 1.632781
1000.0 -0.724626 0.178219
1000.4 0.310610 -0.108002
1250.5 -0.974226 -1.147708
1500.6 -2.281374 0.760010
1750.7 -0.742532 1.533318
2000.8 2.495362 -0.432771
2250.9 -0.068954 0.043520
对于所有选择运算符,选择操作将始终以值为基础工作。
In [163]: dfir[0:1000.4]
Out[163]:
A B
0.0 0.997289 -1.693316
250.0 -0.179129 -1.598062
500.0 0.936914 0.912560
750.0 -1.003401 1.632781
1000.0 -0.724626 0.178219
1000.4 0.310610 -0.108002
In [164]: dfir.loc[0:1001,'A']
Out[164]:
0.0 0.997289
250.0 -0.179129
500.0 0.936914
750.0 -1.003401
1000.0 -0.724626
1000.4 0.310610
Name: A, dtype: float64
In [165]: dfir.loc[1000.4]
Out[165]:
A 0.310610
B -0.108002
Name: 1000.4, dtype: float64
然后,您可以轻松挑出前1秒(1000毫秒)的数据。
In [166]: dfir[0:1000]
Out[166]:
A B
0.0 0.997289 -1.693316
250.0 -0.179129 -1.598062
500.0 0.936914 0.912560
750.0 -1.003401 1.632781
1000.0 -0.724626 0.178219
当然,如果你需要基于整数的选择,然后使用iloc
In [167]: dfir.iloc[0:5]
Out[167]:
A B
0.0 0.997289 -1.693316
250.0 -0.179129 -1.598062
500.0 0.936914 0.912560
750.0 -1.003401 1.632781
1000.0 -0.724626 0.178219