Pandas DataFrame - Как получить определенные комбинации уровней MultiIndex - PullRequest
0 голосов
/ 14 октября 2018

У меня есть следующий DataFrame, который использует трехуровневый MultiIndex:

In [1]: iterables = [[1, 2], ['foo', 'bar'], ['one', 'two']]
   ...: midx = pd.MultiIndex.from_product(iterables)
   ...: df = pd.DataFrame(np.random.randn(8), index=midx)
   ...: df

Out[1]:
                  0
1 foo one -0.217594
      two -1.361612
  bar one  2.477790
      two  0.874409
2 foo one  0.403577
      two  0.076111
  bar one  1.423512
      two  0.047898

Я бы хотел разделить индекс так, чтобы я сохранил все на первом уровне, сохраняя только следующие комбинациивторые два уровня: ('foo', 'one') и ('bar', 'two').То есть я хотел бы, чтобы мой вывод выглядел примерно так:

                  0
1 foo one -0.217594
  bar two  0.874409
2 foo one  0.403577
  bar two  0.047898

Возможно ли сделать это в одну строку, используя атрибут, такой как .loc, дляпример?


Я знаю, что я могу взять поперечные сечения желаемых комбинаций отдельно, используя функцию .xs, но я бы предпочел более короткий, более похожий на срезсинтаксис.В частности, наличие однострочника важно для моего варианта использования.

Кажется, что должно работать следующее:

df.loc[[(slice(None), 'foo', 'one'), (slice(None), 'bar', 'two')]]

Но это приводит к TypeError: unhashable type: 'slice'.

Ответы [ 3 ]

0 голосов
/ 14 октября 2018

Вот решение на основе DataFrame.query.Ваш MultiIndex имеет неназванные уровни, но к уровню k можно получить доступ со специальным именем ilevel_k, в соответствии с документами : «Если уровни MultiIndex не названы, вы можете обратиться к нимиспользуя специальные имена. "

query_string = ('(ilevel_1 == "foo" & ilevel_2 == "one") | '
                '(ilevel_1 == "bar" & ilevel_2 == "two")')

df.query(query_string)
                  0
1 foo one -0.217594
  bar two  0.874409
2 foo one  0.403577
  bar two  0.047898
0 голосов
/ 14 октября 2018

Решения от jpp и Peter хорошие.В случае, если кто-то найдет это полезным, вот другой подход, использующий ряд в качестве фильтра.

f = pd.Series([True]*len(midx), index=midx)
f.loc[:,'foo','two'] = False
f.loc[:,'bar','one'] = False

print(df[f[df.index]])
                  0
1 foo one -0.185593
  bar two -1.265191
2 foo one  0.490959
  bar two  0.414753
0 голосов
/ 14 октября 2018

Вы можете создать булеву маску, сначала отбросив первый уровень индекса, а затем используя pd.Index.isin со списком кортежей:

df_masked = df[df.index.droplevel(0).isin([('foo', 'one'), ('bar', 'two')])]

print(df_masked)

                  0
1 foo one  1.510316
  bar two  0.260862
2 foo one  0.813745
  bar two  0.023386
...