it-swarm.com.de

Logische Operatoren für die boolesche Indizierung in Pandas

Ich arbeite mit Boolean 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)]

existiert mit fehler?

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()
74
user2988577

Wenn du sagst

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

Sie bitten Python implizit, (a['x']==1) und (a['y']==10) in boolesche Werte zu konvertieren. 

NumPy-Arrays (mit einer Länge von mehr als 1) und Pandas-Objekte wie Series haben keinen booleschen Wert, dh sie erhöhen sich 

ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().

wenn als boolescher Wert verwendet. Das liegt daran, dass es unklar ist, wann Wahr oder Falsch sein sollte . Einige Benutzer gehen möglicherweise davon aus, dass sie True sind, wenn sie nicht null sind, wie eine Python-Liste. Andere wünschen sich vielleicht, dass es nur dann wahr ist, wenn all ihre Elemente wahr sind. Andere möchten möglicherweise, dass es True ist, wenn any seiner Elemente True ist. 

Da es so viele widersprüchliche Erwartungen gibt, wollen die Designer von NumPy und Pandas nicht raten und ziehen stattdessen einen ValueError auf.

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

In diesem Fall sieht es jedoch so aus, als wollten Sie keine boolesche Auswertung, sondern element-wise logisch-und. Der binäre Operator & führt Folgendes aus:

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

gibt ein boolesches Array zurück. 


Übrigens sind, da Anmerkungen von alexpmil , die Klammern obligatorisch sind, da & einen höheren Operator-Vorrang als ==..__ hat. Ohne die Klammern würde a['x']==1 & a['y']==10 als a['x'] == (1 & a['y']) == 10 ausgewertet werden. was wiederum dem verketteten Vergleich (a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10) entspricht. Dies ist ein Ausdruck der Form Series and Series. Die Verwendung von and mit zwei Serien würde wiederum dieselbe ValueError wie oben auslösen. Deshalb sind die Klammern obligatorisch.

131
unutbu

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

Die logischen Operatoren and, or und not von Python wurden für die Arbeit mit Skalaren entwickelt. Also musste Pandas einen Schritt besser machen und die bitweisen Operatoren überschreiben, um vektorisiert (elementweise) Version dieser Funktionalität zu erhalten.

Die folgenden Ausdrücke in python (exp1 Und exp2 Sind Ausdrücke, die zu einem booleschen Ergebnis ausgewertet werden) ...

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

... übersetzt nach ...

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

für Pandas.

Wenn bei der Ausführung einer logischen Operation ein ValueError angezeigt wird, müssen Sie für die Gruppierung Klammern verwenden:

(exp1) op (exp2)

Zum Beispiel,

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

Und so weiter.


Boolesche Indizierung : Eine übliche Operation besteht darin, boolesche Masken durch logische Bedingungen zu berechnen, um die Daten zu filtern. Pandas stellt drei Operatoren bereit: & 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

Sagen Sie für df, Sie möchten alle Zeilen mit A <5 und B> 5 ​​zurückgeben. Dazu werden die Masken für jede Bedingung separat berechnet und mit AND verknüpft.

Bitweiser & Operator überladen
Bevor Sie fortfahren, beachten Sie bitte diesen speziellen Auszug aus den Dokumenten, in denen es heißt

Eine andere übliche 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 mithilfe von Klammern gruppiert werden, da standardmäßig Python einen Ausdruck wie df.A > 2 & df.B < 3 als df.A > (2 & df.B) < 3, während die gewünschte Auswertungsreihenfolge (df.A > 2) & (df.B < 3) ist.

Aus diesem Grund 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 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 Standardreihenfolge der bitweisen Operatoren zu überschreiben, die Vorrang vor den bedingten Operatoren < Und > Haben. Weitere Informationen finden Sie im Abschnitt Operator-Rangfolge in den python docs.

Wenn Sie keine Klammern verwenden, wird der Ausdruck falsch ausgewertet. Zum Beispiel, wenn Sie versehentlich etwas wie versuchen

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

Was wird (siehe die python docs auf Chained Operator Vergleich ),

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

Also mach diesen Fehler nicht!1

Vermeiden der Gruppierung von Klammern
Das Update ist eigentlich ganz 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 Auswertungsreihenfolge festzulegen:

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 andere Möglichkeit, Klammern zu vermeiden, ist die Verwendung von 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ührlichquery und eval in Dynamic Expression Evaluation in pandas mit pd dokumentiert .eval () .

