Преимущества panda multiindex?
Итак, я узнал, что могу использовать DataFrame.groupby, не имея MultiIndex для подвыборки/поперечных сечений.
С другой стороны, когда у меня есть MultiIndex на DataFrame, мне все равно нужно использовать DataFrame.groupby для выполнения суб-выборки/сечений.
Итак, что такое MultiIndex, за исключением весьма полезного и красивого отображения иерархии при печати?
Ответы
Ответ 1
Иерархическая индексация (также называемая "многоуровневая" индексация) была введена в релиз pandas 0.4.
Это открывает двери для довольно сложного анализа данных и манипуляций, особенно для работы с более объемными данными. По сути, он позволяет эффективно хранить и манипулировать произвольно большими размерными данными в двумерной табличной структуре (DataFrame), например.
Представьте, что вы создаете кадр данных с помощью MultiIndex
следующим образом: -
import pandas as pd
import numpy as np
np.arrays = [['one','one','one','two','two','two'],[1,2,3,1,2,3]]
df = pd.DataFrame(np.random.randn(6,2),index=pd.MultiIndex.from_tuples(list(zip(*np.arrays))),columns=['A','B'])
df # This is the dataframe we have generated
A B
one 1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
two 1 -0.101713 -1.204458
2 0.958008 -0.455419
3 -0.191702 -0.915983
Этот df
представляет собой просто структуру данных двух измерений
df.ndim
2
Но мы можем представить это, глядя на результат, как на трехмерную структуру данных.
-
one
с 1
с данными -0.732470 -0.313871
.
-
one
с 2
с данными -0.031109 -2.068794
.
-
one
с 3
с данными 1.520652 0.471764
.
A.k.a.: "эффективно хранить и манипулировать произвольно большими размерными данными в двумерной табличной структуре"
Это не просто "симпатичный дисплей". Это позволяет легко извлекать данные, так как теперь у нас есть иерархический индекс.
Например.
In [44]: df.ix["one"]
Out[44]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
предоставит нам новый фрейм данных только для группы данных, принадлежащих "одному".
И мы можем сузить наш выбор данных, сделав следующее: -
In [45]: df.ix["one"].ix[1]
Out[45]:
A -0.732470
B -0.313871
Name: 1
И, конечно, если мы хотим получить конкретное значение, вот пример: -
In [46]: df.ix["one"].ix[1]["A"]
Out[46]: -0.73247029752040727
Итак, если у нас есть еще больше индексов (помимо 2 указателей, показанных в примере выше), мы можем существенно развернуть и выбрать набор данных, который нам действительно интересен без необходимости groupby
.
Мы даже можем захватить поперечный разрез (строки или столбцы) из нашего фрейма данных...
По строкам: -
In [47]: df.xs('one')
Out[47]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
По столбцам: -
In [48]: df.xs('B', axis=1)
Out[48]:
one 1 -0.313871
2 -2.068794
3 0.471764
two 1 -1.204458
2 -0.455419
3 -0.915983
Name: B
Ответ 2
Отличный пост от @Calvin Cheng, но подумал, что я тоже ударю на это.
Когда использовать MultiIndex:
- Когда одного значения столбца недостаточно, чтобы однозначно идентифицировать строку.
- Когда данные логически иерархичны - это означает, что у него есть несколько измерений или "уровни".
Почему (ваш основной вопрос) - по крайней мере, это самые большие преимущества ИМО:
- Легкая манипуляция с помощью stack() и unstack()
- Легкая математика при наличии нескольких уровней столбцов
- Синтаксический сахар для нарезки/фильтрации
Пример:
Dollars Units
Date Store Category Subcategory UPC EAN
2018-07-10 Store 1 Alcohol Liqour 80480280024 154.77 7
Store 2 Alcohol Liqour 80480280024 82.08 4
Store 3 Alcohol Liqour 80480280024 259.38 9
Store 1 Alcohol Liquor 80432400630 477.68 14
674545000001 139.68 4
Store 2 Alcohol Liquor 80432400630 203.88 6
674545000001 377.13 13
Store 3 Alcohol Liquor 80432400630 239.19 7
674545000001 432.32 14
Store 1 Beer Ales 94922755711 65.17 7
702770082018 174.44 14
736920111112 50.70 5
Store 2 Beer Ales 94922755711 129.60 12
702770082018 107.40 10
736920111112 59.65 5
Store 3 Beer Ales 94922755711 154.00 14
702770082018 137.40 10
736920111112 107.88 12
Store 1 Beer Lagers 702770081011 156.24 12
Store 2 Beer Lagers 702770081011 137.06 11
Store 3 Beer Lagers 702770081011 119.52 8
1) Если мы хотим легко сравнивать продажи через магазины, мы можем использовать df.unstack('Store')
чтобы объединить все бок о бок:
Dollars Units
Store Store 1 Store 2 Store 3 Store 1 Store 2 Store 3
Date Category Subcategory UPC EAN
2018-07-10 Alcohol Liqour 80480280024 154.77 82.08 259.38 7 4 9
Liquor 80432400630 477.68 203.88 239.19 14 6 7
674545000001 139.68 377.13 432.32 4 13 14
Beer Ales 94922755711 65.17 129.60 154.00 7 12 14
702770082018 174.44 107.40 137.40 14 10 10
736920111112 50.70 59.65 107.88 5 5 12
Lagers 702770081011 156.24 137.06 119.52 12 11 8
2) Мы также можем легко выполнить математику на нескольких столбцах. Например, df['Dollars']/df['Units']
будет делить каждый доллар магазина на свои единицы, для каждого магазина без нескольких операций:
Store Store 1 Store 2 Store 3
Date Category Subcategory UPC EAN
2018-07-10 Alcohol Liqour 80480280024 22.11 20.52 28.82
Liquor 80432400630 34.12 33.98 34.17
674545000001 34.92 29.01 30.88
Beer Ales 94922755711 9.31 10.80 11.00
702770082018 12.46 10.74 13.74
736920111112 10.14 11.93 8.99
Lagers 702770081011 13.02 12.46 14.94
3) Если мы хотим фильтровать только определенные строки, вместо использования
df[(df[col1] == val1) and (df[col2] == val2) and (df[col3] == val3)]
формат, мы можем вместо этого.xs или.query (да, они работают для обычных dfs, но это не очень полезно). Вместо этого синтаксис будет выглядеть следующим образом:
df.xs((val1, val2, val3), level=(col1, col2, col3))
Дополнительные примеры можно найти в этом учебнике, который я собрал вместе.