Индекс DataFrame с (Multi)Index, содержащим только некоторые уровни DataFrame

Я хочу взять фрейм данных с мультииндексом и проиндексировать его другим (мульти) индексом, содержащим строгое подмножество уровней фрейма данных. Уровни в фрейме данных, а не в другом (мульти)индексе, должны иметь все возвращаемые строки. Пример:

>>> df
              col
num chr
1   a    0.845402
    b    0.099432
    c    0.507409
2   a    0.684363
    b    0.582436
    c    0.666528

>>> df['col'].unstack('chr').mean()
chr
a    0.764883
b    0.340934
c    0.586968
dtype: float64

>>> df['col'].unstack('chr').mean().nsmallest(2)
chr
b    0.340934
c    0.586968
dtype: float64

>>> df['col'].unstack('chr').mean().nsmallest(2).index
Index(['b', 'c'], dtype='object', name='chr')

Теперь я хотел бы вернуть все строки df, содержащие 'b' или 'c' на уровне 'chr', и любое значение на уровне 'num'. Кроме того, я хотел бы попробовать то же самое, когда индекс, возвращаемый на последнем шаге, представляет собой MultiIndex (т.е. когда индекс df имеет более двух уровней):

>>> df
                  col
num chr foo
1   a   bar  0.790995
        baz  0.883363
    b   bar  0.240376
        baz  0.309544
    c   bar  0.637943
        baz  0.265628
2   a   bar  0.783172
        baz  0.612230
    b   bar  0.729979
        baz  0.846814
    c   bar  0.809676
        baz  0.821503

>>> df['col'].unstack(['chr', 'foo']).mean()
chr  foo
a    bar    0.787084
     baz    0.747796
b    bar    0.485177
     baz    0.578179
c    bar    0.723809
     baz    0.543565
dtype: float64

>>> df['col'].unstack(['chr', 'foo']).mean().nsmallest(2)
chr  foo
b    bar    0.485177
c    baz    0.543565
dtype: float64

>>> df['col'].unstack(['chr', 'foo']).mean().nsmallest(2).index
MultiIndex(levels=[['a', 'b', 'c'], ['bar', 'baz']],
           labels=[[1, 2], [0, 1]],
           names=['chr', 'foo'])

Я хотел бы выбрать все строки df, индекс которых содержит ('b', 'bar') или ('c', 'baz') на последних двух уровнях и любое значение на уровне 'num'.


person BallpointBen    schedule 20.04.2018    source источник


Ответы (2)


Я могу сделать это только с get_level_values и isin

s=df['col'].mean(level=[1,2]).nsmallest(2).index.tolist()

df[pd.Series(list(zip(df.index.get_level_values(1),df.index.get_level_values(2)))).isin(s).values]
Out[163]:
                  col
num chr foo
1   b   bar  0.240376
    c   baz  0.265628
2   b   bar  0.729979
    c   baz  0.821503
person BENY    schedule 20.04.2018
comment
Ой... надеюсь, есть более идиоматичное решение - person BallpointBen; 20.04.2018
comment
@BallpointBen, почему бы не reset_index()? множественный индекс все еще находится в стадии разработки, поэтому у него может быть больше потенциальных проблем - person BENY; 20.04.2018

Я думаю, что самым чистым решением может быть извлечение нужных строк по отдельности и их pd.concat. Для решения Index:

idx = df['col'].unstack(['chr', 'foo']).mean().nsmallest(2).index
selected = pd.concat([df.xs(label, level=idx.name, drop_level=False) 
                      for label in idx],
                     axis=0)

Если idx является MultiIndex, используйте вместо него level=idx.names.

person BallpointBen    schedule 20.04.2018