pandas: conditions multiples lors de l'indexation de la trame de données - comportement inattendu

135

Je filtre les lignes d'un dataframe par valeurs dans deux colonnes.

Pour une raison quelconque, l'opérateur OR se comporte comme je m'attendrais à ce que l'opérateur AND se comporte et vice versa.

Mon code de test:

import pandas as pd

df = pd.DataFrame({'a': range(5), 'b': range(5) })

# let's insert some -1 values
df['a'][1] = -1
df['b'][1] = -1
df['a'][3] = -1
df['b'][4] = -1

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a != -1) | (df.b != -1)]

print pd.concat([df, df1, df2], axis=1,
                keys = [ 'original df', 'using AND (&)', 'using OR (|)',])

Et le résultat:

      original df      using AND (&)      using OR (|)    
             a  b              a   b             a   b
0            0  0              0   0             0   0
1           -1 -1            NaN NaN           NaN NaN
2            2  2              2   2             2   2
3           -1  3            NaN NaN            -1   3
4            4 -1            NaN NaN             4  -1

[5 rows x 6 columns]

Comme vous pouvez le voir, l' ANDopérateur supprime chaque ligne dans laquelle au moins une valeur est égale -1. D'autre part, l' ORopérateur exige que les deux valeurs soient égales à -1pour les supprimer. Je m'attendrais exactement au résultat opposé. Quelqu'un pourrait-il expliquer ce comportement, s'il vous plaît?

J'utilise pandas 0.13.1.

Wojciech Walczak
la source
1
df.queryet pd.evalsemblent bien adaptés à ce cas d'utilisation. Pour plus d'informations sur la pd.eval()famille de fonctions, leurs caractéristiques et leurs cas d'utilisation, veuillez visiter Évaluation des expressions dynamiques dans les pandas à l'aide de pd.eval () .
cs95

Réponses:

211

Comme vous pouvez le voir, l'opérateur AND supprime chaque ligne dans laquelle au moins une valeur est égale à -1. D'autre part, l'opérateur OR exige que les deux valeurs soient égales à -1 pour les supprimer.

C'est vrai. N'oubliez pas que vous écrivez la condition en termes de ce que vous voulez conserver , pas en termes de ce que vous voulez laisser tomber. Pour df1:

df1 = df[(df.a != -1) & (df.b != -1)]

Vous dites "conserver les lignes dans lesquelles df.an'est pas -1 et df.bn'est pas -1", ce qui revient à supprimer toutes les lignes dans lesquelles au moins une valeur est -1.

Pour df2:

df2 = df[(df.a != -1) | (df.b != -1)]

Vous dites "conserver les lignes dans lesquelles soit df.aou df.bnon -1", ce qui revient à supprimer des lignes où les deux valeurs sont -1.

PS: un accès enchaîné df['a'][1] = -1peut vous causer des ennuis. Il vaut mieux prendre l'habitude d'utiliser .locet .iloc.

DSM
la source
24
DataFrame.query()fonctionne bien ici aussi. df.query('a != -1 or b != -1').
Phillip Cloud
5
Arriver à savoir pourquoi les pandas veulent &et |encore andet or?
poêles
3
@stoves: en code Python normal, andet oront une sémantique Python de base qui ne peut pas être modifiée &et |, d'autre part, ont des méthodes spéciales correspondantes qui contrôlent leur comportement. (Dans les chaînes de requête, bien sûr, nous sommes libres d'appliquer l'analyse de notre choix.)
DSM
intéressant, cela semble df[True & False]échouer mais df[(True) & (False)]réussit (non testé sur cet exemple)
3pitt
Serait-il possible de briser ce type de syntaxe sur plusieurs lignes? Quel serait le plus PEP8?
tommy.carstensen
41

Vous pouvez utiliser query () , c'est-à-dire:

df_filtered = df.query('a == 4 & b != 2')
CONvid19
la source
J'ai une situation où je pense que cette syntaxe a plus de sens, par exemple: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10
9

Un peu de théorie de la logique mathématique ici:

"NOT a AND NOT b" équivaut à "NOT (a OR b)" , donc:

"a NOT -1 AND b NOT -1" est équivalent à "NOT (a is -1 OR b is -1)" , qui est l'opposé (Complement) de "(a is -1 OR b is -1)" .

Donc, si vous voulez un résultat exactement opposé, df1 et df2 devraient être comme ci-dessous:

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
Jake
la source