web-dev-qa-db-fra.com

La valeur de vérité d'une série est ambiguë. Utilisez a.empty, a.bool (), a.item (), a.any () ou a.all ()

Problème de filtrage de mon image de résultat avec une condition or. Je souhaite que mon résultat df extrait toutes les valeurs de colonne var supérieures à 0,25 et inférieures à -0,25.

Cette logique ci-dessous me donne une valeur de vérité ambiguë, mais cela fonctionne lorsque je divise ce filtrage en deux opérations distinctes. Que se passe-t-il ici? Je ne sais pas où utiliser la a.empty(), a.bool(), a.item(),a.any() or a.all() suggérée.

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
239
obabs

Les instructions or et and python requièrent les valeurs truth-. Pour pandas celles-ci sont considérées comme ambiguës, vous devez donc utiliser les opérations "au niveau du bit" _|_ (ou) ou _&_ (et):

_result = result[(result['var']>0.25) | (result['var']<-0.25)]
_

Celles-ci sont surchargées pour que ce type d’infrastructure de données produise le résultat élémentaire or (ou and).


Juste pour ajouter quelques explications à cette déclaration:

L'exception est levée lorsque vous voulez obtenir le bool d'un _pandas.Series_:

_>>> import pandas as pd
>>> x = pd.Series([1])
>>> bool(x)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
_

Ce que vous avez frappé est un endroit où l'opérateur a implicitement converti les opérandes en bool (vous avez utilisé or mais cela se produit également pour and, if et while):

_>>> x or x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> x and x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> if x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> while x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
_

Outre ces 4 instructions, il existe plusieurs fonctions python qui masquent certains appels bool (comme any, all, filter, ...): normalement pas problématique avec _pandas.Series_ mais pour être complet, je voulais les mentionner.


Dans votre cas, l'exception n'est pas vraiment utile, car elle ne mentionne pas les bonnes alternatives . Pour and et or, vous pouvez utiliser (si vous voulez des comparaisons élément par élément):

  • numpy.logical_or :

    _>>> import numpy as np
    >>> np.logical_or(x, y)
    _

    ou simplement l'opérateur _|_:

    _>>> x | y
    _
  • numpy.logical_and :

    _>>> np.logical_and(x, y)
    _

    ou simplement l'opérateur _&_:

    _>>> x & y
    _

Si vous utilisez les opérateurs, assurez-vous de définir correctement vos parenthèses à cause de priorité des opérateurs .

Il y a plusieurs fonctions logiques numériques qui devraient fonctionner sur _pandas.Series_.


Les alternatives mentionnées dans l’exception conviennent mieux si vous l’avez rencontrée en faisant if ou while. J'expliquerai brièvement chacune de ces choses:

  • Si vous voulez vérifier si votre série est vide :

    _>>> x = pd.Series([])
    >>> x.empty
    True
    >>> x = pd.Series([1])
    >>> x.empty
    False
    _

    Python interprète normalement la length des conteneurs (comme list, Tuple, ...) comme une valeur de vérité si elle n'a pas d'interprétation booléenne explicite. Donc, si vous voulez une vérification semblable à celle de python, vous pouvez faire: _if x.size_ ou _if not x.empty_ au lieu de _if x_.

  • Si votre Series contient un et un seul valeur booléenne:

    _>>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
    _
  • Si vous souhaitez vérifier le premier et unique élément de votre série (comme .bool() mais fonctionnant même pour des contenus non booléens):

    _>>> x = pd.Series([100])
    >>> x.item()
    100
    _
  • Si vous voulez vérifier si tout ou un élément quelconque n'est pas nul , non vide ou non faux

    _>>> x = pd.Series([0, 1, 2])
    >>> x.all()   # because one element is zero
    False
    >>> x.any()   # because one (or more) elements are non-zero
    True
    _
399
MSeifert

Pour la logique booléenne, utilisez & et |.

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,3), columns=list('ABC'))

>>> df
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
2  0.950088 -0.151357 -0.103219
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

>>> df.loc[(df.C > 0.25) | (df.C < -0.25)]
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

Pour voir ce qui se passe, vous obtenez une colonne de booléens pour chaque comparaison, par exemple.

df.C > 0.25
0     True
1    False
2    False
3     True
4     True
Name: C, dtype: bool

Lorsque vous avez plusieurs critères, vous obtiendrez plusieurs colonnes. C'est pourquoi la logique de jointure est ambiguë. Utiliser and ou or traite chaque colonne séparément, vous devez donc d'abord réduire cette colonne à une seule valeur booléenne. Par exemple, pour voir si une valeur ou toutes les valeurs de chacune des colonnes sont vraies.

# Any value in either column is True?
(df.C > 0.25).any() or (df.C < -0.25).any()
True

# All values in either column is True?
(df.C > 0.25).all() or (df.C < -0.25).all()
False

Une méthode compliquée pour obtenir la même chose consiste à compresser toutes ces colonnes et à exécuter la logique appropriée.

>>> df[[any([a, b]) for a, b in Zip(df.C > 0.25, df.C < -0.25)]]
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

Pour plus de détails, voir Indexation Booléenne dans la documentation.

31
Alexander

Vous pouvez également utiliser le module Opérateur. Des informations plus détaillées sont ici Python docs

import operator
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,3), columns=list('ABC'))
df.loc[operator.or_(df.C > 0.25, df.C < -0.25)]

          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.4438
8

Cette excellente réponse explique très bien ce qui se passe et fournit une solution. J'aimerais ajouter une autre solution qui pourrait convenir dans des cas similaires: en utilisant la méthode query :

result = result.query("(var > 0.25) or (var < -0.25)")

Voir aussi http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query .

(Certains tests avec une base de données avec laquelle je travaille actuellement suggèrent que cette méthode est un peu plus lente que d'utiliser les opérateurs au niveau des bits sur une série de booléens: 2 ms contre 870 µs)

Un avertissement : Au moins une situation où ce n'est pas simple, c'est quand les noms de colonnes arrivent à être des expressions python. J'avais des colonnes nommées WT_38hph_IP_2, WT_38hph_input_2 et log2(WT_38hph_IP_2/WT_38hph_input_2) et je voulais exécuter la requête suivante: "(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

J'ai obtenu l'exception en cascade suivante:

  • KeyError: 'log2'
  • UndefinedVariableError: name 'log2' is not defined
  • ValueError: "log2" is not a supported function

Je suppose que cela s’est produit parce que l’analyseur de requête essayait de créer quelque chose à partir des deux premières colonnes au lieu d’identifier l’expression avec le nom de la troisième colonne.

Une solution de contournement possible est proposée ici .

1
bli