web-dev-qa-db-fra.com

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 les valeurs A:
Q1: dans l'intervalle [3.3, 6.6] - valeur de retour attendu: [3.3, 5.5, 6.6] ou [3.3, 3.3, 5.5, 6.6] en dernier lieu inclus, et [ 3.3, 5.5] ou [3.3, 3.3, 5.5] sinon.
Q2: dans l'intervalle [2.0, 4.0] - valeur de retour attendu: [3.3] ou [3.3, 3.3]

Idem pour toute autre dimension MultiIndex , par exemple les valeurs B:
Q: dans la plage [111, 500] avec répétitions, en nombre de lignes de données dans la plage - 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 comprend n lignes. Les cellules de tableau sont des nombres, par exemple des entiers doubles A, B et C. Créons un DataFrame de la table T, appelons-le DF. Définissons les index des colonnes A et B de DF (sans duplication, c.-à-d. Pas de colonnes séparées A et B en tant qu’index et séparées en tant que données), c’est-à-dire A et B dans ce cas MultiIndex .

Des questions:

  1. Comment écrire une requête sur l'index, par exemple, pour interroger l'index A (ou B), par exemple 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 index en réponse à la requête!
  2. Comment faire la même chose, mais dans le cas où les étiquettes 120.0 et 540.0 n'existent pas, mais il existe des étiquettes de valeur inférieure à 120, supérieure à 120 et inférieure à 540 ou supérieure à 540?
  3. Dans le cas où la réponse pour Q1 et Q2 était des valeurs d'index uniques, maintenant identiques, mais avec des répétitions, en tant que nombre de lignes de données dans la plage d'index.

Je connais les réponses aux questions ci-dessus dans le cas de colonnes qui ne sont pas des index, mais dans le cas des index, après une longue recherche sur le Web et une expérimentation des 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 duplicata de A et B en tant que colonnes de données en plus d’index.

54
Vyacheslav Shkolyar

Pour interroger les df par les valeurs MultiIndex, 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

Par conséquent, pour obtenir par exemple les 'A' valeurs d'index, si toujours requis:

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 le cas de données volumineuses, les performances de par index sont 10% plus mauvaises 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
62

Pour une meilleure lisibilité , nous pouvons simplement utiliser la query() Méthode , afin d'éviter la longue fonction df.index.get_level_values() et reset_index/set_index aller et venir.

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 pour Q1 (A dans la plage [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 d'inclusion.


De même, répondez pour Q2 (A dans la plage [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 pour Q3 (B dans la plage [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 en plus, vous pouvez [~ # ~] combiner [~ # ~] 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
31
YaOzI

Avec un index "float", vous voulez toujours l'utiliser comme une colonne plutôt que comme une action d'indexation directe. Celles-ci fonctionneront toutes, que les terminaux 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()

Q1

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

Q2

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

Q3

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 souhaitez récupérer les index, définissez-les simplement. C'est une opération peu coûteuse.

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 valeurs d'index réelles

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
Jeff