TLDR; Logische Operatoren in Pandas sind &
, |
und ~
, und Klammern (...)
sind wichtig!
Python ist and
, or
und not
logische Operatoren sind mit Skalare Arbeit entwickelt. Pandas musste also eine bessere Leistung erbringen und die bitweisen Operatoren überschreiben, um eine vektorisierte (elementweise) Version dieser Funktionalität zu erhalten.
Also das Folgende in Python ( exp1
und exp2
sind Ausdrücke, die zu einem booleschen Ergebnis führen) ...
exp1 and exp2 # Logical AND
exp1 or exp2 # Logical OR
not exp1 # Logical NOT
... wird übersetzt in ...
exp1 & exp2 # Element-wise logical AND
exp1 | exp2 # Element-wise logical OR
~exp1 # Element-wise logical NOT
für Pandas.
Wenn Sie bei der Ausführung einer logischen Operation eine erhalten ValueError
, müssen Sie Klammern für die Gruppierung verwenden:
(exp1) op (exp2)
Beispielsweise,
(df['col1'] == x) & (df['col2'] == y)
Und so weiter.
Boolesche Indizierung : Eine übliche Operation besteht darin, boolesche Masken unter logischen Bedingungen zu berechnen, um die Daten zu filtern. Pandas bietet drei Operatoren:&
für logisches UND,|
für logisches ODER und~
für logisches NICHT.
Betrachten Sie das folgende Setup:
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
oben, sagen Sie alle Zeilen zurückgeben möchten , in der A <5 und B> 5. Diese durch Berechnung Masken für jede Bedingung separat durchgeführt wird, und Anding sie.
Überladener bitweiser &
Operator
Bevor Sie fortfahren, beachten Sie bitte diesen speziellen Auszug der Dokumente, die angeben
Eine weitere häufige Operation ist die Verwendung von Booleschen Vektoren zum Filtern der Daten. Die Operatoren sind: |
für or
, &
für and
und ~
für not
. Diese müssen durch die Verwendung von Klammern gruppiert werden , da standardmäßig Python wertet den Ausdruck wie df.A > 2 & df.B < 3
wie df.A > (2 &
df.B) < 3
, während die gewünschte Auswertungsreihenfolge ist (df.A > 2) & (df.B <
3)
.
In diesem Sinne kann das elementweise logische 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 nachfolgende Filterungsschritt 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 Standardrangfolge von bitweisen Operatoren zu überschreiben, die eine höhere Priorität als die bedingten Operatoren <
und haben >
. Siehe den Abschnitt Operator-Vorrang im in den Python-Dokumenten.
Wenn Sie keine Klammern verwenden, wird der Ausdruck falsch ausgewertet. Zum Beispiel, wenn Sie versehentlich etwas versuchen wie
df['A'] < 5 & df['B'] > 5
Es wird analysiert als
df['A'] < (5 & df['B']) > 5
Welches wird,
df['A'] < something_you_dont_want > 5
Welches wird (siehe die Python-Dokumente zum verketteten Operatorvergleich ),
(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)
Welches wird,
# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2
Welches wirft
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Machen Sie diesen Fehler also nicht! 1
Vermeiden der Gruppierung von Klammern
Die Korrektur ist eigentlich recht einfach. Die meisten Operatoren haben eine entsprechende gebundene Methode für DataFrames. Wenn die einzelnen Masken mithilfe von Funktionen anstelle von bedingten Operatoren erstellt werden, müssen Sie nicht mehr nach Parens gruppieren, um die Bewertungsreihenfolge 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 Option zum Vermeiden von Klammern ist die Verwendung 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 dynamischer Expression Evaluation in Pandas mit pd.eval () .
operator.and_
Ermöglicht es Ihnen, diesen Vorgang auf funktionale Weise auszuführen. Interne Aufrufe, Series.__and__
die dem bitweisen Operator entsprechen.
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
Normalerweise brauchen Sie das nicht, aber es ist nützlich zu wissen.
Verallgemeinern: np.logical_and
(und logical_and.reduce
)
Eine andere Alternative ist die Verwendung np.logical_and
, für die auch keine Gruppierung in Klammern erforderlich ist:
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 (Universal Functions) , und die meisten Ufuncs haben eine reduce
Methode. Dies bedeutet, dass es einfacher ist, zu verallgemeinern, logical_and
wenn Sie mehrere Masken für AND haben. Zum Beispiel zu UND-Masken m1
und m2
und m3
mit &
müssten Sie tun
m1 & m2 & m3
Eine einfachere Option ist jedoch
np.logical_and.reduce([m1, m2, m3])
Dies ist leistungsstark, da Sie mit einer komplexeren Logik darauf aufbauen können (z. B. indem Sie 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 in diesem Punkt Harfe spiele, aber bitte ertrage es mit mir. Dies ist ein sehr , sehr häufiger Anfängerfehler und muss sehr gründlich erklärt werden.
Logisches ODER
Für die df
oben, sagen Sie alle Zeilen zurückgeben möchten , in der A == 3 oder B == 7.
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
Wenn Sie dies noch nicht getan haben, lesen Sie bitte auch den Abschnitt über logisches UND oben. Alle Vorbehalte gelten hier.
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_
Ruft 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
Logisch NICHT
Gegeben eine Maske, wie z
mask = pd.Series([True, True, False])
Wenn Sie jeden booleschen Wert invertieren müssen (damit das Endergebnis ist [False, False, True]
), können Sie eine der folgenden Methoden verwenden.
Bitweise ~
~mask
0 False
1 False
2 True
dtype: bool
Auch hier müssen Ausdrücke in Klammern gesetzt werden.
~(df['A'] == 3)
0 True
1 False
2 False
3 True
4 True
Name: A, dtype: bool
Dies ruft intern auf
mask.__invert__()
0 False
1 False
2 True
dtype: bool
Aber benutze es nicht direkt.
operator.inv
Intern ruft __invert__
die Serie auf.
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
Hinweis, np.logical_and
kann durch np.bitwise_and
, logical_or
mit bitwise_or
und logical_not
mit ersetzt werden invert
.