El valor de verdad de una serie es ambiguo. Utilice a.empty, a.bool (), a.item (), a.any () o a.all ()

369

Tener problemas para filtrar mi marco de datos de resultados con una orcondición. Quiero que mi resultado dfextraiga todos los varvalores de columna 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 ambiguo, sin embargo, funciona cuando divido este filtrado en dos operaciones separadas. ¿Que está sucediendo aquí? No estoy seguro de dónde usar el sugerido a.empty(), a.bool(), a.item(),a.any() or a.all().

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
obabs
fuente
47
utilizar en |lugar deor
MaxU
1
Aquí hay una solución alternativa:abs(result['var'])>0.25
ColinMac

Respuestas:

566

Las declaraciones ory andpython requieren truthvalores. Para pandasestos se consideran ambiguos, por lo que debe utilizar operaciones "bit a bit" |(o) o &(y):

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 inteligente or(o and).


Solo para agregar más explicaciones a esta declaración:

La excepción se produce cuando desea obtener el boolde 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 golpeó fue un lugar donde el operador convirtió implícitamente los operandos a bool(usted usó orpero también sucede para and, ify 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 estos 4 estados hay varias funciones de Python que se esconden algunas boolllamadas (como any, all, filter, ...), estos normalmente no son problemáticos con pandas.Series, pero para lo completo que quería mencionar estos.


En su caso, la excepción no es realmente útil, porque no menciona las alternativas correctas . Para andy orpuede 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 configurar sus paréntesis correctamente debido a la precedencia del operador .

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


Las alternativas mencionadas en la Excepción son más adecuadas si la encontró al hacer ifo while. En breve explicaré cada uno de estos:

  • Si desea verificar si su Serie está vacía :

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

    Python normalmente interpreta la length de los contenedores (como list, tuple...) como valor de verdad si no tiene una interpretación booleana explícita. Entonces, si desea la comprobación similar a Python, puede hacer: if x.sizeo en if not x.emptylugar de if x.

  • Si Seriescontiene uno y solo un valor booleano:

    >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
  • Si desea verificar 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 verificar si todo o algún artículo es cero, no está vacío o no es 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
MSeifert
fuente
¿Por qué estos operadores de python no están sobrecargados para manejar series de pandas?
Mudit Jain
@MuditJain No hay manera de sobrecarga directamente and, ory noten Python. Estos operadores usan directamente lo que booldevuelve los operandos. Y en cierto modo, Pandas / NumPy lo sobrecargó para aumentar el ValueErrorporque consideran que el valor de verdad de dicha estructura de datos es ambiguo.
MSeifert
la solución está bien, pero la explicación está lejos de ser buena
blacksheep
2
@blacksheep ¿Tiene alguna sugerencia de lo que podría haber explicado mejor?
MSeifert
Es una gran explicación. De hecho, me ayudó a comprender bit a bit versus lógico de una manera que los ejemplos más abstractos no han podido hacer.
rocksNwaves
41

Para 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, obtienes una columna de booleanos para cada comparación, por ej.

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

Cuando tenga varios criterios, obtendrá varias columnas devueltas. Es por eso que la lógica de unión es ambigua. Usando ando ortrata cada columna por separado, por lo que primero debe reducir esa columna a un solo valor booleano. Por ejemplo, para ver si algún 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 complicada de lograr lo mismo es comprimir todas estas columnas y realizar la lógica adecuada.

>>> 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 obtener más detalles, consulte Indización booleana en los documentos.

Alejandro
fuente
20

Los pandas usan bitwise '&' '|' y cada condición debe estar envuelta en un '()'

Por ejemplo, siguientes trabajos

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

Pero la misma consulta sin los corchetes adecuados no

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

O, alternativamente, puede usar el módulo Operador. Información más detallada está aquí Documentos de 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
fuente
1

Esta excelente respuesta explica muy bien lo que está sucediendo y brinda una solución. Me gustaría agregar otra solución que podría ser adecuada en casos similares: usando el querymétodo:

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

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

(Algunas pruebas con un marco de datos con el que estoy trabajando actualmente sugieren que este método es un poco más lento que el uso de operadores bit a bit en series de booleanos: 2 ms frente a 870 µs)

Una advertencia : al menos una situación en la que esto no es sencillo es cuando los nombres de columna son expresiones de python. Tenía columnas nombrados WT_38hph_IP_2, WT_38hph_input_2y 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 de las dos primeras columnas en lugar de identificar la expresión con el nombre de la tercera columna.

Aquí se propone una posible solución alternativa .

bli
fuente
1

Encontré el mismo error y me detuve con un marco de datos pyspark durante unos días, pude resolverlo con éxito al completar los valores de na con 0 ya que estaba comparando valores enteros de 2 campos.

iretex
fuente