O valor da verdade de uma série é ambíguo. Use a.empty, a.bool (), a.item (), a.any () ou a.all ()

369

Problema ao filtrar o quadro de dados do resultado com uma orcondição. Desejo que meu resultado dfextraia todos os varvalores da coluna acima de 0,25 e abaixo de -0,25.

Essa lógica abaixo me fornece um valor de verdade ambíguo, mas funciona quando eu divido essa filtragem em duas operações separadas. O que esta acontecendo aqui? não sabe onde usar o sugerido a.empty(), a.bool(), a.item(),a.any() or a.all().

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
obabs
fonte
46
use em |vez deor
MaxU 28/04
11
Aqui está uma solução alternativa:abs(result['var'])>0.25
ColinMac

Respostas:

566

As instruções ore andpython requerem truth-values. Como pandasestes são considerados ambíguos, você deve usar "bit a bit" |(ou) ou& (e):

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

Elas estão sobrecarregadas para esse tipo de estrutura de dados para gerar a or (ou and).


Apenas para adicionar mais explicações a esta declaração:

A exceção é lançada quando você deseja obter o boolde a 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().

O que você acertar era um lugar onde o operador implicitamente convertido os operandos bool(que usou ormas também acontece para and, ife 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().

Além desses 4 declarações existem várias funções do Python que escondem algumas boolchamadas (como any, all, filter, ...), estes normalmente não são problemáticas compandas.Series mas para ser completo eu queria mencionar estes.


No seu caso, a exceção não é realmente útil, porque não menciona as alternativas corretas . Para ande orvocê pode usar (se quiser comparações entre elementos):

  • numpy.logical_or:

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

    ou simplesmente o |operador:

    >>> x | y
  • numpy.logical_and:

    >>> np.logical_and(x, y)

    ou simplesmente o &operador:

    >>> x & y

Se você estiver usando os operadores, certifique-se de definir seus parênteses corretamente devido a à precedência do operador .

Existem várias funções numpy lógicas que devem funcionar empandas.Series .


As alternativas mencionadas na exceção são mais adequadas se você a encontrar ao executar ifouwhile . Em breve vou explicar cada um destes:

  • Se você deseja verificar se sua série está vazia :

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

    O Python normalmente interpreta o leng - ésimo de contêineres (como list, tuple...) como valor de verdade se não tiver uma interpretação booleana explícita. Portanto, se você quiser a verificação do tipo python, poderá fazer: if x.sizeou em if not x.emptyvez deif x .

  • Se o seu Seriescontiver um e apenas um valor booleano:

    >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
  • Se você deseja verificar o primeiro e único item da sua série (como .bool()mas funciona mesmo para conteúdos não booleanos):

    >>> x = pd.Series([100])
    >>> x.item()
    100
  • Se você deseja verificar se todo ou qualquer item não é zero, não está vazio ou não é 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
fonte
Por que esses operadores python não estão sobrecarregados para lidar com séries de pandas?
Mudit Jain
@MuditJain Não há nenhuma maneira para diretamente sobrecarga and, ore notem Python. Esses operadores usam diretamente o que boolos operandos retornam. E de certa forma, o Pandas / NumPy sobrecarregou isso já para aumentar o valor ValueErrorporque considera o valor de verdade de uma estrutura de dados ambígua.
MSDIFER #
a solução está ok, mas a explicação está longe de ser boa
blacksheep
2
@blacksheep Você tem alguma sugestão do que eu poderia ter explicado melhor?
MSeifert 29/02
É uma ótima explicação. Na verdade, isso me ajudou a entender a lógica bit a bit versus lógica de uma maneira que exemplos mais abstratos falharam.
rocksNwaves
41

Para lógica booleana, use &e |.

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 o que está acontecendo, você recebe uma coluna de booleanos para cada comparação, por exemplo

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

Quando você possui vários critérios, receberá várias colunas retornadas. É por isso que a lógica de junção é ambígua. Utilizando andou ortratando cada coluna separadamente, primeiro é necessário reduzir essa coluna para um único valor booleano. Por exemplo, para ver se algum valor ou todos os valores em cada uma das colunas são True.

# 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

Uma maneira complicada de obter a mesma coisa é compactar todas essas colunas e executar a lógica apropriada.

>>> 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 mais detalhes, consulte Indexação booleana na documentação.

Alexander
fonte
20

Bem, os pandas usam '&' '|' bit a bit e cada condição deve ser envolvida em um '()'

Por exemplo, seguindo trabalhos

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

Mas a mesma consulta sem colchetes adequados não

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

Ou, como alternativa, você pode usar o módulo Operador. Informações mais detalhadas estão aqui documentos 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
fonte
1

Esta excelente resposta explica muito bem o que está acontecendo e fornece uma solução. Gostaria de adicionar outra solução que possa ser adequada em casos semelhantes: usando o querymétodo:

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

Veja também http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query .

(Alguns testes com um dataframe em que estou trabalhando sugerem que esse método é um pouco mais lento do que usar os operadores bit a bit em séries de booleanos: 2 ms vs. 870 µs)

Um aviso : pelo menos uma situação em que isso não é direto é quando os nomes das colunas são expressões python. Eu tinha colunas chamado WT_38hph_IP_2, WT_38hph_input_2elog2(WT_38hph_IP_2/WT_38hph_input_2) e queria executar a seguinte consulta:"(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

Eu obtive a seguinte cascata de exceção:

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

Acho que isso aconteceu porque o analisador de consultas estava tentando criar algo com as duas primeiras colunas em vez de identificar a expressão com o nome da terceira coluna.

Uma possível solução alternativa é proposta aqui .

bli
fonte
1

Encontrei o mesmo erro e fiquei paralisado com um dataframe do pyspark por alguns dias. Consegui resolvê-lo com êxito preenchendo valores n com 0, pois estava comparando valores inteiros de 2 campos.

iretex
fonte