Logische Operatoren für die boolesche Indizierung in Pandas

Lesezeit: 15 Minuten

Benutzer-Avatar
Benutzer2988577

Ich arbeite mit einem booleschen Index in Pandas.

Die Frage ist, warum die Aussage:

a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]

funktioniert gut, während

a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]

mit Fehler beendet?

Beispiel:

a = pd.DataFrame({'x':[1,1],'y':[10,20]})

In: a[(a['x']==1)&(a['y']==10)]
Out:    x   y
     0  1  10

In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous.     Use a.any() or a.all()

  • Dies liegt daran, dass numpy-Arrays und Pandas-Serien die bitweisen Operatoren und nicht die logischen verwenden, da Sie jedes Element im Array/in der Serie mit einem anderen vergleichen. Daher ist es in dieser Situation nicht sinnvoll, den logischen Operator zu verwenden. siehe verwandt: stackoverflow.com/questions/8632033/…

    – EdChum

    28. Januar 2014 um 20:15 Uhr


  • Bei Python and != &. Das and Operator in Python kann nicht überschrieben werden, wohingegen der & Operator (__and__) kann. Daher die Wahl der Verwendung & in Numpy und Pandas.

    – Steven Rumbalski

    28. Januar 2014 um 20:19 Uhr


  • Verwandt: Der Wahrheitswert einer Serie ist mehrdeutig. Verwenden Sie a.empty, a.bool(), a.item(), a.any() oder a.all()

    – cs95

    8. März 2019 um 22:10 Uhr

  • Die Titel- oder Schlüsselwortliste soll numpy enthalten, aber die Bearbeitungswarteschlange ist voll.

    – Rainald62

    21. Juli um 10:56 Uhr

Benutzer-Avatar
unutbu

Wenn du sagst

(a['x']==1) and (a['y']==10)

Sie fordern Python implizit auf, zu konvertieren (a['x']==1) und (a['y']==10) zu booleschen Werten.

NumPy-Arrays (mit einer Länge größer als 1) und Pandas-Objekte wie Series haben keinen booleschen Wert – mit anderen Worten, sie erhöhen

ValueError: Der Wahrheitswert eines Arrays ist mehrdeutig. Verwenden Sie a.empty, a.any() oder a.all().

bei Verwendung als boolescher Wert. Das ist, weil es ist unklar, wann es wahr oder falsch sein sollte. Einige Benutzer nehmen möglicherweise an, dass sie wahr sind, wenn sie eine Länge ungleich Null haben, wie eine Python-Liste. Andere mögen wünschen, dass es nur wahr ist, wenn alle seine Elemente sind wahr. Andere möchten vielleicht, dass es True if ist irgendein seiner Elemente sind wahr.

Da es so viele widersprüchliche Erwartungen gibt, weigern sich die Designer von NumPy und Pandas zu raten und lösen stattdessen einen ValueError aus.

Stattdessen müssen Sie explizit sein, indem Sie die aufrufen empty(), all() oder any() Methode, um anzuzeigen, welches Verhalten Sie wünschen.

In diesem Fall sieht es jedoch so aus, als ob Sie keine boolesche Auswertung wollen, Sie wollen elementweise logisch-und. Das ist, was die & binärer Operator führt aus:

(a['x']==1) & (a['y']==10)

gibt ein boolesches Array zurück.


Übrigens, wie alexpmil anmerkt, sind die Klammern seither obligatorisch & hat eine höhere Operator Vorrang als ==.

