Memilih dengan kriteria kompleks dari pandas.DataFrame

235

Misalnya saya punya DF sederhana:

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)]})

Dapatkah saya memilih nilai dari 'A' yang mana nilai yang sesuai untuk 'B' akan lebih besar dari 50, dan untuk 'C' - tidak sama dengan 900, menggunakan metode dan idiom Pandas?

Gill Bates
sumber
df.querydan pd.evalsepertinya cocok untuk kasus penggunaan ini. Untuk informasi tentang rangkaian pd.eval()fungsi, fitur dan kasingnya , silakan kunjungi Evaluasi Ekspresi Dinamis di panda menggunakan pd.eval () .
cs95
Mungkin juga memeriksa jawaban @Gecko di: stackoverflow.com/questions/13611065/…
Nicholas Humphrey

Jawaban:

391

Tentu! Mempersiapkan:

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

Kami dapat menerapkan operasi kolom dan mendapatkan objek Seri 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

[Perbarui, untuk beralih ke gaya baru .loc]:

Dan kemudian kita bisa menggunakan ini untuk mengindeks ke objek. Untuk akses baca, Anda dapat membuat rantai indeks:

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

tetapi Anda bisa mendapatkan masalah karena perbedaan antara tampilan dan salinan yang melakukan ini untuk akses tulis. Anda bisa menggunakan .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

Perhatikan bahwa saya tidak sengaja mengetik == 900dan tidak != 900, atau ~(df["C"] == 900), tapi saya terlalu malas untuk memperbaikinya. Latihan untuk pembaca. : ^)

DSM
sumber
5
Tentang .locpembaruan - akan lebih baik jika Anda mengklarifikasi dari mana kami mendapatkan salinan dan di mana pandangan.
Gill Bates
3
apakah mungkin untuk menyaring kerangka data panda dan menggunakan operator ATAU. Misalnya jika ada bulan kolom, dapatkah Anda mengatakan df = data ['bulan' == JAN ATAU 'bulan' == FEB]? Dan mungkin termasuk kolom kedua yang membuat kueri menjadi lebih kompleks, newdf di mana col_month = jan ATAU feb DAN col_day = SENIN atau WENDNESDAY
yoshiserry
7
@yoshiserry: tolong tanyakan itu sebagai pertanyaan terpisah. Tidak ada yang akan melihatnya di sini di komentar pada jawaban lama.
DSM
2
Jangan lupa tanda kurung - Anda akan mendapatkan kesalahan aneh seperti{TypeError}cannot compare a dtyped [int64] array with a scalar of type [bool]
Mr_and_Mrs_D
Bukankah penggunaan tanda kurung ini mengarah pada perhitungan seluruh seri? Bagaimana jika kita ingin subset berulang kali untuk efisiensi?
ifly6
56

Solusi lain adalah dengan menggunakan metode kueri :

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

Sekarang jika Anda ingin mengubah nilai yang dikembalikan di kolom A Anda dapat menyimpan indeksnya:

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

.... dan gunakan .ilocuntuk mengubahnya yaitu:

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

Dan ingatlah untuk menggunakan tanda kurung!

Perlu diingat bahwa &operator lebih diutamakan daripada operator seperti >atau <dll. Itulah sebabnya

4 < 5 & 6 > 4

mengevaluasi ke False. Oleh karena itu jika Anda menggunakan pd.loc, Anda harus menempatkan tanda kurung di sekitar pernyataan logis Anda, jika tidak Anda akan mendapatkan kesalahan. Itu sebabnya:

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

dari pada

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

yang akan menghasilkan

TypeError: tidak dapat membandingkan array dtyped [float64] dengan skalar tipe [bool]

Tomasz Bartkowiak
sumber
3

Anda dapat menggunakan panda yang memiliki beberapa fungsi bawaan untuk perbandingan. Jadi jika Anda ingin memilih nilai "A" yang dipenuhi oleh kondisi "B" dan "C" (dengan asumsi Anda ingin mengembalikan objek panda DataFrame)

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

df[['A']] akan memberi Anda kembali kolom A dalam format DataFrame.

Fungsi panda 'gt' akan mengembalikan posisi kolom B yang lebih besar dari 50 dan 'ne' akan mengembalikan posisi yang tidak sama dengan 900.

Christopher Matthews
sumber