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

370

Problème de filtrage de ma trame de données de résultat avec une orcondition Je veux que mon résultat dfextrait toutes les varvaleurs de colonne supérieures à 0,25 et inférieures à -0,25.

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

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
obabs
la source
47
utiliser |au lieu deor
MaxU
1
Voici une solution:abs(result['var'])>0.25
ColinMac

Réponses:

567

Les instructions oret andpython nécessitent truth-values. Pour pandasceux-ci sont considérés comme ambigus, vous devez donc utiliser les opérations "au niveau du bit" |(ou) ou &(et):

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

Ceux-ci sont surchargés pour ce type de infrastructures de données pour produire l'élément or(ou and).


Juste pour ajouter quelques explications à cette déclaration:

L'exception est levée lorsque vous voulez obtenir l' boolun 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 touché était un endroit où l'opérateur a implicitement converti les opérandes bool(vous l'avez utilisé ormais cela arrive aussi pour and, ifet 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().

En plus de ces 4 déclarations il y a plusieurs fonctions de python qui cachent des boolappels (comme any, all, filter, ...) ceux - ci ne sont généralement pas problématique , pandas.Seriesmais pour être complet que je voulais mentionner ces derniers .


Dans votre cas, l'exception n'est pas vraiment utile, car elle ne mentionne pas les bonnes alternatives . Pour andet orvous pouvez utiliser (si vous voulez des comparaisons 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 en raison de la priorité de l'opérateur .

Il existe plusieurs fonctions logiques numpy qui devraient fonctionner pandas.Series.


Les alternatives mentionnées dans l'exception sont plus adaptées si vous l'avez rencontré lors de l'exécution de ifou while. Je vais expliquer brièvement chacun de ces éléments:

  • Si vous souhaitez 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 le lenGTH des conteneurs (comme list, tuple, ...) comme valeur de vérité si elle n'a pas d' interprétation booléenne explicite. Donc, si vous voulez la vérification de type python, vous pouvez faire: if x.sizeou if not x.emptyau lieu de if x.

  • Si votre Seriescontient une et une seule valeur booléenne:

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

    >>> x = pd.Series([100])
    >>> x.item()
    100
  • Si vous souhaitez vérifier si tout ou partie d' un élément 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
MSeifert
la source
Pourquoi ces opérateurs python ne sont-ils pas surchargés pour gérer les séries de pandas?
Mudit Jain
@MuditJain Il n'y a aucun moyen de surcharge directement and, oret noten Python. Ces opérateurs utilisent directement ce qui se trouve boolsur les retours d'opérandes. Et d'une certaine manière, Pandas / NumPy les a déjà surchargés, ValueErrorcar ils considèrent la valeur de vérité d'une telle structure de données comme ambiguë.
MSeifert
la solution est ok, mais l'explication est loin d'être bonne
blacksheep
2
@blacksheep Avez-vous des suggestions sur ce que j'aurais pu mieux expliquer?
MSeifert
C'est une excellente explication. En fait, cela m'a aidé à comprendre le bit par rapport à la logique d'une manière que les exemples plus abstraits n'ont pas réussi à faire.
rocksNwaves
41

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 retournées. C'est pourquoi la logique de jointure est ambiguë. L'utilisation andou le ortraitement de 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 façon compliquée de réaliser 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, reportez-vous à l'indexation booléenne dans la documentation.

Alexandre
la source
20

Et bien les pandas utilisent bit & '' | ' et chaque condition doit être entourée d'un '()'

Par exemple les travaux suivants

data_query = data[(data['year'] >= 2005) & (data['year'] <= 2010)]

Mais la même requête sans crochets appropriés ne

data_query = data[(data['year'] >= 2005 & data['year'] <= 2010)]
Nipun
la source
8

Ou, vous pouvez également utiliser le module Opérateur. Des informations plus détaillées sont disponibles ici. Documents Python

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
Cảnh Toàn Nguyễn
la source
1

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

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

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

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

Un avertissement : au moins une situation où ce n'est pas simple est lorsque les noms de colonnes sont des expressions python. J'avais des colonnes nommées WT_38hph_IP_2, WT_38hph_input_2et log2(WT_38hph_IP_2/WT_38hph_input_2)et je voulais effectuer la requête suivante:"(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

J'ai obtenu la cascade d'exceptions 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 .

bli
la source
1

J'ai rencontré la même erreur et j'ai été bloqué avec une trame de données pyspark pendant quelques jours, j'ai pu le résoudre avec succès en remplissant na valeurs avec 0 car je comparais les valeurs entières de 2 champs.

iretex
la source