Ohne die Klammern a['x']==1 & a['y']==10 würde gewertet als a['x'] == (1 & a['y']) == 10 was wiederum dem verketteten Vergleich äquivalent wäre (a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10). Das ist ein Ausdruck der Form Series and Series. Die Verwendung von and bei zwei Serien würde das wieder das gleiche auslösen ValueError wie oben. Deshalb sind die Klammern obligatorisch.

  • numpy-Arrays haben diese Eigenschaft wenn Sie sind Länge eins. Nur Panda-Entwickler weigern sich (hartnäckig) zu raten :p

    – Andy Hayden

    28. Januar 2014 um 20:37 Uhr


  • Hat ‘&’ nicht die gleiche mehrdeutige Kurve wie ‘and’? Wie kommt es, dass beim Thema „&“ plötzlich alle Benutzer übereinstimmen, dass es elementweise sein sollte, während ihre Erwartungen bei „und“ unterschiedlich sind?

    – Indominus

    15. April 2016 um 21:17 Uhr

  • @Indominus: Die Python-Sprache selbst erfordert dass der Ausdruck x and y löst die Auswertung aus bool(x) und bool(y). Python “wertet zuerst aus x; wenn x falsch ist, wird sein Wert zurückgegeben; Andernfalls, y ausgewertet und der resultierende Wert zurückgegeben.” Also die Syntax x and y kann nicht für elementweise logische und da nur verwendet werden x oder y kann zurückgegeben werden. Im Gegensatz, x & y löst aus x.__and__(y) und die __and__ -Methode kann so definiert werden, dass sie alles zurückgibt, was wir möchten.

    – unutbu

    15. April 2016 um 22:58 Uhr

  • Wichtig zu beachten: Die Klammern um die == Klausel sind obligatorisch. a['x']==1 & a['y']==10 gibt denselben Fehler wie in der Frage zurück.

    – Alex P. Miller

    18. Juli 2017 um 18:41 Uhr

  • Wofür steht ” | “?

    – Euler_Salter

    24. Januar 2018 um 9:02 Uhr

Benutzer-Avatar
cs95

TLDR; Logische Operatoren in Pandas sind &, | und ~und Klammern (...) ist wichtig!

Pythons and, or und not Logische Operatoren sind für die Arbeit mit Skalaren ausgelegt. Pandas musste also noch eins draufsetzen und die bitweisen Operatoren überschreiben, um das zu erreichen vektorisiert (elementweise) Version dieser Funktionalität.

Also das Folgende in Python (exp1 und exp2 sind Ausdrücke, die zu einem boolschen Ergebnis ausgewertet werden)…

exp1 and exp2              # Logical AND
exp1 or exp2               # Logical OR
not exp1                   # Logical NOT

…übersetzt zu…

exp1 & exp2                # Element-wise logical AND
exp1 | exp2                # Element-wise logical OR
~exp1                      # Element-wise logical NOT

für Pandas.

Wenn Sie beim Ausführen einer logischen Operation a erhalten ValueErrordann müssen Sie Klammern zum Gruppieren verwenden:

(exp1) op (exp2)

Zum Beispiel,

(df['col1'] == x) & (df['col2'] == y) 

Usw.


Boolesche Indizierung: Eine übliche Operation besteht darin, boolesche Masken durch logische Bedingungen zu berechnen, um die Daten zu filtern. Pandas bietet drei Betreiber: & für logisches UND, | für logisches ODER und ~ für logisches NICHT.

Betrachten Sie die folgende Konfiguration:

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC'))
df

   A  B  C
0  5  0  3
1  3  7  9
2  3  5  2
3  4  7  6
4  8  8  1

Logisches UND

Zum df Sagen Sie oben, Sie möchten alle Zeilen zurückgeben, in denen A < 5 und B > 5 ist. Dies geschieht, indem Sie Masken für jede Bedingung separat berechnen und sie UND-verknüpfen.

Bitweise überladen & Operator

Bevor Sie fortfahren, beachten Sie bitte diesen speziellen Auszug aus den Dokumenten, der besagt

Eine weitere übliche Operation ist die Verwendung von booleschen Vektoren zum Filtern der Daten. Die Operatoren sind: | zum or, & zum andund ~ zum not. Diese müssen mit Klammern gruppiert werdenda Python standardmäßig einen Ausdruck wie z df.A > 2 & df.B < 3 wie df.A > (2 &
df.B) < 3
während die gewünschte Auswertungsreihenfolge lautet (df.A > 2) & (df.B <
3)
.

Vor diesem Hintergrund kann ein elementweises logisches UND mit dem bitweisen Operator implementiert werden &:

df['A'] < 5

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'] > 5

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool

