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.