web-dev-qa-db-fra.com

Évaluer les valeurs de la série de pandas avec des expressions logiques et des déclarations if

Je ne parviens pas à évaluer les valeurs d'un dictionnaire à l'aide d'instructions if.

Étant donné le dictionnaire suivant, que j’ai importé à partir d’une base de données (au cas où cela compte):

>>> pnl[company]
29:   Active Credit       Date   Debit Strike Type
0      1      0 2013-01-08  2.3265  21.15  Put
1      0      0 2012-11-26      40     80  Put
2      0      0 2012-11-26     400     80  Put

J'ai essayé d'évaluer la déclaration suivante pour établir la valeur de la dernière valeur de Active:

if pnl[company].tail(1)['Active']==1:
    print 'yay'

Cependant, j'ai été confronté au message d'erreur suivant:

Traceback (most recent call last):
  File "<pyshell#69>", line 1, in <module>
    if pnl[company].tail(1)['Active']==1:
  File "/usr/lib/python2.7/dist-packages/pandas/core/generic.py", line 676, in __nonzero__
    .format(self.__class__.__name__))
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Cela m'a surpris, étant donné que je pouvais afficher la valeur souhaitée à l'aide de la commande ci-dessus sans l'instruction if:

>>> pnl[company].tail(1)['Active']
30: 2    0
Name: Active, dtype: object

Étant donné que la valeur est clairement égale à zéro et que l'indice est égal à 2, j'ai essayé ce qui suit pour un bref contrôle de cohérence et constaté que les choses ne se passaient pas comme prévu.

>>> if pnl[company]['Active'][2]==0:
...     print 'woo-hoo'
... else:
...     print 'doh'


doh

Ma question est:  

1) Que peut-il se passer ici? Je soupçonne que je comprends mal les dictionnaires à un niveau fondamental.

2) J'ai remarqué que lorsque j'évoque une valeur donnée de ce dictionnaire, le nombre à gauche augmente de 1. Qu'est-ce que cela représente? Par exemple:

>>> pnl[company].tail(1)['Active']
31: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
32: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
33: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
34: 2    0
Name: Active, dtype: object

Merci d'avance pour votre aide.

13
neanderslob

Ce que vous cédez est un objet de la série Pandas et il ne peut pas être évalué comme vous le tentez même s'il ne s'agit que d'une valeur unique pour laquelle vous devez modifier votre ligne:

if pnl[company].tail(1)['Active'].any()==1:
  print 'yay'

En ce qui concerne votre deuxième question, voyez mon commentaire.

MODIFIER

À partir des commentaires et du lien vers votre sortie, l'appel de any() a corrigé le message d'erreur, mais vos données sont en fait des chaînes. La comparaison a donc échoué. Vous pouvez soit:

if pnl[company].tail(1)['Active'].any()=='1':
  print 'yay'

Effectuer une comparaison de chaîne ou corriger les données quelle que soit leur lecture ou leur génération.

Ou faire:

pnl['Company']['Active'] = pnl['Company']['Active'].astype(int)

Pour convertir la dtype de la colonne afin que votre comparaison soit plus correcte.

6
EdChum

Une série est une sous-classe de NDFrame. La méthode NDFrame.__bool__déclenche toujours une ValueError . Ainsi, essayer d'évaluer une série dans un contexte booléen génère une valeur ValueError, même si la série n'a qu'une seule valeur.

La raison pour laquelle les cadres NDF n'ont pas de valeur booléenne (err, c'est-à-dire, toujours déclencher une valeur ValueError), est parce qu'il existe plus d'un critère possible auquel on peut raisonnablement s'attendre pour qu'un cadre NDF soit vrai. Cela pourrait signifier

  1. tous les éléments du cadre NDFr sont vrais ou (si oui, utilisez .all())
  2. tout élément du cadre NDFround est défini sur True ou (le cas échéant, utilisez Series.any())
  3. le cadre NDF n'est pas vide (si c'est le cas, utilisez .empty())

