Pandas: Mehrere Bedingungen beim Indizieren des Datenrahmens - unerwartetes Verhalten


133

Ich filtere Zeilen in einem Datenrahmen nach Werten in zwei Spalten.

Aus irgendeinem Grund verhält sich der OR-Operator so, wie ich es von einem AND-Operator erwarten würde und umgekehrt.

Mein Testcode:

import pandas as pd

df = pd.DataFrame({'a': range(5), 'b': range(5) })

# let's insert some -1 values
df['a'][1] = -1
df['b'][1] = -1
df['a'][3] = -1
df['b'][4] = -1

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a != -1) | (df.b != -1)]

print pd.concat([df, df1, df2], axis=1,
                keys = [ 'original df', 'using AND (&)', 'using OR (|)',])

Und das Ergebnis:

      original df      using AND (&)      using OR (|)    
             a  b              a   b             a   b
0            0  0              0   0             0   0
1           -1 -1            NaN NaN           NaN NaN
2            2  2              2   2             2   2
3           -1  3            NaN NaN            -1   3
4            4 -1            NaN NaN             4  -1

[5 rows x 6 columns]

Wie Sie sehen können, löscht der ANDOperator jede Zeile, in der mindestens ein Wert gleich ist -1. Andererseits ORverlangt der Bediener, dass beide Werte gleich sind -1, um sie fallen zu lassen. Ich würde genau das Gegenteil erwarten. Könnte jemand dieses Verhalten bitte erklären?

Ich benutze Pandas 0.13.1.


1
df.queryund pd.evalscheinen gut für diesen Anwendungsfall zu passen. Informationen zur pd.eval()Funktionsfamilie, ihren Funktionen und Anwendungsfällen finden Sie unter Auswertung dynamischer Ausdrücke in Pandas mit pd.eval () .
— CS95

Antworten:


209

Wie Sie sehen können, löscht der AND-Operator jede Zeile, in der mindestens ein Wert gleich -1 ist. Andererseits erfordert der OR-Operator, dass beide Werte gleich -1 sind, um sie zu löschen.

Das stimmt. Denken Sie daran, dass Sie die Bedingung in Bezug auf das schreiben, was Sie behalten möchten , und nicht in Bezug auf das, was Sie löschen möchten. Für df1:

df1 = df[(df.a != -1) & (df.b != -1)]

Du sagst "behalte die Zeilen, in denen df.anicht -1 und istdf.b nicht -1 ist", was dem Löschen jeder Zeile entspricht, in der mindestens ein Wert -1 ist.

Für df2:

df2 = df[(df.a != -1) | (df.b != -1)]

Sie sagen "Behalte die Zeilen, in denen entweder -1 ist df.aoder df.bnicht -1", was dem Löschen von Zeilen entspricht, in denen beide Werte -1 sind.

PS: Verketteter Zugriff wie df['a'][1] = -1kann Sie in Schwierigkeiten bringen. Es ist besser, sich daran zu gewöhnen, .locund zu verwenden .iloc.


24
DataFrame.query()funktioniert auch hier gut. df.query('a != -1 or b != -1').
— Phillip Cloud

4
Zufällig wissen, warum Pandas wollen &und |über andund or?
— Öfen

2
@stoves: im normalen Python-Code andund ormit grundlegender Python-Semantik, die nicht geändert werden kann. &und |andererseits entsprechende spezielle Methoden haben, die ihr Verhalten steuern. (In Abfragezeichenfolgen können wir natürlich jede Analyse durchführen, die wir möchten.)
— DSM

Interessanterweise scheint es zu df[True & False]scheitern, aber df[(True) & (False)]es ist erfolgreich (in diesem Beispiel nicht getestet)
— 3pitt

Wäre es möglich, diese Art von Syntax über mehrere Zeilen hinweg zu unterbrechen? Was wäre das meiste PEP8?
— Tommy.carstensen

41

Sie können query () verwenden , dh:

df_filtered = df.query('a == 4 & b != 2')

Ich habe eine Situation, in der ich denke, dass diese Syntax sinnvoller ist, zB: df.query ('' (a == 4 & b! = 2) | c == 3 ")
— Aus_10

9

Eine kleine mathematische Logiktheorie hier:

"NICHT a UND NICHT b" ist dasselbe wie "NICHT (a ODER b)" , also:

"a NICHT -1 UND b NICHT -1" ist äquivalent zu "NICHT (a ist -1 ODER b ist -1)" , was entgegengesetzt (Komplement) zu "(a ist -1 ODER b ist -1)" ist .

Wenn Sie also genau das Gegenteil wünschen, sollten df1 und df2 wie folgt aussehen:

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.