При индексации мультииндексированного 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 - индекс с этим, но бродил, если это можно сделать более напрямую, либо с помощью funky .iloc синтаксис или некоторые существующие функции, разработанные специально для этого случая.

# df.iloc[-1]
qux   one     0.89071  1.75489  1.49564  1.06939
      two    -0.77271  0.79486  0.31427 -1.32627
14
Brad Solomon 30 Авг 2017 в 21:44

3 ответа

Лучший ответ

Да, это обдуманное дизайнерское решение:

.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

Использование двойных скобок сохранит первый уровень индекса.

4
Brad Solomon 31 Авг 2017 в 04:33

Вы можете использовать:

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
1
FabienP 30 Авг 2017 в 20:34

Вы можете использовать swaplevel метод переупорядочения индекса перед использованием loc.

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']
0
Håken Lid 15 Авг 2019 в 16:01