Étant donné que l’un ou l’autre est possible et que différents utilisateurs ont des attentes différentes, au lieu de choisir un choix, les développeurs refusent de deviner et demandent à l’utilisateur du NDFrame de préciser le critère qu’ils souhaitent utiliser.

Le message d'erreur répertorie les choix les plus probables:

Utilisez a.empty, a.bool (), a.item (), a.any () ou a.all ()

Puisque dans votre cas, vous savez que la série ne contiendra qu'une seule valeur, vous pouvez utiliser item:

if pnl[company].tail(1)['Active'].item() == 1:
    print 'yay'

En ce qui concerne votre deuxième question: les nombres sur la gauche semblent être des numéros de ligne produits par votre interprète Python (PyShell?) - mais ce n’est que ma supposition. 


ATTENTION: Vraisemblablement,

if pnl[company].tail(1)['Active']==1:

signifie que vous souhaitez que la condition soit vraie lorsque la valeur unique de la série est égale à 1. Le code

if pnl[company].tail(1)['Active'].any()==1:
    print 'yay'

sera True si le type de la série est numérique et que la valeur de la série est tout nombre autre que 0. Par exemple, si nous prenons pnl[company].tail(1)['Active'] égal à 

In [128]: s = pd.Series([2], index=[2])

puis

In [129]: s.any()
Out[129]: True

et donc,

In [130]: s.any()==1
Out[130]: True

Je pense que s.item() == 1 préserve plus fidèlement le sens voulu:

In [132]: s.item()==1
Out[132]: False

(s == 1).any() fonctionnerait aussi, mais utiliser any n'exprime pas votre intention très clairement, car vous savez que la série ne contiendra qu'une seule valeur.

3
unutbu

Votre question n'a rien à voir avec les dictionnaires Python, ni avec le Python natif . Il s'agit de pandas Series, et les autres réponses vous ont donné la syntaxe correcte:

Pour interpréter vos questions dans un sens plus large, il s’agit de savoir comment pandas Series a été lancé sur NumPy, et Historiquement, jusqu'à récemment, NumPy supportait notoirement peu les valeurs logiques et les opérateurs . pandas fait le meilleur travail possible avec ce que NumPy fournit. Avoir à invoquer parfois manuellement des fonctions logiques numpy au lieu d'écrire du code avec des opérateurs arbitraires (Python) est ennuyeux et maladroit, et enfle parfois le code des pandas. En outre, vous devez souvent faire cela pour la performance (numpy mieux que de penser à Python natif). Mais c'est le prix que nous payons.

Il existe de nombreuses limitations, bizarreries et pièges (exemples ci-dessous) - le meilleur conseil est de se méfier des booléens en tant que citoyen de première classe dans les pandas en raison des limitations de numpy:

.

import numpy as np
import pandas as pd
s = pd.Series([True, True, False, True, np.NaN])
s2  = pd.Series([True, True, False, True, np.NaN])
dir(s) # look at .all, .any, .bool, .eq, .equals, .invert, .isnull, .value_counts() ...

s.astype(bool) # WRONG: should use the member s.bool ; no parentheses, it's a member, not a function
# 0     True
# 1     True
# 2    False
# 3     True
# 4     True  # <--- should be NA!!
#dtype: bool

s.bool
# <bound method Series.bool of
# 0     True
# 1     True
# 2    False
# 3     True
# 4      NaN
# dtype: object>

# Limitation: value_counts() currently excludes NAs
s.value_counts()
# True     3
# False    1
# dtype: int64
help(s.value_counts) # "... Excludes NA values(!)"

# Equality comparison - vector - fails on NAs, again there's no NA-handling option):
s == s2 # or equivalently, s.eq(s2)
# 0     True
# 1     True
# 2     True
# 3     True
# 4    False  # BUG/LIMITATION: we should be able to choose NA==NA
# dtype: bool

# ...but the scalar equality comparison says they are equal!!
s.equals(s2)
# True
0
smci