`.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的主要内容,如果未能解决你的问题,请参考以下文章

大熊猫中的“iloc”和“loc”是啥?

pandas的loc与iloc

熊猫 iloc 返回与 loc 不同的范围 [重复]

Day3:loc()与iloc()

Python 得到 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因

Pandas——ix 与 loc 与 iloc 与 icol 的区别