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?


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. : ^)


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

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]


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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.