Wartość prawdy serii jest niejednoznaczna. Użyj a.empty, a.bool (), a.item (), a.any () lub a.all ()

366

Problem z filtrowaniem wynikowej ramki danych z orwarunkiem. Chcę, aby mój wynik dfwyodrębnił wszystkie varwartości kolumn powyżej 0,25 i poniżej -0,25.

Ta logika poniżej daje mi dwuznaczną wartość prawdy, jednak działa, gdy podzielę filtrowanie na dwie osobne operacje. Co tu się dzieje? nie wiem, gdzie użyć sugerowanego a.empty(), a.bool(), a.item(),a.any() or a.all().

 result = result[(result['var']>0.25) or (result['var']<-0.25)]
obabs
źródło
46
użyj |zamiastor
MaxU
1
Oto obejście:abs(result['var'])>0.25
ColinMac

Odpowiedzi:

565

Instrukcje ori andpython wymagają truthwartości -values. Ponieważ pandassą one uważane za niejednoznaczne, dlatego należy stosować operacje „bitowe” |(lub) lub &(i):

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

Są one przeciążone dla tego rodzaju struktur danych, aby uzyskać element or(lub and).


Aby dodać więcej wyjaśnień do tego oświadczenia:

Wyjątkiem jest generowany, gdy chcesz uzyskać booltematyce 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().

Co trafisz był miejscem, gdzie operator niejawnie konwertowane do argumentów bool(użyłeś orale zdarza się również and, ifi 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().

Oprócz tych 4 sprawozdania istnieje kilka funkcji Pythona, który ukryć niektóre boolpołączenia (jak any, all, filter, ...) nie są normalnie problematyczne pandas.Series, ale dla kompletności chciałem wspomnieć nich.


W twoim przypadku wyjątek nie jest tak naprawdę pomocny, ponieważ nie wspomina o odpowiednich alternatywach . Do andi ormożesz użyć (jeśli chcesz porównania elementarne):

  • numpy.logical_or:

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

    lub po prostu |operator:

    >>> x | y
  • numpy.logical_and:

    >>> np.logical_and(x, y)

    lub po prostu &operator:

    >>> x & y

Jeśli używasz operatorów, upewnij się, że poprawnie ustawiłeś nawias ze względu na pierwszeństwo operatora .

Istnieje kilka logicznych funkcji numpy, które powinny działać pandas.Series.


Alternatywy wymienione w wyjątku są bardziej odpowiednie, jeśli napotkałeś je podczas wykonywania iflub while. Wkrótce wyjaśnię każde z nich:

  • Jeśli chcesz sprawdzić, czy Twoja seria jest pusta :

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

    Python zwykle interpretuje length kontenerów (jak list, tuple...) jako wartość prawdy, jeśli nie ma wyraźnej interpretacji boolowskiej. Więc jeśli chcesz sprawdzić jak w python, możesz zrobić: if x.sizelub if not x.emptyzamiast if x.

  • Jeśli Serieszawiera jedną i tylko jedną wartość logiczną:

    >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
  • Jeśli chcesz sprawdzić pierwszy i jedyny element swojej Serii (jak, .bool()ale działa nawet w przypadku treści nie boolowskich):

    >>> x = pd.Series([100])
    >>> x.item()
    100
  • Jeśli chcesz sprawdzić, czy wszystkie lub którekolwiek elementy nie są zerem, nie są puste lub nie są fałszywe:

    >>> 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
źródło
Dlaczego ci operatorzy pytonów nie są przeciążeni do obsługi serii pand?
Mudit Jain,
@MuditJain Nie ma sposobu, aby bezpośrednio przed przeciążeniem and, ororaz notw Pythonie. Operatorzy ci używają bezpośrednio tego, co boolzwraca operand. I w pewien sposób Pandas / NumPy przeciążyło to już, aby podnieść, ValueErrorponieważ uważają prawdziwość takiej struktury danych za niejednoznaczną.
MSeifert,
rozwiązanie jest w porządku, ale wyjaśnienie jest dalekie od dobrego
blacksheep
2
@blacksheep Czy masz jakieś sugestie, które mógłbym lepiej wyjaśnić?
MSeifert
To świetne wytłumaczenie. Pomogło mi to zrozumieć bitowe vs logiczne w sposób, którego nie dały abstrakcyjne przykłady.
rocksNwaves
41

Dla logiki logicznej użyj &i |.

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

Aby zobaczyć, co się dzieje, otrzymujesz kolumnę logów dla każdego porównania, np

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

Jeśli masz wiele kryteriów, otrzymasz wiele kolumn. Dlatego logika łączenia jest niejednoznaczna. Używanie andlub ortraktowanie każdej kolumny osobno, więc najpierw musisz zredukować tę kolumnę do pojedynczej wartości logicznej. Na przykład, aby sprawdzić, czy jakakolwiek wartość lub wszystkie wartości w każdej kolumnie są prawdziwe.

# 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

Jednym skomplikowanym sposobem osiągnięcia tego samego jest spakowanie wszystkich tych kolumn razem i wykonanie odpowiedniej logiki.

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

Aby uzyskać więcej informacji, zobacz indeksowanie boolowskie w dokumentacji.

Alexander
źródło
20

Cóż, pandy używają bitów „&” „|” i każdy warunek powinien być zawarty w „()”

Na przykład następujące prace

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

Ale to samo zapytanie bez odpowiednich nawiasów nie

data_query = data[(data['year'] >= 2005 & data['year'] <= 2010)]
Nipun
źródło
8

Możesz też skorzystać z modułu operatora. Bardziej szczegółowe informacje znajdują się tutaj w dokumentacji Pythona

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
źródło
1

Ta doskonała odpowiedź bardzo dobrze wyjaśnia, co się dzieje i stanowi rozwiązanie. Chciałbym dodać inne rozwiązanie, które może być odpowiednie w podobnych przypadkach: przy użyciu querymetody:

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

Zobacz także http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query .

(Niektóre testy z ramką danych, nad którymi obecnie pracuję, sugerują, że ta metoda jest nieco wolniejsza niż użycie operatorów bitowych na szeregu wartości logicznych: 2 ms vs. 870 µs)

Ostrzeżenie : Przynajmniej jedna sytuacja, w której nie jest to proste, polega na tym, że nazwy kolumn są wyrażeniami python. Miałem nazwie kolumny WT_38hph_IP_2, WT_38hph_input_2a log2(WT_38hph_IP_2/WT_38hph_input_2)i chciał wykonać następujące zapytanie:"(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

Uzyskałem następującą kaskadę wyjątków:

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

Wydaje mi się, że tak się stało, ponieważ parser zapytań próbował stworzyć coś z dwóch pierwszych kolumn zamiast identyfikować wyrażenie za pomocą nazwy trzeciej kolumny.

Możliwe obejście jest tutaj zaproponowane .

bli
źródło
1

Napotkałem ten sam błąd i utknąłem na ramce danych pyspark na kilka dni, udało mi się go rozwiązać, wypełniając wartości na 0, ponieważ porównywałem wartości całkowite z 2 pól.

iretex
źródło