operator.and_
Ermöglicht die Ausführung dieses Vorgangs auf funktionale Weise. Ruft intern Series.__and__ Auf, 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

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 von np.logical_and, Für die ebenfalls keine Klammergruppierung 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 eine ufunc (Universal Functions) , und die meisten ufuncs haben eine reduce -Methode . Dies bedeutet, dass es einfacher ist, mit logical_and Zu verallgemeinern, wenn Sie mehrere Masken zu UND haben. Zum Beispiel müssten Sie AND-Masken m1 Und m2 Und m3 Mit & Ausführen

m1 & m2 & m3

Eine einfachere Option ist jedoch

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

Dies ist sehr effektiv, da Sie auf diese Weise eine komplexere Logik 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 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

Sagen Sie für df, Sie möchten alle Zeilen mit A == 3 oder B == 7 zurückgeben.

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 . Hier gelten alle Einschränkungen.

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

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

Bei einer Maske, wie z

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

Wenn Sie jeden booleschen Wert invertieren müssen (sodass das Endergebnis [False, False, True] Ist), 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

Dieser ruft intern an

mask.__invert__()

0    False
1    False
2     True
dtype: bool

Aber benutze es nicht direkt.

operator.inv
Ruft intern __invert__ Für 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

Beachten Sie, dass np.logical_and Durch np.bitwise_and, logical_or Durch bitwise_or Und logical_not Durch invert ersetzt werden kann.

23
cs95

Logische Operatoren für die Boolesche Indizierung in Pandas

Es ist wichtig zu wissen, dass Sie keinen der Python logischen Operatoren (and, or oder not verwenden können. ) für pandas.Series oder pandas.DataFrames (Sie können sie auch nicht für numpy.arrays mit mehr als einem Element verwenden). Der Grund, warum Sie diese nicht verwenden können, ist, dass sie implizit bool für ihre Operanden aufrufen, was eine Ausnahme auslöst, da diese Datenstrukturen entschieden haben, dass der Boolesche Wert eines Arrays nicht eindeutig 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 den "Wahrheitswert einer Reihe ist mehrdeutig. Verwenden Sie a.empty, a.bool (), a.item (), a.any () oder a.all () "Q + A .

NumPys logische Funktionen

NumPy bietet diesen Operatoren elementweise Entsprechungen als Funktionen, die für numpy.array, pandas.Series, pandas.DataFrame oder jede andere (konforme) numpy.array-Unterklasse verwendet werden können:

Daher sollte man im Wesentlichen Folgendes 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-Reihe oder pandas-Datenrahmen haben, können Sie auch die elementweisen bitweisen Funktionen ( für Boolesche Werte sind sie von den logischen Funktionen nicht zu unterscheiden (oder sollten es zumindest sein):

In der Regel werden die Operatoren verwendet. In Kombination mit Vergleichsoperatoren muss man jedoch daran denken, den Vergleich in Klammern zu setzen, da die bitweisen Operatoren eine höhere Priorität haben als die Vergleichsoperatoren :

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

Dies kann irritierend sein, da die logischen Python -Operatoren eine geringere Priorität haben als die Vergleichsoperatoren, sodass Sie normalerweise a < 10 and b > 10 schreiben (wobei a und b beispielsweise einfache Ganzzahlen sind) und brauche die Klammer nicht.

Unterschiede zwischen logischen und bitweisen Operationen (auf Nicht-Booleschen)

Es ist wirklich wichtig zu betonen, dass Bit- und logische Operationen nur für boolesche NumPy-Arrays (und boolesche Serien und Datenrahmen) äquivalent sind. Wenn diese keine Booleschen Werte enthalten, ergeben die Operationen unterschiedliche Ergebnisse. Ich werde Beispiele mit NumPy-Arrays einfügen, aber die Ergebnisse sind für die Datenstrukturen pandas ähnlich:

>>> 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 in ähnlicher Weise auch Pandas) verschiedene Funktionen für boolesche ( boolesche oder "maskierte" Index-Arrays ) und ganzzahlige ( Index-Arrays ausführt )) indizes die ergebnisse der indizierung werden auch anders ausfallen:

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

Wobei der logische Operator für NumPy-Arrays , pandas-Serien und pandas-Datenrahmen nicht funktioniert. Die anderen arbeiten an diesen Datenstrukturen (und einfachen Python Objekten) und arbeiten elementweise. Seien Sie jedoch vorsichtig beim bitweisen Invertieren von einfachen Python bool, da der Bool in diesem Kontext als Ganzzahl interpretiert wird (z. B. gibt ~False-1 und ~True-2 zurück).

2
MSeifert