El valor de verdad de una Serie es ambiguo. Usar un.empty, un.bool(), un.item(), un.any() o un.all()


Tiene problemas para filtrar mi dataframe de resultados con una condición or. Quiero que mi resultado df extraiga todos los valores de la columna _var_ que están por encima de 0.25 y por debajo de -0.25. Esta lógica a continuación me da un valor de verdad ambigua sin embargo funciona cuando divido este filtrado en dos operaciones separadas. ¿Qué está pasando aquí? no estoy seguro de dónde usar el a.empty(), a.bool(), a.item(),a.any() or a.all() sugerido.

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
Author: Uribe-Convers, 2016-04-28

4 answers

Las sentencias or y and python requieren truth-valores. Para pandas estos se consideran ambiguos, por lo que debe usar "bitwise" | (or) o & (and) operaciones:

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

Estos están sobrecargados para este tipo de estructuras de datos para producir el elemento-sabio or (o and).


Solo para añadir algo más de explicación a esta declaración:{[49]]}

La excepción se lanza cuando se quiere obtener el bool de 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().

Lo que golpeaste fue un lugar donde el operador implícitamente convirtió los operandos a bool (usaste or pero también sucede para and, if y 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().

Además de estas 4 sentencias hay varias funciones de python que ocultan algunas llamadas bool (como any, all, filter, ...) estos normalmente no son problemáticos con pandas.Series pero para completar quería mencionar estos.


En su caso, la excepción no es realmente útil, porque no menciona la alternativas correctas. Para and y or puede usar (si desea comparaciones basadas en elementos):

  • numpy.logical_or:

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

    O simplemente el operador |:

    >>> x | y
    
  • numpy.logical_and:

    >>> np.logical_and(x, y)
    

    O simplemente el operador &:

    >>> x & y
    

Si está utilizando los operadores, asegúrese de establecer su paréntesis correctamente debido a la precedencia del operador.

Hay varias funciones numpy lógicas que deberían trabajar en pandas.Series.


Las alternativas mencionadas en la Excepción son más adecuadas si las encontró al hacer if o while. Voy a explicar en breve cada uno de estos:

  • Si quieres comprobar si tu Serie está vacía :

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

    Python normalmente interpreta el length de contenedores (como list, tuple, ...) como valor de verdad si no tiene booleano explícito interpretación. Así que si quieres la comprobación similar a python, puedes hacer: if x.size o if not x.empty en lugar de if x.

  • Si su Series contiene uno y solo uno valor booleano:

    >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
    
  • Si desea comprobar el primer y único elemento de su Serie (como .bool() pero funciona incluso para contenidos no booleanos):

    >>> x = pd.Series([100])
    >>> x.item()
    100
    
  • Si desea comprobar si los o cualquier elemento no es cero, no-vacío o no Falso:

    >>> 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
    
 211
Author: MSeifert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-01 18:50:10

Para la lógica booleana, use & y |.

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

Para ver lo que está sucediendo, se obtiene una columna de booleanos para cada comparación, por ejemplo,

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

Cuando tiene múltiples criterios, obtendrá varias columnas devueltas. Esta es la razón por la lógica de la unión es ambigua. Usando and o or trata cada columna por separado, por lo que primero debe reducir esa columna a un solo valor booleano. Por ejemplo, para ver si cualquier valor o todos los valores en cada una de las columnas es Verdadero.

# 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

Una forma enrevesada de lograr lo mismo es comprimir todas estas columnas juntas y realizar la lógica apropiada.

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

Para más detalles, consulte Indexación booleana en los documentos.

 24
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 18:23:59

O, alternativamente, puede usar el módulo de operador. Más información detallada está aquí 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
 6
Author: Cảnh Toàn Nguyễn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 07:48:25

Esta excelente respuesta explica muy bien lo que está sucediendo y proporciona una solución. Me gustaría añadir otra solución que podría ser adecuada en casos similares: el uso de la query método:

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

Véase también http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query .

(Algunas pruebas con un dataframe con el que estoy trabajando actualmente sugieren que este método es un poco más lento que usar los operadores bitwise en series de booleanos: 2 ms vs 870 µs)

Una advertencia: Al menos una situación en la que esto no es sencillo es cuando los nombres de las columnas son expresiones de python. Tenía columnas nombradas WT_38hph_IP_2, WT_38hph_input_2 y log2(WT_38hph_IP_2/WT_38hph_input_2) y quería realizar la siguiente consulta: "(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

Obtuve la siguiente cascada de excepciones:

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

Supongo que esto sucedió porque el analizador de consultas estaba tratando de hacer algo a partir de los dos primeros columnas en lugar de identificar la expresión con el nombre de la tercera columna.

Se propone una posible solución aquí.

 1
Author: bli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-02 12:20:55