2016-04-28 7 views
63

Problem beim Filtern meines Ergebnisdatenrahmens mit einer oder-Bedingung. Ich möchte mein Ergebnis df alle Spalte var Werte über 0,25 und unter -0,25 extrahieren. Diese Logik gibt mir einen mehrdeutigen Wahrheitswert, aber es funktioniert, wenn ich diese Filterung in zwei getrennte Operationen aufteile. Was passiert hier? nicht sicher, wo die vorgeschlagenen a.empty(), a.bool(), a.item(), a.any() oder a.all() zu verwenden.Wahrheitswert einer Serie ist mehrdeutig. Verwenden Sie a.empty, a.bool(), a.item(), a.any() oder a.all()

result = result[(result['var']>0.25) or (result['var']<-0.25)] 
+0

Sie sollten mehr Kontext hinzufügen. Ich verstehe nicht, was das Ergebnis ist und was du zu tun versuchst. – kingledion

+5

Verwenden Sie '|' anstelle von 'oder' – MaxU

Antwort

121

Die or und and Python-Anweisungen erfordern truth -Werten. Für pandas diese gelten als mehrdeutig so sollten Sie „bitweise“ | (oder) oder & (und) Operationen verwenden:

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

Diese sind für diese Art von Datenstrukturen überlastet die elementweise or (oder and) zu erhalten .


einfach etwas mehr Erklärung zu dieser Aussage hinzuzufügen:

Die Ausnahme wird ausgelöst, wenn Sie die bool eines pandas.Series erhalten möchten:

>>> 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(). 

Was Sie schlagen war ein Ort, an dem der Operator implizit konvertierte die Operanden zu bool (Sie verwendeten or, aber es passiert auch für and, if und 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(). 

Neben diesen vier Aussagen mehrere Python-Funktionen sind, die einige bool Anrufe (wie any, all, filter, ...) diese sind in der Regel nicht problematisch mit pandas.Series aber der Vollständigkeit halber verstecken wollte ich um diese zu erwähnen.


In Ihrem Fall ist die Ausnahme nicht wirklich hilfreich, weil es nicht die richtigen Alternativen nicht erwähnt.Für and und or Sie verwenden können (wenn Sie Element weise Vergleiche wollen):

  • numpy.logical_or:

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

    oder einfach die | Betreiber:

    >>> x | y 
    
  • numpy.logical_and:

    >>> np.logical_and(x, y) 
    

    oder einfach die & Betreiber:

    >>> x & y 
    

Wenn Sie die Operatoren sind dann stellen Sie sicher, dass Sie Ihre Klammer richtig wegen the operator precedence gesetzt.

Es gibt several logical numpy functions die sollte arbeiten auf pandas.Series.


Die Alternativen in der Ausnahme erwähnt werden, sind besser geeignet, wenn Sie es auf, wenn if oder while tun. Ich werde in Kürze jede dieser erklären:

  • Wenn Sie möchten, zu überprüfen, ob Ihr Series leer ist:

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

    Python normalerweise interpretiert die len gth von Containern (wie list, tuple, ...) als Wahrheitswert, wenn es keine explizite boolesche Interpretation gibt. Also, wenn Sie die Python-artige Prüfung möchten, könnten Sie tun: if x.size oder if not x.empty anstelle von if x.

  • Wenn Ihre Series enthält ein und nur ein Booleschen Wert:

    >>> x = pd.Series([100]) 
    >>> (x > 50).bool() 
    True 
    >>> (x < 50).bool() 
    False 
    
  • Wenn Sie das erste und einziges Element Ihre Serie (wie .bool() aber auch für nicht boolean funktioniert überprüfen mögen Inhalt):

    >>> x = pd.Series([100]) 
    >>> x.item() 
    100 
    
  • wenn Sie, wenn überprüfen möchten ein ll oder jeder Artikel ist nicht Null ist, nicht leer oder nicht-False:

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

Oh mein Gott! Ihr Kommentar "Wenn Sie die Operatoren verwenden, stellen Sie sicher, dass Sie die Klammern richtig setzen, weil der Operator Vorrang hat", hat das Problem gelöst, das mich in den Wahnsinn getrieben hat. Ein sehr wichtiger und in meinem Fall übersehener Punkt. Vielen Dank! – user4896331

+2

eine der informativsten Antworten, die ich seit langem gelesen habe – deadcode

15

für Boolesche Logik verwendet & und |.

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 

Um zu sehen, was passiert, erhalten Sie eine Spalte von boolean für jeden Vergleich, z.

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

Wenn Sie mehrere Kriterien haben, erhalten Sie mehrere zurückgegebene Spalten. Aus diesem Grund ist die Join-Logik mehrdeutig. Wenn Sie and oder or verwenden, wird jede Spalte separat behandelt. Daher müssen Sie diese Spalte zuerst auf einen einzelnen booleschen Wert reduzieren. Zum Beispiel, um zu sehen, ob ein Wert oder alle Werte in jeder der Spalten True ist.

# 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 

Ein gewundener Weg, um die gleiche Sache zu erreichen ist, alle dieser Spalten zusammen zip, und die entsprechende Logik auszuführen.

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

Weitere Einzelheiten finden Sie unter Boolean Indexing in der Dokumentation.

2

Oder alternativ könnten Sie Operator-Modul verwenden. Nähere Informationen finden Sie hier 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 
0

This excellent answer sehr gut erklärt, was eine Lösung geschieht, und zur Verfügung stellt. Ich möchte eine andere Lösung hinzuzufügen, die in ähnlichen Fällen geeignet sein könnten: mit der query Methode:

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

Siehe auch http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query.

(Einige Tests mit einem Datenrahmen arbeite ich zur Zeit mit deuten darauf hin, dass diese Methode ein wenig langsamer ist die Bit-Operatoren auf einer Reihe von booleans als die Verwendung von 2 ms vs. 870 us)

Ein Stück Warnung: Mindestens eine Situation, in der dies nicht einfach ist, wenn Spaltennamen Python-Ausdrücke sind. Ich hatte Spalten namens WT_38hph_IP_2, WT_38hph_input_2 und log2(WT_38hph_IP_2/WT_38hph_input_2) und wollte die folgende Abfrage ausführen: "(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

I erhalten die folgende Ausnahme Kaskade:

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

Ich vermute, das passierte, weil die Abfrage par ser versuchte, etwas aus den ersten beiden Spalten zu machen, anstatt den Ausdruck mit dem Namen der dritten Spalte zu identifizieren.

Eine mögliche Problemumgehung wird here vorgeschlagen.