Как вы обновляете уровни pandas MultiIndex после резки его DataFrame?
У меня есть Dataframe с pandas MultiIndex:
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')
Но это кажется довольно грязным. Есть ли лучший способ, которым я не хватает?
Ответы
Ответ 1
Это то, что укусило меня раньше. Удаление столбцов или строк НЕ изменяет базовый MultiIndex по производительности и философским соображениям, и это официально не считается ошибкой (читайте подробнее здесь), Короткий ответ заключается в том, что разработчики говорят, что "это не то, для чего используется MultiIndex". Если вам понадобится список содержимого уровня MultiIndex после модификации, например, для итерации или для проверки того, включено ли что-либо, вы можете использовать:
df.index.get_level_values(<levelname>)
Это возвращает текущие активные значения в пределах этого уровня индекса.
Итак, я думаю, что "трюк" здесь заключается в том, что для этого API-интерфейса это использовать get_level_values, а не только .index или .columns
Ответ 2
Из версии 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'])
Ответ 3
Я удивлюсь, если есть более "встроенный" способ устранить неиспользуемую страну, чем воссоздать индекс так, как вы делаете (или похожим образом). Если вы посмотрите на свой индекс до и после среза:
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" из перечисления, вам нужно изменить определение столбца; это эквивалент переиндексации фрейма данных в pandas.