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:

  1. 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!
  2. 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?
  3. 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.

32
demandé sur Mel 2013-07-29 13:56:50

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
46
répondu Vyacheslav Shkolyar 2013-08-07 12:51:15

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
21
répondu YaOzI 2018-07-11 06:33:14

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)]
9
répondu Jeff 2013-07-29 13:16:22