Auswahl mit komplexen Kriterien aus pandas.DataFrame

234

Zum Beispiel habe ich einfache DF:

import pandas as pd
from random import randint

df = pd.DataFrame({'A': [randint(1, 9) for x in xrange(10)],
                   'B': [randint(1, 9)*10 for x in xrange(10)],
                   'C': [randint(1, 9)*100 for x in xrange(10)]})

Kann ich Werte aus 'A' auswählen, für die die entsprechenden Werte für 'B' größer als 50 sind, und für 'C' - ungleich 900 - unter Verwendung der Methoden und Redewendungen von Pandas?

Gill Bates
quelle
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
Könnte auch @Geckos Antwort überprüfen in: stackoverflow.com/questions/13611065/…
Nicholas Humphrey

Antworten:

390

Sicher! Konfiguration:

>>> import pandas as pd
>>> from random import randint
>>> df = pd.DataFrame({'A': [randint(1, 9) for x in range(10)],
                   'B': [randint(1, 9)*10 for x in range(10)],
                   'C': [randint(1, 9)*100 for x in range(10)]})
>>> df
   A   B    C
0  9  40  300
1  9  70  700
2  5  70  900
3  8  80  900
4  7  50  200
5  9  30  900
6  2  80  700
7  2  80  400
8  5  80  300
9  7  70  800

Wir können Spaltenoperationen anwenden und boolesche Serienobjekte abrufen:

>>> df["B"] > 50
0    False
1     True
2     True
3     True
4    False
5    False
6     True
7     True
8     True
9     True
Name: B
>>> (df["B"] > 50) & (df["C"] == 900)
0    False
1    False
2     True
3     True
4    False
5    False
6    False
7    False
8    False
9    False

[Update, um zum neuen Stil zu wechseln .loc]:

Und dann können wir diese verwenden, um in das Objekt zu indizieren. Für den Lesezugriff können Sie Indizes verketten:

>>> df["A"][(df["B"] > 50) & (df["C"] == 900)]
2    5
3    8
Name: A, dtype: int64

Sie können jedoch aufgrund des Unterschieds zwischen einer Ansicht und einer Kopie, die dies für den Schreibzugriff tun, in Schwierigkeiten geraten. Sie können .locstattdessen verwenden:

>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"]
2    5
3    8
Name: A, dtype: int64
>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"].values
array([5, 8], dtype=int64)
>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"] *= 1000
>>> df
      A   B    C
0     9  40  300
1     9  70  700
2  5000  70  900
3  8000  80  900
4     7  50  200
5     9  30  900
6     2  80  700
7     2  80  400
8     5  80  300
9     7  70  800

Beachten Sie, dass ich versehentlich getippt habe == 900und nicht != 900oder ~(df["C"] == 900), aber ich bin zu faul, um das Problem zu beheben. Übung für den Leser. : ^)

DSM
quelle
5
Über das .locUpdate - es wäre gut, wenn Sie klären würden, wo wir eine Kopie bekommen und wo eine Ansicht.
Gill Bates
3
Ist es möglich, einen Pandas-Datenrahmen zu filtern und den Operator OR zu verwenden? Wenn es zum Beispiel einen Spaltenmonat gäbe, könnten Sie df = data ['month' == JAN OR 'month' == FEB] sagen? Und vielleicht eine zweite Spalte einfügen, die die Abfrage komplexer macht, newdf wobei col_month = jan ODER feb AND col_day = MONDAY oder WENDNESDAY
yoshiserry
7
@yoshiserry: bitte stellen Sie das als separate Frage. Niemand wird es hier in den Kommentaren zu einer alten Antwort sehen.
DSM
2
Vergessen Sie nicht die Klammern - Sie werden seltsame Fehler wie{TypeError}cannot compare a dtyped [int64] array with a scalar of type [bool]
Mr_and_Mrs_D
Führt diese Verwendung von Klammern nicht zu Berechnungen über die gesamte Reihe? Was ist, wenn wir aus Effizienzgründen wiederholt Teilmengen erstellen möchten?
ifly6
56

Eine andere Lösung besteht darin, die Abfragemethode zu verwenden:

import pandas as pd

from random import randint
df = pd.DataFrame({'A': [randint(1, 9) for x in xrange(10)],
                   'B': [randint(1, 9) * 10 for x in xrange(10)],
                   'C': [randint(1, 9) * 100 for x in xrange(10)]})
print df

   A   B    C
0  7  20  300
1  7  80  700
2  4  90  100
3  4  30  900
4  7  80  200
5  7  60  800
6  3  80  900
7  9  40  100
8  6  40  100
9  3  10  600

print df.query('B > 50 and C != 900')

   A   B    C
1  7  80  700
2  4  90  100
4  7  80  200
5  7  60  800

Wenn Sie nun die zurückgegebenen Werte in Spalte A ändern möchten, können Sie deren Index speichern:

my_query_index = df.query('B > 50 & C != 900').index

.... und verwenden .iloc, um sie zu ändern, dh:

df.iloc[my_query_index, 0] = 5000

print df

      A   B    C
0     7  20  300
1  5000  80  700
2  5000  90  100
3     4  30  900
4  5000  80  200
5  5000  60  800
6     3  80  900
7     9  40  100
8     6  40  100
9     3  10  600
Nikos Tavoularis
quelle
12

Und denken Sie daran, Klammern zu verwenden!

Beachten Sie, dass der &Operator Vorrang vor Operatoren wie >oder <usw. hat. Deshalb

4 < 5 & 6 > 4

bewertet zu False. Wenn Sie also verwenden pd.loc, müssen Sie Ihre logischen Anweisungen in Klammern setzen, da sonst eine Fehlermeldung angezeigt wird. Deshalb tun Sie:

df.loc[(df['A'] > 10) & (df['B'] < 15)]

anstatt

df.loc[df['A'] > 10 & df['B'] < 15]

was dazu führen würde

TypeError: Ein dtyped [float64] -Array kann nicht mit einem Skalar vom Typ [bool] verglichen werden.

Tomasz Bartkowiak
quelle
3

Sie können Pandas verwenden, für deren Vergleich einige Funktionen integriert sind. Wenn Sie also Werte von "A" auswählen möchten, die von den Bedingungen von "B" und "C" erfüllt werden (vorausgesetzt, Sie möchten ein DataFrame-Pandas-Objekt zurückgeben).

df[['A']][df.B.gt(50) & df.C.ne(900)]

df[['A']] gibt Ihnen Spalte A im DataFrame-Format zurück.

Die Funktion 'gt' von pandas gibt die Positionen von Spalte B zurück, die größer als 50 sind, und 'ne' gibt die Positionen ungleich 900 zurück.

Christopher Matthews
quelle