2015-02-27 2 views
10

У меня есть Dataframe с пандами мультииндексным:Как вы обновляете уровни pandas MultiIndex после разрезания его DataFrame?

In [1]: import pandas as pd 
In [2]: multi_index = pd.MultiIndex.from_product([['CAN','USA'],['total']],names=['country','sex']) 
In [3]: df = pd.DataFrame({'pop':[35,318]},index=multi_index) 
In [4]: df 
Out[4]: 
       pop 
country sex 
CAN  total 35 
USA  total 318 

Тогда я удалить некоторые строки из этой DataFrame:

In [5]: df = df.query('pop > 100') 

In [6]: df 
Out[6]: 
       pop 
country sex 
USA  total 318 

Но когда я советуюсь с MutliIndex, она до сих пор обе страны в своих уровнях.

In [7]: df.index.levels[0] 
Out[7]: Index([u'CAN', u'USA'], dtype='object') 

я могу это исправить себя довольно странным образом:

In [8]: idx_names = df.index.names 

In [9]: df = df.reset_index(drop=False) 

In [10]: df = df.set_index(idx_names) 

In [11]: df 
Out[11]: 
       pop 
country sex 
USA  total 318 

In [12]: df.index.levels[0] 
Out[12]: Index([u'USA'], dtype='object') 

Но это кажется довольно грязно. Есть ли лучший способ, который мне не хватает?

ответ

7

Это то, что укусило меня раньше. Удаление столбцов или строк НЕ изменяет базовый MultiIndex по производительности и философским причинам, и это официально не считается ошибкой (read more here). Короткий ответ заключается в том, что разработчики говорят: «Это не то, для чего используется MultiIndex». Если вам нужен список содержимого уровня мультииндексного после модификации, например, для итерации или проверить, чтобы увидеть, если что-то включено, вы можете использовать:

df.index.get_level_values(<levelname>) 

Это возвращает текущие активные значения в пределах этого уровня индекса ,

Так что я предполагаю, что «трюк» здесь является то, что API нативный способ сделать это состоит в использовании get_level_values, а не просто .index или .columns

+0

Oh , и вы можете добавить .unique() к этому, если вы не хотите повторений. Значения уровня по умолчанию включают каждое событие, поэтому вы увидите много дубликатов в типичном сценарии с несколькими индексами. –

+0

Вы также можете использовать 'unique (data.index.values)' для получения значений на всех уровнях. – user2699

0

Я буду удивлен, если есть более «встроенный» способ устранить неиспользуемую страну, чем воссоздать индекс так, как вы делаете (или похожим образом). Если вы посмотрите на индексе до и после среза:

In [165]: df.index 
Out[165]: 
MultiIndex(levels=[[u'CAN', u'USA'], [u'total']], 
      labels=[[0, 1], [0, 0]], 
      names=[u'country', u'sex']) 

In [166]: df = df.query('pop > 100') 

In [167]: df.index 
Out[167]: 
MultiIndex(levels=[[u'CAN', u'USA'], [u'total']], 
      labels=[[1], [0]], 
      names=[u'country', u'sex']) 

вы можете увидеть, что этикетки - которые индексы в значение уровня - обновили, но не значение уровня. Это может быть несовершенная аналогия, но мне кажется, что значения уровня аналогичны перечисляемому столбцу в таблице базы данных, тогда как метки аналогичны фактическим значениям строк в таблице. Если вы удалите все строки таблицы со значением «CAN», это не изменит того факта, что «CAN» по-прежнему является допустимым выбором, основанным на определении столбца. Чтобы удалить «CAN» из перечисления, вам нужно изменить определение столбца; это эквивалент переиндексации фрейма данных в пандах.

5

От версии 0.20.0 использования MultiIndex.remove_unused_levels:

print (df.index) 
MultiIndex(levels=[['CAN', 'USA'], ['total']], 
      labels=[[1], [0]], 
      names=['country', 'sex']) 

df.index = df.index.remove_unused_levels() 

print (df.index) 
MultiIndex(levels=[['USA'], ['total']], 
      labels=[[0], [0]], 
      names=['country', 'sex']) 
Смежные вопросы