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.

Wojciech Walczak
sumber
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.

DSM
sumber
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')
CONvid19
sumber
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)]
Jake
sumber