Saya memiliki skenario di mana pengguna ingin menerapkan beberapa filter ke objek DataFrame atau Seri Pandas. Pada dasarnya, saya ingin rantai secara efisien sekelompok penyaringan (operasi perbandingan) bersama-sama yang ditentukan pada saat dijalankan oleh pengguna.
Filter harus aditif (alias masing-masing yang diterapkan harus mempersempit hasil).
Saat ini saya menggunakan reindex()
tetapi ini menciptakan objek baru setiap kali dan menyalin data yang mendasarinya (jika saya memahami dokumentasi dengan benar). Jadi, ini bisa sangat tidak efisien saat memfilter Seri besar atau DataFrame.
Aku berpikir bahwa menggunakan apply()
, map()
atau sesuatu yang serupa mungkin lebih baik. Aku cukup baru untuk panda meskipun masih mencoba untuk membungkus kepalaku di sekitar segalanya.
TL; DR
Saya ingin mengambil kamus dari formulir berikut dan menerapkan setiap operasi ke objek Seri yang diberikan dan mengembalikan objek Seri yang 'difilter'.
relops = {'>=': [1], '<=': [1]}
Contoh Panjang
Saya akan mulai dengan contoh dari apa yang saya miliki saat ini dan hanya memfilter objek Seri tunggal. Di bawah ini adalah fungsi yang saya gunakan saat ini:
def apply_relops(series, relops):
"""
Pass dictionary of relational operators to perform on given series object
"""
for op, vals in relops.iteritems():
op_func = ops[op]
for val in vals:
filtered = op_func(series, val)
series = series.reindex(series[filtered])
return series
Pengguna menyediakan kamus dengan operasi yang ingin mereka lakukan:
>>> df = pandas.DataFrame({'col1': [0, 1, 2], 'col2': [10, 11, 12]})
>>> print df
>>> print df
col1 col2
0 0 10
1 1 11
2 2 12
>>> from operator import le, ge
>>> ops ={'>=': ge, '<=': le}
>>> apply_relops(df['col1'], {'>=': [1]})
col1
1 1
2 2
Name: col1
>>> apply_relops(df['col1'], relops = {'>=': [1], '<=': [1]})
col1
1 1
Name: col1
Sekali lagi, 'masalah' dengan pendekatan saya di atas adalah bahwa saya pikir ada banyak kemungkinan penyalinan data yang tidak perlu untuk langkah-langkah di antaranya.
Juga, saya ingin memperluas ini sehingga kamus yang disahkan dapat menyertakan kolom untuk operator dan memfilter seluruh DataFrame berdasarkan pada kamus input. Namun, saya mengasumsikan apa pun yang berfungsi untuk Seri dapat dengan mudah diperluas ke DataFrame.
df.query
danpd.eval
sepertinya cocok untuk kasus penggunaan Anda. Untuk informasi tentang rangkaianpd.eval()
fungsi, fitur dan kasingnya , silakan kunjungi Evaluasi Ekspresi Dinamis di panda menggunakan pd.eval () .Jawaban:
Panda (dan numpy) memungkinkan pengindeksan boolean , yang akan jauh lebih efisien:
Jika Anda ingin menulis fungsi pembantu untuk ini, pertimbangkan sesuatu di sepanjang baris ini:
Pembaruan: panda 0.13 memiliki metode kueri untuk jenis kasus penggunaan ini, dengan asumsi nama kolom adalah pengidentifikasi yang valid karya-karya berikut (dan dapat lebih efisien untuk bingkai besar karena menggunakan numexpr di belakang layar):
sumber
df[(ge(df['col1'], 1) & le(df['col1'], 1)]
. Masalah bagi saya sebenarnya adalah kamus dengan filter bisa berisi banyak operator dan merantai mereka bersama-sama rumit. Mungkin saya bisa menambahkan setiap array boolean perantara ke array besar dan kemudian gunakan sajamap
untuk menerapkanand
operator kepada mereka?f()
harus mengambil*b
alih-alih adilb
? Apakah ini agar penggunaf()
masih dapat menggunakanout
parameter opsionallogical_and()
? Ini mengarah ke pertanyaan sampingan kecil lainnya. Apa manfaat / trade off dari passing dalam array melaluiout()
vs. menggunakan yang dikembalikan darilogical_and()
? Terima kasih lagi!*b
diperlukan karena Anda melewati dua arrayb1
danb2
dan Anda perlu membongkar mereka saat meneleponlogical_and
. Namun, pertanyaan lainnya masih ada. Apakah ada manfaat kinerja untuk melewatkan dalam array melaluiout
parameter kelogical_and()
vs hanya menggunakan nilai baliknya?Kondisi rantai menciptakan garis panjang, yang tidak disarankan oleh pep8. Menggunakan metode .query memaksa untuk menggunakan string, yang kuat tetapi unpythonic dan tidak terlalu dinamis.
Setelah masing-masing filter berada di tempatnya, satu pendekatan adalah
np.logical beroperasi dan cepat, tetapi tidak membutuhkan lebih dari dua argumen, yang ditangani oleh functools.reduce.
Perhatikan bahwa ini masih memiliki beberapa redudansi: a) pintasan tidak terjadi pada tingkat global b) Masing-masing kondisi individu berjalan pada seluruh data awal. Namun, saya berharap ini cukup efisien untuk banyak aplikasi dan sangat mudah dibaca.
Anda juga dapat membuat disjungsi (di mana hanya satu kondisi yang perlu benar) dengan menggunakan
np.logical_or
sebagai gantinya:sumber
c_1
,c_2
,c_3
, ...c_n
dalam daftar, dan kemudian melewatidata[conjunction(conditions_list)]
tapi mendapatkan errorValueError: Item wrong length 5 instead of 37.
Juga mencobadata[conjunction(*conditions_list)]
tetapi saya mendapatkan hasil yang berbeda daridata[conjunction(c_1, c_2, c_3, ... c_n )]
, tidak yakin apa yang sedang terjadi.data[conjunction(*conditions_list)]
tidak berfungsi setelah mengemas dataframe ke dalam daftar, dan membongkar daftar di tempatdf[f_2 & f_3 & f_4 & f_5 ]
denganf_2 = df["a"] >= 0
dll. Tidak perlu untuk fungsi itu ... (penggunaan fungsi fungsi tingkat tinggi yang bagus ...)Solusi termudah:
Menggunakan:
Contoh lain , Untuk memfilter bingkai data untuk nilai-nilai milik Feb-2018, gunakan kode di bawah ini
sumber
Sejak panda pembaruan 0,22 , opsi perbandingan tersedia seperti:
dan masih banyak lagi. Fungsi-fungsi ini mengembalikan array boolean. Mari kita lihat bagaimana kita dapat menggunakannya:
sumber
Kenapa tidak melakukan ini?
Demo:
Hasil:
Anda dapat melihat bahwa kolom 'a' telah difilter di mana a> = 2.
Ini sedikit lebih cepat (waktu mengetik, bukan kinerja) daripada rantai operator. Anda tentu saja dapat menempatkan impor di bagian atas file.
sumber
e juga dapat memilih baris berdasarkan nilai-nilai kolom yang tidak ada dalam daftar atau iterable. Kami akan membuat variabel boolean seperti sebelumnya, tetapi sekarang kami akan meniadakan variabel boolean dengan menempatkan ~ di depan.
Sebagai contoh
sumber