Comment interroger les valeurs des colonnes d'index MultiIndex dans pandas
exemple de Code:
In [171]: A = np.array([1.1, 1.1, 3.3, 3.3, 5.5, 6.6])
In [172]: B = np.array([111, 222, 222, 333, 333, 777])
In [173]: C = randint(10, 99, 6)
In [174]: df = pd.DataFrame(zip(A, B, C), columns=['A', 'B', 'C'])
In [175]: df.set_index(['A', 'B'], inplace=True)
In [176]: df
Out[176]:
C
A B
1.1 111 20
222 31
3.3 222 24
333 65
5.5 333 22
6.6 777 74
maintenant, je veux récupérer une valeur:
T1: dans l'intervalle [3.3, 6.6] - valeur de retour attendue: [3.3, 5.5, 6.6] ou [3.3, 3.3, 5.5, 6.6] dans le cas de la dernière inclus, et [3.3, 5.5] ou [3.3, 3.3, 5.5] si pas.
T2: dans l'intervalle [2.0, 4.0] - valeur de retour attendue: [3.3] ou [3.3, 3.3]
autres MultiIndex dimension, par exemple B valeurs:
T3: dans la gamme [111, 500] avec répétitions, comme nombre de lignes de données dans la gamme-valeur de retour attendue: [111, 222, 222, 333, 333]
plus formel:
supposons que T est un tableau avec les colonnes A, B et C. Le tableau inclut n lignes. Les cellules de Table sont des nombres, par exemple un double, B et c entiers. Nous allons créer un DataFrame de la table T, appelons-la DF. Définissons les index des colonnes A et B DE DF (sans duplication, c'est-à-dire pas de colonnes séparées A et B comme index, et séparées comme données), c'est-à-dire A et B dans ce cas MultiIndex.
Questions:
- comment écrire une requête sur l'index, par exemple, pour interroger l'index A (ou B), disons dans l'intervalle des étiquettes [120.0, 540.0]? Les étiquettes 120.0 et 540.0 existent. Je dois préciser que je ne m'intéresse qu'à la liste des indices en réponse à la question!
- Comment faire la même chose, mais en cas de les étiquettes 120.0 et 540.0 n'existent pas, mais il y a des étiquettes de valeur inférieure à 120, supérieur à 120 et inférieur à 540 ou supérieur à 540?
- dans le cas où la réponse pour Q1 et Q2 était des valeurs d'index uniques, maintenant les mêmes, mais avec des répétitions, comme le nombre de lignes de données dans l'intervalle d'index.
je connais les réponses aux questions ci-dessus dans le cas de colonnes qui ne sont pas des indices, mais dans le cas index, après une longue recherche dans le web et l'expérimentation avec le les fonctionnalités de pandas, je n'ai pas réussi. La seule méthode (sans programmation supplémentaire) que je vois maintenant est d'avoir un double de A et B comme colonnes de données en plus de l'index.
3 réponses
requête df par MultiIndex valeurs, par exemple, où (a > 1.7) et (B < 666):
In [536]: result_df = df.loc[(df.index.get_level_values('A') > 1.7) & (df.index.get_level_values('B') < 666)]
In [537]: result_df
Out[537]:
C
A B
3.3 222 43
333 59
5.5 333 56
donc, pour obtenir par exemple le 'A' valeurs de l'indice, si encore:
In [538]: result_df.index.get_level_values('A')
Out[538]: Index([3.3, 3.3, 5.5], dtype=object)
le problème est que dans les grands cadres de données la performance de par index sélection moins bonne de 10% que la sélection des lignes régulières triées. Et dans le travail répétitif, en boucle, le retard accumulé. Voir exemple:
In [558]: df = store.select(STORE_EXTENT_BURSTS_DF_KEY)
In [559]: len(df)
Out[559]: 12857
In [560]: df.sort(inplace=True)
In [561]: df_without_index = df.reset_index()
In [562]: %timeit df.loc[(df.index.get_level_values('END_TIME') > 358200) & (df.index.get_level_values('START_TIME') < 361680)]
1000 loops, best of 3: 562 µs per loop
In [563]: %timeit df_without_index[(df_without_index.END_TIME > 358200) & (df_without_index.START_TIME < 361680)]
1000 loops, best of 3: 507 µs per loop
Pour une meilleure lisibilité, nous pouvons simplement utiliser query()
Méthode, pour éviter les longues df.index.get_level_values()
et reset_index
/set_index
aller - retour.
Voici la cible DataFrame
:
In [12]: df
Out[12]:
C
A B
1.1 111 68
222 40
3.3 222 20
333 11
5.5 333 80
6.6 777 51
Réponse de T1 (A
[3.3, 6.6]
):
In [13]: df.query('3.3 <= A <= 6.6') # for closed interval
Out[13]:
C
A B
3.3 222 20
333 11
5.5 333 80
6.6 777 51
In [14]: df.query('3.3 < A < 6.6') # for open interval
Out[14]:
C
A B
5.5 333 80
et bien sûr, on peut jouer avec <, <=, >, >=
pour tout type de inclusion.
de même, répondez T2 (A
[2.0, 4.0]
):
In [15]: df.query('2.0 <= A <= 4.0')
Out[15]:
C
A B
3.3 222 20
333 11
Réponse de T3 (B
[111, 500]
):
In [16]: df.query('111 <= B <= 500')
Out[16]:
C
A B
1.1 111 68
222 40
3.3 222 20
333 11
5.5 333 80
et de plus, vous pouvez combinez la requête pour col A
et B
très naturellement!
In [17]: df.query('0 < A < 4 and 150 < B < 400')
Out[17]:
C
A B
1.1 222 40
3.3 222 20
333 11
Avec un "flotter", comme l'indice que vous voulez toujours l'utiliser comme une colonne plutôt qu'une indexation directe de l'action. Toutes ces mesures fonctionneront, que les paramètres existent ou non.
In [11]: df
Out[11]:
C
A B
1.1 111 81
222 45
3.3 222 98
333 13
5.5 333 89
6.6 777 98
In [12]: x = df.reset_index()
T1
In [13]: x.loc[(x.A>=3.3)&(x.A<=6.6)]
Out[13]:
A B C
2 3.3 222 98
3 3.3 333 13
4 5.5 333 89
5 6.6 777 98
T2
In [14]: x.loc[(x.A>=2.0)&(x.A<=4.0)]
Out[14]:
A B C
2 3.3 222 98
3 3.3 333 13
T3
In [15]: x.loc[(x.B>=111.0)&(x.B<=500.0)]
Out[15]:
A B C
0 1.1 111 81
1 1.1 222 45
2 3.3 222 98
3 3.3 333 13
4 5.5 333 89
si vous voulez récupérer les indices, il vous suffit de les régler. C'est une opération pas cher.
In [16]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B'])
Out[16]:
C
A B
1.1 111 81
222 45
3.3 222 98
333 13
5.5 333 89
Si vous voulez VRAIMENT les réelles valeurs de l'indice
In [5]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B']).index
Out[5]:
MultiIndex
[(1.1, 111), (1.1, 222), (3.3, 222), (3.3, 333), (5.5, 333)]