web-dev-qa-db-fra.com

sélection sur plusieurs colonnes avec python pandas?

J'ai un cadre de données df dans pandas qui a été construit à l'aide de pandas.read_table à partir d'un fichier csv. La trame de données a plusieurs colonnes et elle est indexée par l'une des colonnes (qui est unique, en ce que chaque ligne a une valeur unique pour cette colonne utilisée pour l'indexation.)

Comment puis-je sélectionner des lignes de ma trame de données en fonction d'un filtre "complexe" appliqué à plusieurs colonnes? Je peux facilement sélectionner la tranche de la trame de données où la colonne colA est supérieure à 10 par exemple:

df_greater_than10 = df[df["colA"] > 10]

Mais que faire si je voulais un filtre comme: sélectionnez la tranche de dfany des colonnes sont supérieures à 10?

Ou lorsque la valeur de colA est supérieure à 10 mais que la valeur de colB est inférieure à 5?

Comment sont-ils mis en œuvre dans les pandas? Merci.

37
user248237

Je vous encourage à poser ces questions sur la liste de diffusion , mais dans tous les cas, c'est toujours une affaire de très bas niveau qui fonctionne avec les tableaux NumPy sous-jacents. Par exemple, pour sélectionner des lignes où la valeur d'une colonne dépasse, disons, 1,5 dans cet exemple:

In [11]: df
Out[11]: 
            A        B        C        D      
2000-01-03 -0.59885 -0.18141 -0.68828 -0.77572
2000-01-04  0.83935  0.15993  0.95911 -1.12959
2000-01-05  2.80215 -0.10858 -1.62114 -0.20170
2000-01-06  0.71670 -0.26707  1.36029  1.74254
2000-01-07 -0.45749  0.22750  0.46291 -0.58431
2000-01-10 -0.78702  0.44006 -0.36881 -0.13884
2000-01-11  0.79577 -0.09198  0.14119  0.02668
2000-01-12 -0.32297  0.62332  1.93595  0.78024
2000-01-13  1.74683 -1.57738 -0.02134  0.11596
2000-01-14 -0.55613  0.92145 -0.22832  1.56631
2000-01-17 -0.55233 -0.28859 -1.18190 -0.80723
2000-01-18  0.73274  0.24387  0.88146 -0.94490
2000-01-19  0.56644 -0.49321  1.17584 -0.17585
2000-01-20  1.56441  0.62331 -0.26904  0.11952
2000-01-21  0.61834  0.17463 -1.62439  0.99103
2000-01-24  0.86378 -0.68111 -0.15788 -0.16670
2000-01-25 -1.12230 -0.16128  1.20401  1.08945
2000-01-26 -0.63115  0.76077 -0.92795 -2.17118
2000-01-27  1.37620 -1.10618 -0.37411  0.73780
2000-01-28 -1.40276  1.98372  1.47096 -1.38043
2000-01-31  0.54769  0.44100 -0.52775  0.84497
2000-02-01  0.12443  0.32880 -0.71361  1.31778
2000-02-02 -0.28986 -0.63931  0.88333 -2.58943
2000-02-03  0.54408  1.17928 -0.26795 -0.51681
2000-02-04 -0.07068 -1.29168 -0.59877 -1.45639
2000-02-07 -0.65483 -0.29584 -0.02722  0.31270
2000-02-08 -0.18529 -0.18701 -0.59132 -1.15239
2000-02-09 -2.28496  0.36352  1.11596  0.02293
2000-02-10  0.51054  0.97249  1.74501  0.20525
2000-02-11  0.10100  0.27722  0.65843  1.73591

In [12]: df[(df.values > 1.5).any(1)]
Out[12]: 
            A       B       C        D     
2000-01-05  2.8021 -0.1086 -1.62114 -0.2017
2000-01-06  0.7167 -0.2671  1.36029  1.7425
2000-01-12 -0.3230  0.6233  1.93595  0.7802
2000-01-13  1.7468 -1.5774 -0.02134  0.1160
2000-01-14 -0.5561  0.9215 -0.22832  1.5663
2000-01-20  1.5644  0.6233 -0.26904  0.1195
2000-01-28 -1.4028  1.9837  1.47096 -1.3804
2000-02-10  0.5105  0.9725  1.74501  0.2052
2000-02-11  0.1010  0.2772  0.65843  1.7359

Plusieurs conditions doivent être combinées à l'aide de & ou | (et entre parenthèses!):

In [13]: df[(df['A'] > 1) | (df['B'] < -1)]
Out[13]: 
            A        B       C        D     
2000-01-05  2.80215 -0.1086 -1.62114 -0.2017
2000-01-13  1.74683 -1.5774 -0.02134  0.1160
2000-01-20  1.56441  0.6233 -0.26904  0.1195
2000-01-27  1.37620 -1.1062 -0.37411  0.7378
2000-02-04 -0.07068 -1.2917 -0.59877 -1.4564

Je serais très intéressé d'avoir une sorte d'API de requête pour faciliter ce genre de choses

44
Wes McKinney

Il y a au moins quelques approches pour raccourcir la syntaxe pour cela dans Pandas, jusqu'à ce qu'il obtienne une API de requête complète sur la route (peut-être que j'essaierai de rejoindre le projet github et que cela est possible si le temps le permet et si personne d'autre ne l'a déjà fait) commencé).

Une méthode pour raccourcir un peu la syntaxe est donnée ci-dessous:

inds = df.apply(lambda x: x["A"]>10 and x["B"]<5, axis=1) 
print df[inds].to_string()

Pour résoudre ce problème, il faudrait construire quelque chose comme les clauses SQL select et where dans Pandas. Ce n'est pas trivial du tout, mais je pense que cela pourrait fonctionner pour cela est d'utiliser le module intégré Python operator. Cela vous permet de traiter des choses comme plus -que comme fonctions au lieu de symboles. Vous pouvez donc faire ce qui suit:

def pandas_select(dataframe, select_dict):

    inds = dataframe.apply(lambda x: reduce(lambda v1,v2: v1 and v2, 
                           [elem[0](x[key], elem[1]) 
                           for key,elem in select_dict.iteritems()]), axis=1)
    return dataframe[inds]

Ensuite, un exemple de test comme le vôtre serait le suivant:

import operator
select_dict = {
               "A":(operator.gt,10),
               "B":(operator.lt,5)                  
              }

print pandas_select(df, select_dict).to_string()

Vous pouvez raccourcir encore plus la syntaxe en créant plus d'arguments à pandas_select pour gérer automatiquement les différents opérateurs logiques communs, ou en les important dans l'espace de noms avec des noms plus courts.

Notez que le pandas_select la fonction ci-dessus ne fonctionne qu'avec des chaînes de contraintes logiques et. Vous devez le modifier pour obtenir un comportement logique différent. Ou utilisez not et les lois de DeMorgan.

5
ely

Une fonction de requête a été ajoutée à Pandas depuis que cette question a été posée et répondue. Un exemple est donné ci-dessous.

Compte tenu de cet exemple de trame de données:

periods = 8
dates = pd.date_range('20170101', periods=periods)
Rand_df = pd.DataFrame(np.random.randn(periods,4), index=dates, 
      columns=list('ABCD'))

La syntaxe de requête comme suit vous permettra d'utiliser plusieurs filtres, comme une clause "WHERE" dans une instruction select.

Rand_df.query("A < 0 or B < 0")

Voir documentation Pandas pour plus de détails.

2
M. K. Hunter