Evaluando los valores de las series de pandas con expresiones lógicas y sentencias if

Tengo problemas para evaluar los valores de un diccionario con las instrucciones if.

Dado el siguiente diccionario, que importé desde un dataframe (en caso de que importe):

>>> 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 

Intenté evaluar la siguiente statement para establecer el valor del último valor de Active :

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

Sin embargo, me encontré con el siguiente mensaje de error:

 Traceback (most recent call last): File "", line 1, in  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(). 

Esto me sorprendió, dado que podía mostrar el valor que quería usando el comando anterior sin la instrucción if:

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

Dado que el valor es claramente cero y el índice es 2, probé lo siguiente para realizar una breve comprobación de seguridad y descubrí que las cosas no estaban sucediendo como esperaba:

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

Mi pregunta es:

1) ¿Qué podría estar pasando aquí? Sospecho que estoy entendiendo mal los diccionarios en algún nivel fundamental.

2) Noté que cuando menciono cualquier valor de este diccionario, el número de la izquierda aumenta en 1. ¿Qué representa esto? Por ejemplo:

 >>> 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 

Gracias de antemano por cualquier ayuda.

Lo que produce es un objeto de la Serie Pandas y esto no puede evaluarse de la manera que está intentando, aunque es solo un valor único al que necesita cambiar su línea para:

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

Con respecto a su segunda pregunta vea mi comentario.

EDITAR

Desde los comentarios y el enlace a su salida, al llamar a any() corrigió el mensaje de error, pero sus datos son en realidad cadenas, por lo que la comparación aún falla, puede hacer lo siguiente:

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

Para hacer una comparación de cadenas, o corregir los datos sin embargo, se leyeron o generaron.

O hacer:

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

Para convertir el dtype de la columna para que su comparación sea más correcta.

Una serie es una subclase de NDFrame. El método NDFrame.__bool__ siempre genera un ValueError . Por lo tanto, tratar de evaluar una Serie en un contexto booleano genera un ValueError, incluso si la Serie tiene un solo valor.

La razón por la que los NDFrames no tienen un valor booleano (err, es decir, siempre genera un ValueError), es porque hay más de un posible criterio que uno podría esperar razonablemente para que un NDFrame sea Verdadero. Podría significar

  1. cada elemento en el NDFrame es True, o (si es así, use .all() )
  2. cualquier elemento en el NDFrame es True, o (si es así, use Series.any() )
  3. el NDFrame no está vacío (si es así, use .empty() )

Dado que cualquiera de los dos es posible, y dado que los diferentes usuarios tienen diferentes expectativas, en lugar de solo elegir una, los desarrolladores se niegan a adivinar y en su lugar requieren que el usuario del NDFrame haga explícito qué criterio desean utilizar.

El mensaje de error enumera las opciones más probables:

Utilice a.empty, a.bool (), a.item (), a.any () o a.all ()

Ya que en su caso usted sabe que la Serie contendrá solo un valor, podría usar el item :

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

Con respecto a su segunda pregunta: los números de la izquierda parecen ser la numeración de líneas producida por su intérprete de Python (PyShell?), Pero eso es solo mi suposición.


ADVERTENCIA: Presumiblemente,

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

significa que le gustaría que la condición fuera Verdadera cuando el valor único en la Serie es igual a 1. El código

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

será verdadero si el tipo de la Serie es numérico y el valor de la Serie es cualquier número distinto de 0. Por ejemplo, si tomamos pnl[company].tail(1)['Active'] para que sea igual a

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

entonces

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

y por lo tanto,

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

Creo que en el s.item() == 1 más fielmente conserva su significado previsto:

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

(s == 1).any() también funcionaría, pero usar any no expresa su intención muy claramente, ya que sabe que la Serie contendrá solo un valor.

Su pregunta no tiene nada que ver con los diccionarios Python, o Python nativos en absoluto. Se trata de pandas Series, y las otras respuestas te dieron la syntax correcta:

Al interpretar sus preguntas en el sentido más amplio, se trata de cómo se pandas Series en NumPy , y NumPy históricamente hasta hace poco tenía un apoyo notoriamente deficiente para los valores lógicos y los operadores . pandas hace el mejor trabajo que puede con lo que proporciona NumPy. En ocasiones, tener que invocar manualmente funciones numéricas numpy en lugar de solo escribir código con operadores arbitrarios (Python) es molesto e incómodo y, en ocasiones, infla el código de pandas. Además, a menudo se debe esto para el rendimiento (mejor que el thunk de y desde Python nativo). Pero ese es el precio que pagamos.

Existen muchas limitaciones, peculiaridades y errores (ejemplos a continuación); el mejor consejo es desconfiar de los booleanos como ciudadanos de primera clase en pandas debido a las limitaciones de numpy:

  • Advertencias y advertencias de los pandas: uso de declaraciones If / Truth con pandas

  • un ejemplo de rendimiento: Python ~ se puede usar en lugar de np.invert () – más legible pero 3x más lento o peor

  • algunos errores y limitaciones: en el código a continuación, tenga en cuenta que el número reciente ahora permite valores booleanos (representados internamente como int) y permite NA, pero que, por ejemplo, value_counts() ignora NA (compare con la tabla de R, que tiene la opción ‘useNA’ ).

.

 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 #  # 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