J'ai donc appris que je peux utiliser DataFrame.groupby sans avoir un MultiIndex pour faire des sous-échantillonnages/coupes transversales.
D'un autre côté, lorsque j'ai un MultiIndex sur un DataFrame, j'ai toujours besoin d'utiliser DataFrame.groupby pour faire du sous-échantillonnage/coupes transversales.
Alors, à quoi sert un MultiIndex en dehors de l'affichage assez utile et joli des hiérarchies lors de l'impression?
L'indexation hiérarchique (également appelée indexation "à plusieurs niveaux") a été introduite dans la version pandas 0.4).
Cela ouvre la porte à une analyse et une manipulation des données assez sophistiquées, en particulier pour travailler avec des données de dimension supérieure. En substance, il vous permet de stocker et de manipuler efficacement des données de dimension arbitrairement élevée dans une structure tabulaire à 2 dimensions (DataFrame), par exemple.
Imaginez construire une trame de données en utilisant MultiIndex
comme ceci: -
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
Ce df
est simplement une structure de données à deux dimensions
df.ndim
2
Mais nous pouvons l'imaginer, en regardant la sortie, comme une structure de données en 3 dimensions.
one
avec 1
avec données -0.732470 -0.313871
.one
avec 2
avec données -0.031109 -2.068794
.one
avec 3
avec données 1.520652 0.471764
.A.k.a .: "stocker et manipuler efficacement des données de dimensions arbitrairement élevées dans une structure tabulaire bidimensionnelle"
Ce n'est pas seulement un "joli affichage". Il présente l'avantage de retrouver facilement les données, car nous avons maintenant un index hiérarchique.
Par exemple.
In [44]: df.ix["one"]
Out[44]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
nous donnera une nouvelle trame de données uniquement pour le groupe de données appartenant à "un".
Et nous pouvons affiner notre sélection de données en procédant ainsi: -
In [45]: df.ix["one"].ix[1]
Out[45]:
A -0.732470
B -0.313871
Name: 1
Et bien sûr, si nous voulons une valeur spécifique, voici un exemple: -
In [46]: df.ix["one"].ix[1]["A"]
Out[46]: -0.73247029752040727
Donc, si nous avons encore plus d'index (en plus des 2 index illustrés dans l'exemple ci-dessus), nous pouvons essentiellement explorer et sélectionner l'ensemble de données qui nous intéresse vraiment sans avoir besoin de groupby
.
Nous pouvons même saisir une coupe transversale (lignes ou colonnes) de notre dataframe ...
Par rangées: -
In [47]: df.xs('one')
Out[47]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
Par colonnes: -
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
Excellent article de @Calvin Cheng, mais j'ai pensé que j'essaierais aussi.
Quand utiliser un MultiIndex:
Pourquoi (votre question principale) - au moins ce sont les plus grands avantages de l'OMI:
Exemple:
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) Si nous voulons comparer facilement les ventes entre les magasins, nous pouvons utiliser df.unstack('Store')
pour tout aligner côte à côte:
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) Nous pouvons également facilement faire des calculs sur plusieurs colonnes. Par exemple, df['Dollars'] / df['Units']
Divisera ensuite les dollars de chaque magasin par ses unités, pour chaque magasin sans opérations multiples:
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) Si nous voulons ensuite filtrer sur des lignes spécifiques, au lieu d'utiliser le
df[(df[col1] == val1) and (df[col2] == val2) and (df[col3] == val3)]
format, nous pouvons à la place .xs ou .query (oui ceux-ci fonctionnent pour les dfs ordinaires, mais ce n'est pas très utile). La syntaxe serait plutôt:
df.xs((val1, val2, val3), level=(col1, col2, col3))
Plus d'exemples peuvent être trouvés dans ce cahier de tutoriel J'ai mis en place.