pandas: beberapa kondisi saat mengindeks bingkai data - perilaku tak terduga


135

Saya memfilter baris dalam kerangka data dengan nilai dalam dua kolom.

Untuk beberapa alasan operator OR berperilaku seperti yang saya harapkan dari operator AND dan sebaliknya.

Kode tes saya:

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 (|)',])

Dan hasilnya:

      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]

Seperti yang Anda lihat, ANDoperator menjatuhkan setiap baris di mana setidaknya satu nilai sama -1. Di sisi lain, ORoperator mengharuskan kedua nilai sama -1untuk menjatuhkannya. Saya mengharapkan hasil yang justru sebaliknya. Adakah yang bisa menjelaskan perilaku ini?

Saya menggunakan panda 0.13.1.


1
df.querydan pd.evalsepertinya cocok untuk kasus penggunaan ini. Untuk informasi tentang pd.eval()keluarga fungsi, fitur dan kasus penggunaannya, silakan kunjungi Evaluasi Ekspresi Dinamis di pandas menggunakan pd.eval () .
cs95

Jawaban:


211

Seperti yang Anda lihat, operator AND menghapus setiap baris di mana setidaknya satu nilai sama dengan -1. Di sisi lain, operator OR mengharuskan kedua nilai sama dengan -1 untuk menghapusnya.

Betul sekali. Ingatlah bahwa Anda menulis kondisi dalam kaitannya dengan apa yang ingin Anda pertahankan , bukan apa yang ingin Anda hapus. Untuk df1:

df1 = df[(df.a != -1) & (df.b != -1)]

Anda mengatakan "pertahankan baris yang df.abukan -1 dan df.bbukan -1", yang sama dengan menghapus setiap baris yang setidaknya satu nilainya adalah -1.

Untuk df2:

df2 = df[(df.a != -1) | (df.b != -1)]

Anda mengatakan "pertahankan baris yang salah satu df.aatau df.bbukan -1", yang sama dengan menghapus baris di mana kedua nilai adalah -1.

PS: akses berantai seperti df['a'][1] = -1dapat membuat Anda mendapat masalah. Lebih baik membiasakan diri menggunakan .locdan .iloc.


24
DataFrame.query()bekerja dengan baik di sini juga. df.query('a != -1 or b != -1').
Phillip Cloud

5
Kebetulan tahu mengapa panda ingin &dan |lebih anddan or?
kompor

3
@stoves: dalam kode Python normal, anddan ormemiliki semantik Python dasar yang tidak dapat dimodifikasi. &dan |, di sisi lain, memiliki metode khusus terkait yang mengontrol perilaku mereka. (Dalam string kueri, tentu saja, kami bebas menerapkan penguraian apa pun yang kami suka.)
DSM

menariknya, sepertinya df[True & False]gagal tetapi df[(True) & (False)]berhasil (tidak diuji pada contoh ini)
3pitt

Apakah mungkin untuk memecah sintaks semacam ini di beberapa baris? Apa yang paling PEP8?
tommy.carstensen

41

Anda dapat menggunakan query () , yaitu:

df_filtered = df.query('a == 4 & b != 2')

Saya memiliki situasi di mana menurut saya sintaks ini lebih masuk akal misalnya: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10

9

Sedikit teori logika matematika di sini:

"NOT a AND NOT b" sama dengan "NOT (a OR b)" , jadi:

"a NOT -1 AND b NOT -1" setara dengan "NOT (a is -1 OR b is -1)" , yang merupakan kebalikan dari (Complement) dari "(a is -1 OR b is -1)" .

Jadi jika Anda ingin hasil yang berlawanan, df1 dan df2 harus seperti di bawah ini:

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
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.