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

578

Tengo 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)]
4
  • 69
    usar en |lugar deor
    MaxU
    28/04/2016 a las 17:54
  • 4
    Aquí tienes una solución alternativa: abs(result['var'])>0.25 28/12/18 a las 17:29
  • 3
    Relacionado: Operadores lógicos para indexación booleana en Pandas
    cs95
    8 de marzo de 2019 a las 22:09
  • Me encontré con el mismo mensaje de error usando la max()función estándar . Reemplazarlo con con numpy.maximum()para máximos de elementos entre dos valores resolvió mi problema. 3 de febrero a las 11:34
812

Las declaraciones ory andpython requieren truth-valores. Porque pandasestos se consideran ambiguos, por lo que debe usar operaciones "bit a bit" |(o) o &(y):

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

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


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

La excepción se lanza 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(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 por 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 los paréntesis correctamente debido a la precedencia de los operadores .

Hay varias funciones numéricas lógicas en las que deberían funcionar pandas.Series.


Las alternativas mencionadas en la Excepción son más adecuadas si las encontró al hacer ifo while. Explicaré brevemente 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 el lenGTH de contenedores (como list, tuple, ...) como valor de verdad si no tiene una interpretación explícita booleano. Entonces, si desea la verificación similar a la de Python, puede hacer: if x.sizeo en if not x.emptylugar de if x.

  • Si su 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 (me gusta, .bool()pero funciona incluso para contenidos no booleanos):

    >>> x = pd.Series([100])
    >>> x.item()
    100
    
  • Si desea verificar si todo o algún elemento no 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
    
0
56

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 valores booleanos para cada comparación, p. 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. Por eso la lógica de combinación es ambigua. El uso de 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 juntas 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 Indexación booleana en los documentos.

53

Bueno, los pandas usan bit a bit & |y cada condición debe estar envuelta en un()

Por ejemplo los 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)]
2
  • 1
    ¡Estás absolutamente en lo correcto! Gracias 18 dic.20 a las 14:22
  • Maravilloso, la única respuesta que menciona la importancia de envolver las condiciones entre paréntesis. El único problema con mi sintaxis. Pero, ¿por qué es esto obligatorio?
    u tyagi
    22 de septiembre a las 9:18
11

O, alternativamente, puede utilizar el módulo Operador. Más información 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
3

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

Consulte 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 usar los operadores bit a bit en series de valores 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 las columnas 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 a partir 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 .

1

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

1

Necesita usar operadores bit a bit en |lugar de ory en &lugar de andpandas, no puede simplemente usar las declaraciones bool de python.

Para un filtrado mucho más complejo, cree masky aplique la máscara en el marco de datos.
Pon toda tu consulta en la máscara y aplícala.
Suponer,

mask = (df["col1"]>=df["col2"]) & (stock["col1"]<=df["col2"])
df_new = df[mask]
1

Intentaré dar el punto de referencia de las tres formas más comunes (también mencionadas anteriormente):

from timeit import repeat

setup = """
import numpy as np;
import random;
x = np.linspace(0,100);
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) * (x <= ub)]', 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'

for _ in range(3):
    for stmt in stmts:
        t = min(repeat(stmt, setup, number=100_000))
        print('%.4f' % t, stmt)
    print()

resultado:

0.4808 x[(x > lb) * (x <= ub)]
0.4726 x[(x > lb) & (x <= ub)]
0.4904 x[np.logical_and(x > lb, x <= ub)]

0.4725 x[(x > lb) * (x <= ub)]
0.4806 x[(x > lb) & (x <= ub)]
0.5002 x[np.logical_and(x > lb, x <= ub)]

0.4781 x[(x > lb) * (x <= ub)]
0.4336 x[(x > lb) & (x <= ub)]
0.4974 x[np.logical_and(x > lb, x <= ub)]

Pero, *no es compatible con la serie Panda, y NumPy Array es más rápido que el marco de datos de pandas (alrededor de 1000 veces más lento, consulte el número):

from timeit import repeat

setup = """
import numpy as np;
import random;
import pandas as pd;
x = pd.DataFrame(np.linspace(0,100));
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'

for _ in range(3):
    for stmt in stmts:
        t = min(repeat(stmt, setup, number=100))
        print('%.4f' % t, stmt)
    print()

resultado:

0.1964 x[(x > lb) & (x <= ub)]
0.1992 x[np.logical_and(x > lb, x <= ub)]

0.2018 x[(x > lb) & (x <= ub)]
0.1838 x[np.logical_and(x > lb, x <= ub)]

0.1871 x[(x > lb) & (x <= ub)]
0.1883 x[np.logical_and(x > lb, x <= ub)]

Nota: agregar una línea de código x = x.to_numpy()necesitará aproximadamente 20 µs.

Para los que prefieren %timeit:

import numpy as np
import random
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
lb, ub
x = pd.DataFrame(np.linspace(0,100))

def asterik(x):
    x = x.to_numpy()
    return x[(x > lb) * (x <= ub)]

def and_symbol(x):
    x = x.to_numpy()
    return x[(x > lb) & (x <= ub)]

def numpy_logical(x):
    x = x.to_numpy()
    return x[np.logical_and(x > lb, x <= ub)]

for i in range(3):
    %timeit asterik(x)
    %timeit and_symbol(x)
    %timeit numpy_logical(x)
    print('\n')

resultado:

23 µs ± 3.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
35.6 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
31.3 µs ± 8.9 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)


21.4 µs ± 3.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
21.9 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
21.7 µs ± 500 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


25.1 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
36.8 µs ± 18.3 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
28.2 µs ± 5.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
1

Recibí un error en este comando:

if df! = '': pasar

Pero funcionó cuando lo cambié a esto:

si df no es '': pasar

0

Una cosa menor, que me hizo perder el tiempo.

Ponga las condiciones (si compara usando "=", "! =") Entre paréntesis, si no lo hace, también genera esta excepción. Esto funcionará

df[(some condition) conditional operator (some conditions)]

Esto no

df[some condition conditional-operator some condition]
0

Pruebe
result['var'].all()si tiene más de 1 valor Si es un único valor, entonces puede usarresult['var'].item()