(df['A'] < 5) & (df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

Und der anschließende Filterschritt ist einfach,

df[(df['A'] < 5) & (df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

Die Klammern werden verwendet, um die Standardprioritätsreihenfolge bitweiser Operatoren zu überschreiben, die Vorrang vor den bedingten Operatoren haben < und >. Siehe den Abschnitt von Vorrang des Operators in der Python-Dokumentation.

Wenn Sie keine Klammern verwenden, wird der Ausdruck falsch ausgewertet. Wenn Sie beispielsweise versehentlich etwas wie z

df['A'] < 5 & df['B'] > 5

Es wird analysiert als

df['A'] < (5 & df['B']) > 5

Was wird,

df['A'] < something_you_dont_want > 5

Was wird (siehe die Python-Dokumentation auf Verketteter Operatorvergleich),

(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)

Was wird,

# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2

Welche wirft

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Machen Sie also nicht diesen Fehler!1

Klammergruppierung vermeiden

Die Lösung ist eigentlich ganz einfach. Die meisten Operatoren haben eine entsprechende gebundene Methode für DataFrames. Wenn die einzelnen Masken mit Funktionen anstelle von Bedingungsoperatoren aufgebaut sind, müssen Sie nicht mehr nach Klammern gruppieren, um die Auswertungsreihenfolge anzugeben:

df['A'].lt(5)

0     True
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'].gt(5)

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool

df['A'].lt(5) & df['B'].gt(5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

Siehe den Abschnitt über Flexible Vergleiche.. Zusammenfassend haben wir

╒════╤════════════╤════════════╕
│    │ Operator   │ Function   │
╞════╪════════════╪════════════╡
│  0 │ >          │ gt         │
├────┼────────────┼────────────┤
│  1 │ >=         │ ge         │
├────┼────────────┼────────────┤
│  2 │ <          │ lt         │
├────┼────────────┼────────────┤
│  3 │ <=         │ le         │
├────┼────────────┼────────────┤
│  4 │ ==         │ eq         │
├────┼────────────┼────────────┤
│  5 │ !=         │ ne         │
╘════╧════════════╧════════════╛

Eine weitere Möglichkeit, Klammern zu vermeiden, ist use DataFrame.query (oder eval):

df.query('A < 5 and B > 5')

   A  B  C
1  3  7  9
3  4  7  6

Ich habe ausführlich dokumentiert query und eval in der dynamischen Ausdrucksauswertung in Pandas mit pd.eval().

operator.and_

Ermöglicht es Ihnen, diesen Vorgang auf funktionale Weise auszuführen. Anrufe intern Series.__and__ was dem bitweisen Operator entspricht.

import operator 

operator.and_(df['A'] < 5, df['B'] > 5)
# Same as,
# (df['A'] < 5).__and__(df['B'] > 5) 

0    False
1     True
2    False
3     True
4    False
dtype: bool

df[operator.and_(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

Sie werden dies normalerweise nicht brauchen, aber es ist nützlich zu wissen.

Verallgemeinerung: np.logical_and (und logical_and.reduce)

Eine andere Alternative ist die Verwendung np.logical_anddie auch keine Klammergruppierung benötigt:

np.logical_and(df['A'] < 5, df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
Name: A, dtype: bool

df[np.logical_and(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

np.logical_and ist ein ufunc (Universelle Funktionen)und die meisten ufuncs haben a reduce Methode. Dies bedeutet, dass es einfacher ist, mit zu verallgemeinern logical_and wenn Sie mehrere Masken mit UND haben. Zum Beispiel zu UND-Masken m1 und m2 und m3 mit &müsstest du machen

m1 & m2 & m3

Es gibt jedoch eine einfachere Option

np.logical_and.reduce([m1, m2, m3])

Dies ist leistungsfähig, da Sie damit mit komplexerer Logik darauf aufbauen können (z. B. Masken in einem Listenverständnis dynamisch generieren und alle hinzufügen):

import operator

cols = ['A', 'B']
ops = [np.less, np.greater]
values = [5, 5]

m = np.logical_and.reduce([op(df[c], v) for op, c, v in zip(ops, cols, values)])
m 
# array([False,  True, False,  True, False])

df[m]
   A  B  C
1  3  7  9
3  4  7  6

1 – Ich weiß, dass ich an diesem Punkt herumzicke, aber bitte haben Sie Geduld mit mir. Das ist ein sehr, sehr ein häufiger Anfängerfehler und muss sehr genau erklärt werden.


Logisches ODER

Für die df Sagen Sie oben, Sie möchten alle Zeilen zurückgeben, in denen A == 3 oder B == 7 ist.

Bitweise überladen |

df['A'] == 3

0    False
1     True
2     True
3    False
4    False
Name: A, dtype: bool

df['B'] == 7

0    False
1     True
2    False
3     True
4    False
Name: B, dtype: bool

(df['A'] == 3) | (df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[(df['A'] == 3) | (df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

Falls noch nicht geschehen, lesen Sie bitte auch den Abschnitt über Logisches UND oben gelten hier alle Vorbehalte.

Alternativ kann diese Operation mit angegeben werden

df[df['A'].eq(3) | df['B'].eq(7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

operator.or_

Anrufe Series.__or__ unter der Haube.

operator.or_(df['A'] == 3, df['B'] == 7)
# Same as,
# (df['A'] == 3).__or__(df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[operator.or_(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

np.logical_or

Verwenden Sie für zwei Bedingungen logical_or:

np.logical_or(df['A'] == 3, df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df[np.logical_or(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

Verwenden Sie für mehrere Masken logical_or.reduce:

np.logical_or.reduce([df['A'] == 3, df['B'] == 7])
# array([False,  True,  True,  True, False])

df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

Logisches NICHT

Angesichts einer Maske, wie z

mask = pd.Series([True, True, False])

Wenn Sie jeden booleschen Wert invertieren müssen (so dass das Endergebnis [False, False, True]), dann können Sie eine der folgenden Methoden verwenden.

Bitweise ~

~mask

0    False
1    False
2     True
dtype: bool

Auch hier müssen Ausdrücke eingeklammert werden.

~(df['A'] == 3)

0     True
1    False
2    False
3     True
4     True
Name: A, dtype: bool

Dieser ruft intern an

mask.__invert__()

0    False
1    False
2     True
dtype: bool

Aber nicht direkt verwenden.

operator.inv

Anrufe intern __invert__ auf der Serie.

operator.inv(mask)

0    False
1    False
2     True
dtype: bool

np.logical_not

Dies ist die numpy-Variante.

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool

Notiz, np.logical_and kann ersetzt werden np.bitwise_and, logical_or mit bitwise_orund logical_not mit invert.

  • @ cs95 im TLDR, für elementweises boolesches ODER befürworten Sie die Verwendung |was gleichbedeutend ist mit numpy.bitwise_orAnstatt von numpy.logical_or. Darf ich fragen warum? Ist nicht numpy.logical_or speziell für diese Aufgabe konzipiert? Warum die Last hinzufügen, es bitweise für jedes Paar von Elementen zu tun?

    – flow2k

    13. Juni 2019 um 21:40 Uhr

  • @flow2k kannst du bitte den entsprechenden Text zitieren? Ich kann nicht finden, worauf Sie sich beziehen. FWIW Ich behaupte, dass logical_* das korrekte funktionale Äquivalent der Operatoren ist.

    – cs95

    13. Juni 2019 um 21:50 Uhr

  • @ cs95 Ich beziehe mich auf die erste Zeile der Antwort: “TLDR; Logische Operatoren in Pandas sind &, | und ~”.

    – flow2k

    13. Juni 2019 um 21:59 Uhr

  • @flow2k Es ist buchstäblich in der Dokumentation: “Eine weitere häufige Operation ist die Verwendung von booleschen Vektoren zum Filtern der Daten. Die Operatoren sind: | für oder, & für und und ~ für nicht.”

    – cs95

    13. Juni 2019 um 22:05 Uhr

  • @ cs95, ok, ich habe gerade diesen Abschnitt gelesen, und er verwendet | für elementweise boolesche Operationen. Aber für mich ist diese Dokumentation eher ein “Tutorial”, und im Gegensatz dazu sind diese API-Referenzen meiner Meinung nach näher an der Quelle der Wahrheit: numpy.bitwise_or und numpy.logical_or – also versuche ich zu verstehen, was hier beschrieben wird.

    – flow2k

    14. Juni 2019 um 0:11 Uhr

Benutzer-Avatar
MSeifert

Logische Operatoren für die boolesche Indizierung in Pandas

Es ist wichtig zu wissen, dass Sie kein Python verwenden können logische Operatoren (and, or oder not) an pandas.Series oder pandas.DataFrames (ebenso können Sie sie nicht auf verwenden numpy.arrays mit mehr als einem Element). Der Grund, warum Sie diese nicht verwenden können, ist, dass sie implizit aufrufen bool auf ihren Operanden, die eine Ausnahme auslöst, weil diese Datenstrukturen entschieden haben, dass der boolesche Wert eines Arrays mehrdeutig ist:

>>> import numpy as np
>>> import pandas as pd
>>> arr = np.array([1,2,3])
>>> s = pd.Series([1,2,3])
>>> df = pd.DataFrame([1,2,3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> bool(s)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> bool(df)
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Ich habe dies ausführlicher in meiner Antwort auf die Frage „Der Wahrheitswert einer Reihe ist mehrdeutig. Verwenden Sie a.empty, a.bool(), a.item(), a.any() oder a.all()“ Q behandelt +A.

Die logischen Funktionen von NumPy

Jedoch NumPy bietet elementweise operative Äquivalente zu diesen Operatoren als Funktionen, die verwendet werden können numpy.array, pandas.Series, pandas.DataFrameoder jede andere (konforme) numpy.array Unterklasse:

Also sollte man im Wesentlichen verwenden (vorausgesetzt df1 und df2 sind Pandas DataFrames):

np.logical_and(df1, df2)
np.logical_or(df1, df2)
np.logical_not(df1)
np.logical_xor(df1, df2)

Bitweise Funktionen und bitweise Operatoren für Boolesche Werte

Falls Sie jedoch ein boolesches NumPy-Array, eine Pandas-Serie oder Pandas-DataFrames haben, können Sie auch die verwenden elementweise bitweise Funktionen (für boolesche Werte sind sie – oder sollten es zumindest sein – nicht von den logischen Funktionen zu unterscheiden):

Typischerweise werden die Operatoren verwendet. In Kombination mit Vergleichsoperatoren muss man jedoch daran denken, den Vergleich in Klammern zu setzen, da die bitweisen Operatoren ein a haben Vorrang vor den Vergleichsoperatoren:

(df1 < 10) | (df2 > 10)  # instead of the wrong df1 < 10 | df2 > 10

Dies kann irritierend sein, da die logischen Python-Operatoren eine niedrigere Priorität haben als die Vergleichsoperatoren, also schreiben Sie normalerweise a < 10 and b > 10 (wo a und b sind zum Beispiel einfache ganze Zahlen) und brauchen keine Klammern.

Unterschiede zwischen logischen und bitweisen Operationen (bei nicht-booleschen Werten)

Es ist wirklich wichtig zu betonen, dass Bit- und logische Operationen nur für boolesche NumPy-Arrays (und boolesche Serien und Datenrahmen) gleichwertig sind. Wenn diese keine booleschen Werte enthalten, führen die Operationen zu anderen Ergebnissen. Ich werde Beispiele mit NumPy-Arrays einfügen, aber die Ergebnisse werden für die Pandas-Datenstrukturen ähnlich sein:

>>> import numpy as np
>>> a1 = np.array([0, 0, 1, 1])
>>> a2 = np.array([0, 1, 0, 1])

>>> np.logical_and(a1, a2)
array([False, False, False,  True])
>>> np.bitwise_and(a1, a2)
array([0, 0, 0, 1], dtype=int32)

Und da NumPy (und ähnlich Pandas) verschiedene Dinge für Boolean (Boolesche oder „maskierte“ Indexarrays) und Ganzzahl (Index-Arrays)-Indizes werden die Ergebnisse der Indizierung ebenfalls unterschiedlich sein:

>>> a3 = np.array([1, 2, 3, 4])

>>> a3[np.logical_and(a1, a2)]
array([4])
>>> a3[np.bitwise_and(a1, a2)]
array([1, 1, 1, 2])

Übersichtstabelle

Logical operator | NumPy logical function | NumPy bitwise function | Bitwise operator
-------------------------------------------------------------------------------------
       and       |  np.logical_and        | np.bitwise_and         |        &
-------------------------------------------------------------------------------------
       or        |  np.logical_or         | np.bitwise_or          |        |
-------------------------------------------------------------------------------------
                 |  np.logical_xor        | np.bitwise_xor         |        ^
-------------------------------------------------------------------------------------
       not       |  np.logical_not        | np.invert              |        ~

Wo Der logische Operator funktioniert nicht für NumPy-Arrays, Pandas Series und Pandas DataFrames. Die anderen arbeiten an diesen Datenstrukturen (und einfachen Python-Objekten) und arbeiten elementweise. Seien Sie jedoch vorsichtig mit der bitweisen Umkehrung in einfachem Python bools weil bool in diesem Zusammenhang als ganze Zahlen interpretiert wird (z ~False kehrt zurück -1 und ~True kehrt zurück -2).

1063210cookie-checkLogische Operatoren für die boolesche Indizierung in Pandas

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy