Wybieranie za pomocą złożonych kryteriów z pandas.DataFrame

234

Na przykład mam prosty 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)]})

Czy mogę wybrać wartości z „A”, dla których odpowiadające wartości dla „B” będą większe niż 50, a dla „C” - nie równe 900, stosując metody i idiomy Pand?

Gill Bates
źródło
df.queryi pd.evalwydają się pasować do tego przypadku użycia. Aby uzyskać informacje na temat pd.eval()rodziny funkcji, ich funkcji i przypadków użycia, odwiedź stronę Dynamic Expression Evaluation w pandach za pomocą pd.eval () .
cs95,
Mogę również sprawdzić odpowiedź @Gecko w: stackoverflow.com/questions/13611065/…
Nicholas Humphrey

Odpowiedzi:

390

Pewnie! Ustawiać:

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

Możemy zastosować operacje na kolumnach i uzyskać obiekty z serii boolean:

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

[Aktualizacja, aby przejść do nowego stylu .loc]:

Następnie możemy użyć ich do zindeksowania obiektu. Aby uzyskać dostęp do odczytu, możesz połączyć łańcuchy indeksów:

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

ale możesz wpaść w kłopoty z powodu różnicy między widokiem a kopią, która robi to w celu dostępu do zapisu. Zamiast tego możesz użyć .loc:

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

Pamiętaj, że przypadkowo wpisałem == 900i nie != 900, lub ~(df["C"] == 900), ale jestem zbyt leniwy, aby to naprawić. Ćwiczenie dla czytelnika. : ^)

DSM
źródło
5
O .locaktualizacji - dobrze by było, gdybyś wyjaśnił, skąd mamy kopię i gdzie widok.
Gill Bates,
3
czy można filtrować ramkę danych pandy i użyć operatora OR. Na przykład, jeśli byłby miesiąc kolumny, czy możesz powiedzieć df = data [„miesiąc” == JAN LUB „miesiąc” == FEB]? I może zawierać drugą kolumnę, która sprawia, że ​​zapytanie jest bardziej złożone, newdf gdzie col_month = jan LUB feb AND col_day = PONIEDZIAŁEK lub WENDNESDAY
yoshiserry
7
@yoshiserry: zadaj to jako osobne pytanie. Nikt nie zobaczy go tutaj w komentarzach do starej odpowiedzi.
DSM
2
Nie zapomnij o nawiasach - otrzymasz dziwne błędy, takie jak{TypeError}cannot compare a dtyped [int64] array with a scalar of type [bool]
Mr_and_Mrs_D
Czy to użycie nawiasów nie prowadzi do obliczeń dla całej serii? Co zrobić, jeśli chcemy wielokrotnie składać podziały ze względu na wydajność?
ifly6
56

Innym rozwiązaniem jest użycie metody zapytania :

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

Teraz, jeśli chcesz zmienić zwracane wartości w kolumnie A, możesz zapisać ich indeks:

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

.... i użyj, .ilocaby je zmienić, tj .:

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
źródło
12

I pamiętaj, aby używać nawiasów!

Należy pamiętać, że &operator bierze pierwszeństwo nad operatorami takimi jak >lub <itd. Dlatego

4 < 5 & 6 > 4

ocenia na False. Dlatego jeśli używasz pd.loc, musisz umieścić nawiasy wokół instrukcji logicznych, w przeciwnym razie pojawi się błąd. Właśnie dlatego:

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

zamiast

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

co spowodowałoby

TypeError: nie można porównać tablicy dtyped [float64] ze skalarem typu [bool]

Tomasz Bartkowiak
źródło
3

Możesz użyć pand, ma kilka wbudowanych funkcji do porównania. Więc jeśli chcesz wybrać wartości „A”, które są spełnione przez warunki „B” i „C” (zakładając, że chcesz odzyskać obiekt pand DataFrame)

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

df[['A']] zwróci Ci kolumnę A w formacie DataFrame.

funkcja „gt” pandy zwróci pozycje kolumny B większe niż 50, a „ne” zwróci pozycje nie równe 900.

Christopher Matthews
źródło