Je travaille avec un index booléen dans Pandas. La question est de savoir pourquoi la déclaration:
a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]
fonctionne bien alors que
a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]
quitte avec une erreur?
Exemple:
a=pd.DataFrame({'x':[1,1],'y':[10,20]})
In: a[(a['x']==1)&(a['y']==10)]
Out: x y
0 1 10
In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
and != &
. L'and
opérateur en Python ne peut pas être remplacé, alors que l'&
opérateur (__and__
) le peut. D'où le choix de l'utilisation&
dans numpy et pandas.Réponses:
Quand tu dis
Vous demandez implicitement à Python de convertir
(a['x']==1)
et(a['y']==10)
en valeurs booléennes.Les tableaux NumPy (de longueur supérieure à 1) et les objets Pandas tels que Series n'ont pas de valeur booléenne - en d'autres termes, ils augmentent
lorsqu'il est utilisé comme valeur booléenne. C'est parce qu'il n'est pas clair quand il devrait être vrai ou faux . Certains utilisateurs peuvent supposer qu'ils sont True s'ils ont une longueur non nulle, comme une liste Python. D'autres pourraient désirer que ce soit vrai seulement si tous ses éléments sont vrais. D'autres pourraient vouloir qu'il soit vrai si l' un de ses éléments est vrai.
Parce qu'il y a tellement d'attentes contradictoires, les concepteurs de NumPy et de Pandas refusent de deviner et lèvent à la place une ValueError.
Au lieu de cela, vous devez être explicite, en appelant la méthode
empty()
,all()
ouany()
pour indiquer le comportement souhaité.Dans ce cas, cependant, il semble que vous ne vouliez pas d'évaluation booléenne, vous vouliez des éléments logiques et. C'est ce que fait l'
&
opérateur binaire:renvoie un tableau booléen.
À propos, comme le note alexpmil , les parenthèses sont obligatoires car elles
&
ont une priorité d'opérateur plus élevée que==
. Sans les parenthèses,a['x']==1 & a['y']==10
serait évalué commea['x'] == (1 & a['y']) == 10
ce qui serait à son tour équivalent à la comparaison chaînée(a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10)
. C'est une expression de la formeSeries and Series
. L'utilisation deand
avec deux séries déclencherait à nouveau la même choseValueError
que ci-dessus. C'est pourquoi les parenthèses sont obligatoires.la source
x and y
déclenche l'évaluation debool(x)
etbool(y)
. Python "évalue d'abordx
; six
est faux, sa valeur est renvoyée; sinon,y
est évaluée et la valeur résultante est renvoyée." Ainsi, la syntaxex and y
ne peut pas être utilisée pour la logique élémentaire et puisque seulementx
ouy
peut être retournée. En revanche, lesx & y
déclencheursx.__and__(y)
et la__and__
méthode peuvent être définis pour renvoyer tout ce que nous voulons.==
clause sont obligatoires .a['x']==1 & a['y']==10
renvoie la même erreur que dans la question.TLDR; Les opérateurs logiques dans Pandas sont
&
,|
et~
, et les parenthèses(...)
sont importantes!Python
and
,or
et lesnot
opérateurs logiques sont conçus pour fonctionner avec des scalaires. Les Pandas ont donc dû faire mieux et remplacer les opérateurs binaires pour obtenir une version vectorisée (élément par élément) de cette fonctionnalité.Donc ce qui suit en python (
exp1
et ceexp2
sont des expressions qui s'évaluent en un résultat booléen) ...... se traduira par ...
pour les pandas.
Si, lors de l'exécution d'une opération logique, vous obtenez un
ValueError
, vous devez utiliser des parenthèses pour le regroupement:Par exemple,
Etc.
Indexation booléenne : une opération courante consiste à calculer des masques booléens via des conditions logiques pour filtrer les données. Pandas fournit trois opérateurs:
&
pour ET logique,|
pour OU logique et~
pour NON logique.Considérez la configuration suivante:
ET logique
Pour
df
ci-dessus, disons que vous souhaitez renvoyer toutes les lignes où A <5 et B> 5. Cela se fait en calculant les masques pour chaque condition séparément, et en les AND.&
Opérateur bit à bit surchargéAvant de continuer, veuillez prendre note de cet extrait particulier de la documentation, qui indique
Donc, dans cet esprit, ET logique élément par élément peut être implémenté avec l'opérateur bit à bit
&
:Et l'étape de filtrage suivante est simplement,
Les parenthèses sont utilisées pour remplacer l'ordre de priorité par défaut des opérateurs au niveau du bit, qui ont une priorité plus élevée sur les opérateurs conditionnels
<
et>
. Voir la section sur la priorité des opérateurs dans la documentation python.Si vous n'utilisez pas de parenthèses, l'expression est évaluée de manière incorrecte. Par exemple, si vous tentez accidentellement quelque chose comme
Il est analysé comme
Qui devient,
Ce qui devient (voir la documentation python sur la comparaison d'opérateurs chaînés ),
Qui devient,
Qui jette
Alors, ne faites pas cette erreur! 1
Éviter le regroupement des parenthèses
Le correctif est en fait assez simple. La plupart des opérateurs ont une méthode liée correspondante pour DataFrames. Si les masques individuels sont créés à l'aide de fonctions au lieu d'opérateurs conditionnels, vous n'aurez plus besoin de regrouper par parenthèses pour spécifier l'ordre d'évaluation:
Voir la section sur les comparaisons flexibles. . Pour résumer, nous avons
Une autre option pour éviter les parenthèses consiste à utiliser
DataFrame.query
(oueval
):J'ai largement documenté
query
eteval
en évaluation d'expression dynamique chez les pandas en utilisant pd.eval () .operator.and_
Vous permet d'effectuer cette opération de manière fonctionnelle. Appelle en interne
Series.__and__
qui correspond à l'opérateur au niveau du bit.Vous n'en aurez généralement pas besoin, mais il est utile de le savoir.
Généralisation:
np.logical_and
(etlogical_and.reduce
)Une autre alternative consiste à utiliser
np.logical_and
, qui ne nécessite pas non plus de regroupement de parenthèses:np.logical_and
est un ufunc (Universal Functions) , et la plupart des ufuncs ont unereduce
méthode. Cela signifie qu'il est plus facile de généraliser aveclogical_and
si vous avez plusieurs masques à AND. Par exemple, pour les masques ANDm1
etm2
etm3
avec&
, vous devrez faireCependant, une option plus simple est
C'est puissant, car cela vous permet de construire sur cela avec une logique plus complexe (par exemple, générer dynamiquement des masques dans une compréhension de liste et les ajouter tous):
1 - Je sais que je persiste sur ce point, mais soyez indulgents avec moi. C'est une erreur très , très courante du débutant, et doit être expliquée très en détail.
OU logique
Pour ce qui
df
précède, disons que vous souhaitez renvoyer toutes les lignes où A == 3 ou B == 7.Surchargé au niveau du bit
|
Si vous ne l'avez pas encore fait, veuillez également lire la section sur la logique ET ci - dessus, toutes les mises en garde s'appliquent ici.
Alternativement, cette opération peut être spécifiée avec
operator.or_
Appels
Series.__or__
sous le capot.np.logical_or
Pour deux conditions, utilisez
logical_or
:Pour plusieurs masques, utilisez
logical_or.reduce
:NON logique
Étant donné un masque, tel que
Si vous devez inverser chaque valeur booléenne (pour que le résultat final soit
[False, False, True]
), vous pouvez utiliser l'une des méthodes ci-dessous.Bitwise
~
Encore une fois, les expressions doivent être mises entre parenthèses.
Cela appelle en interne
Mais ne l'utilisez pas directement.
operator.inv
Fait appel
__invert__
à la série en interne .np.logical_not
C'est la variante numpy.
Remarque,
np.logical_and
peut être remplacé parnp.bitwise_and
,logical_or
parbitwise_or
etlogical_not
parinvert
.la source
|
, ce qui équivaut ànumpy.bitwise_or
, au lieu denumpy.logical_or
. Puis-je demander pourquoi? N'est-il pasnumpy.logical_or
conçu spécifiquement pour cette tâche? Pourquoi ajouter le fardeau de le faire au niveau du bit pour chaque paire d'éléments?|
pour une opération booléenne élément par élément. Mais pour moi, cette documentation est plus un "tutoriel", et en revanche, je pense que ces références API sont plus proches de la source de vérité: numpy.bitwise_or et numpy.logical_or - donc j'essaie de comprendre ce qui est décrit ici.Il est important de comprendre que vous ne pouvez utiliser aucun des opérateurs logiques Python (
and
,or
ounot
) surpandas.Series
oupandas.DataFrame
s (de même, vous ne pouvez pas les utiliser surnumpy.array
s avec plus d'un élément). La raison pour laquelle vous ne pouvez pas les utiliser est parce qu'ils appellent implicitementbool
leurs opérandes qui lèvent une exception car ces structures de données ont décidé que le booléen d'un tableau est ambigu:J'ai couvert cela plus en détail dans ma réponse à la "La valeur de vérité d'une série est ambiguë. Utilisez a.empty, a.bool (), a.item (), a.any () ou a.all ()" Q + A .
Fonctions logiques NumPys
Cependant NumPy fournit des équivalents de fonctionnement par élément à ces opérateurs comme des fonctions qui peuvent être utilisées sur
numpy.array
,pandas.Series
,pandas.DataFrame
, ou de tout autre (conforme)numpy.array
Sous - classe:and
anp.logical_and
or
anp.logical_or
not
anp.logical_not
numpy.logical_xor
qui n'a pas d' équivalent Python , mais est une logique « ou exclusif » opérationDonc, essentiellement, on devrait utiliser (en supposant que
df1
etdf2
sont des pandas DataFrames):Fonctions au niveau du bit et opérateurs au niveau du bit pour les booléens
Cependant, dans le cas où vous avez un tableau NumPy booléen, une série pandas ou des DataFrames pandas, vous pouvez également utiliser les fonctions binaires élément par élément (pour les booléens, elles sont - ou du moins devraient être - indiscernables des fonctions logiques):
np.bitwise_and
ou l'&
opérateurnp.bitwise_or
ou l'|
opérateurnp.invert
(ou l'aliasnp.bitwise_not
) ou l'~
opérateurnp.bitwise_xor
ou l'^
opérateurEn général, les opérateurs sont utilisés. Cependant, lorsqu'ils sont combinés avec des opérateurs de comparaison, il ne faut pas oublier de mettre la comparaison entre parenthèses car les opérateurs au niveau du bit ont une priorité plus élevée que les opérateurs de comparaison :
Cela peut être irritant car les opérateurs logiques Python ont une priorité plus faible que les opérateurs de comparaison, donc vous écrivez normalement
a < 10 and b > 10
(oùa
etb
sont par exemple des entiers simples) et n'avez pas besoin de la parenthèse.Différences entre les opérations logiques et binaires (sur les non-booléens)
Il est vraiment important de souligner que les opérations binaires et logiques ne sont équivalentes que pour les tableaux booléens NumPy (et les séries booléennes et DataFrames). Si ceux-ci ne contiennent pas de booléens, les opérations donneront des résultats différents. J'inclurai des exemples utilisant des tableaux NumPy mais les résultats seront similaires pour les structures de données pandas:
Et comme NumPy (et de même les pandas) fait des choses différentes pour les indices booléens ( tableaux d'index booléens ou «masques» ) et entiers ( tableaux d'index ), les résultats de l'indexation seront également différents:
Sommaire
Où l'opérateur logique ne fonctionne pas pour les tableaux NumPy , pandas Series et pandas DataFrames. Les autres fonctionnent sur ces structures de données (et sur des objets Python simples) et fonctionnent élément par élément. Cependant, soyez prudent avec l'inversion au niveau du bit sur les Python simples
bool
car le booléen sera interprété comme des entiers dans ce contexte (par exemple, les~False
retours-1
et les~True
retours-2
).la source