Преимущества 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:

  1. Когда одного значения столбца недостаточно, чтобы однозначно идентифицировать строку.
  2. Когда данные логически иерархичны - это означает, что у него есть несколько измерений или "уровни".

Почему (ваш основной вопрос) - по крайней мере, это самые большие преимущества ИМО:

  1. Легкая манипуляция с помощью stack() и unstack()
  2. Легкая математика при наличии нескольких уровней столбцов
  3. Синтаксический сахар для нарезки/фильтрации

Пример:

                                                       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))

Дополнительные примеры можно найти в этом учебнике, который я собрал вместе.