`.loc` 和 `.iloc` 与 MultiIndex'd DataFrame
Posted
技术标签:
【中文标题】`.loc` 和 `.iloc` 与 MultiIndex\'d DataFrame【英文标题】:`.loc` and `.iloc` with MultiIndex'd DataFrame`.loc` 和 `.iloc` 与 MultiIndex'd DataFrame 【发布时间】:2018-02-08 14:21:28 【问题描述】:在索引 MultiIndex-ed DataFrame 时,.iloc
似乎假设您引用的是索引的“内部级别”,而 .loc
则查看外部级别。
例如:
np.random.seed(123)
iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]
idx = pd.MultiIndex.from_product(iterables, names=['first', 'second'])
df = pd.DataFrame(np.random.randn(8, 4), index=idx)
# .loc looks at the outer index:
print(df.loc['qux'])
# df.loc['two'] would throw KeyError
0 1 2 3
second
one -1.25388 -0.63775 0.90711 -1.42868
two -0.14007 -0.86175 -0.25562 -2.79859
# while .iloc looks at the inner index:
print(df.iloc[-1])
0 -0.14007
1 -0.86175
2 -0.25562
3 -2.79859
Name: (qux, two), dtype: float64
两个问题:
首先,这是为什么呢?这是一个深思熟虑的设计决定吗?
其次,我可以使用.iloc
来引用索引的外部级别,以产生下面的结果吗?我知道我可以先用get_level_values
找到索引的最后一个成员,然后用.loc
-index 找到它,但是如果它可以更直接地完成,可以使用时髦的.iloc
语法或设计的一些现有函数专门针对这种情况。
# df.iloc[-1]
qux one 0.89071 1.75489 1.49564 1.06939
two -0.77271 0.79486 0.31427 -1.32627
【问题讨论】:
索引是一个模拟成表格的线性列表。您会注意到,在第二个示例中,索引-1
实际上由 两个 值组成:Name: (qux, two)
。更多的是 loc
允许引用完整索引(例如 df.loc['qux', 'two']
)或部分索引,但它是有序的。如果你愿意,你可以做一个reset_index
并以其他顺序设置索引。
【参考方案1】:
你可以使用:
df.iloc[[6, 7], :]
Out[1]:
0 1 2 3
first second
qux one -1.253881 -0.637752 0.907105 -1.428681
two -0.140069 -0.861755 -0.255619 -2.798589
其中[6, 7]
对应于这些行的实际行索引,如下所示:
df.reset_index()
Out[]:
first second 0 1 2 3
0 bar one -1.085631 0.997345 0.282978 -1.506295
1 bar two -0.578600 1.651437 -2.426679 -0.428913
2 baz one 1.265936 -0.866740 -0.678886 -0.094709
3 baz two 1.491390 -0.638902 -0.443982 -0.434351
4 foo one 2.205930 2.186786 1.004054 0.386186
5 foo two 0.737369 1.490732 -0.935834 1.175829
6 qux one -1.253881 -0.637752 0.907105 -1.428681
7 qux two -0.140069 -0.861755 -0.255619 -2.798589
这也适用于df.iloc[[-2, -1], :]
或df.iloc[range(-2, 0), :]
。
编辑:将其转变为更通用的解决方案
那么就可以得到一个泛型函数:
def multindex_iloc(df, index):
label = df.index.levels[0][index]
return df.iloc[df.index.get_loc(label)]
multiindex_loc(df, -1)
Out[]:
0 1 2 3
first second
qux one -1.253881 -0.637752 0.907105 -1.428681
two -0.140069 -0.861755 -0.255619 -2.798589
multiindex_loc(df, 2)
Out[]:
0 1 2 3
first second
foo one 2.205930 2.186786 1.004054 0.386186
two 0.737369 1.490732 -0.935834 1.175829
【讨论】:
鉴于我将拥有不同长度的 DataFrame,正在寻找更通用的方法 @BradSolomon:与df.iloc[-1]
相比,它的通用性如何?
与df.iloc[-1]
大致相同......通用性(?)将是理想的
@BradSolomon:做了一个更通用的函数,是不是更好?
感谢您的更新,但我的问题特别询问是否可以在不引用索引级别的情况下这样做,就像您在函数中所做的那样。我想知道是否有为此构建的索引方法或iloc
中的语法可以做到这一点。【参考方案2】:
是的,这是deliberate design decision:
.iloc
是一个严格的位置索引器,它不考虑结构 根本,只有第一个实际行为。 ....loc
确实考虑 考虑级别行为。 [强调]
因此,使用.iloc
以灵活的方式不可能获得问题中给出的所需结果。在几个类似问题中使用的最接近的解决方法是
print(df.loc[[df.index.get_level_values(0)[-1]]])
0 1 2 3
first second
qux one -1.25388 -0.63775 0.90711 -1.42868
two -0.14007 -0.86175 -0.25562 -2.79859
使用double brackets 将保留第一个索引级别。
【讨论】:
我可能遗漏了一些东西,但我需要添加.unique()
以使这项工作按预期进行。 df.loc[[df.index.get_level_values(0).unique()[-1]]]
【参考方案3】:
您可以在使用loc
之前使用swaplevel
方法重新排序索引。
df.swaplevel(0,-1).loc['two']
使用您问题中的示例数据,它看起来像这样:
>>> df
0 1 2 3
first second
bar one -1.085631 0.997345 0.282978 -1.506295
two -0.578600 1.651437 -2.426679 -0.428913
baz one 1.265936 -0.866740 -0.678886 -0.094709
two 1.491390 -0.638902 -0.443982 -0.434351
foo one 2.205930 2.186786 1.004054 0.386186
two 0.737369 1.490732 -0.935834 1.175829
qux one -1.253881 -0.637752 0.907105 -1.428681
two -0.140069 -0.861755 -0.255619 -2.798589
>>> df.loc['bar']
0 1 2 3
second
one -1.085631 0.997345 0.282978 -1.506295
two -0.578600 1.651437 -2.426679 -0.428913
>>> df.swaplevel().loc['two']
0 1 2 3
first
bar -0.578600 1.651437 -2.426679 -0.428913
baz 1.491390 -0.638902 -0.443982 -0.434351
foo 0.737369 1.490732 -0.935834 1.175829
qux -0.140069 -0.861755 -0.255619 -2.798589
swaplevel
是一个 MultiIndex 方法,但是你可以直接在 DataFrame 上调用它。
默认是交换内部两层,所以如果多索引中有两个以上的层,则应明确说明要交换的层。
df.swaplevel(0,-1).loc['two']
【讨论】:
以上是关于`.loc` 和 `.iloc` 与 MultiIndex'd DataFrame的主要内容,如果未能解决你的问题,请参考以下文章