numpy.amax () akan menemukan nilai maksimal dalam sebuah array, dan numpy.amin () melakukan hal yang sama untuk nilai min. Jika saya ingin menemukan max dan min, saya harus memanggil kedua fungsi tersebut, yang mengharuskan melewati dua kali array (sangat besar), yang tampaknya lambat.
Apakah ada fungsi dalam numpy API yang menemukan max dan min hanya dengan satu kali pass melalui data?
amax
danamin
minmax
ke pustaka yang dipermasalahkan ( github.com/numpy/numpy/issues/9836 ).Jawaban:
Tidak. Pada saat tulisan ini dibuat, belum ada fungsi seperti itu. (Dan ya, jika ada yang fungsi seperti, kinerjanya akan secara signifikan lebih baik daripada menelepon
numpy.amin()
dannumpy.amax()
berturut-turut pada array besar.)sumber
Saya tidak berpikir bahwa melewati array dua kali adalah masalah.Pertimbangkan pseudo-code berikut:Meskipun hanya ada 1 loop di sini, masih ada 2 pemeriksaan. (Alih-alih memiliki 2 loop dengan masing-masing 1 centang). Sungguh satu-satunya hal yang Anda simpan adalah overhead 1 loop. Jika array benar-benar besar seperti yang Anda katakan, overhead itu kecil dibandingkan dengan beban kerja loop yang sebenarnya. (Perhatikan bahwa ini semua diimplementasikan di C, jadi loop lebih atau kurang gratis).
EDIT Maaf untuk 4 dari Anda yang memberikan suara positif dan percaya pada saya. Anda pasti bisa mengoptimalkan ini.
Berikut beberapa kode fortran yang dapat dikompilasi menjadi modul python melalui
f2py
(mungkin seorangCython
guru dapat datang dan membandingkannya dengan versi C yang dioptimalkan ...):Kompilasi melalui:
Dan sekarang kami berada di tempat di mana kami dapat mengujinya:
Hasilnya agak mengejutkan bagi saya:
Saya harus mengatakan, saya tidak sepenuhnya memahaminya. Membandingkan hanya
np.min
versusminmax1
danminmax2
masih merupakan pertarungan yang kalah, jadi ini bukan hanya masalah memori ...catatan - Meningkatkan ukuran dengan faktor
10**a
dan mengurangi pengulangan dengan faktor10**a
(menjaga ukuran masalah konstan) memang mengubah kinerja, tetapi tidak dengan cara yang tampaknya konsisten yang menunjukkan bahwa ada beberapa interaksi antara kinerja memori dan overhead panggilan fungsi di python. Bahkan membandingkanmin
implementasi sederhana di fortran beats numpy's dengan faktor sekitar 2 ...sumber
i < minval
benar, makai > maxval
selalu salah, jadi Anda hanya perlu melakukan 1,5 pemeriksaan rata-rata per iterasi ketika detikif
diganti denganelif
.f2py
hanya membungkus Fortran dengan kode tangan sehingga dapat dipanggil oleh Python. Tes yang "lebih adil" mungkin adalah C coding tangan dan kemudian menggunakanf2py
(!) Untuk membungkusnya untuk Python. Jika Anda mengizinkan C ++, maka Shed Skin mungkin merupakan tempat yang tepat untuk menyeimbangkan kemudahan pengkodean dengan kinerja.Ada fungsi untuk mencari (max-min) yang disebut numpy.ptp jika itu berguna untuk Anda:
tapi saya rasa tidak ada cara untuk menemukan min dan max dengan satu traversal.
EDIT: ptp hanya memanggil min dan max di bawah tenda
sumber
Anda dapat menggunakan Numba , yang merupakan kompiler Python dinamis yang sadar NumPy menggunakan LLVM. Implementasi yang dihasilkan cukup sederhana dan jelas:
Ini juga harus lebih cepat daripada
min() & max()
implementasi Numpy . Dan semuanya tanpa harus menulis satu baris kode C / Fortran.Lakukan tes kinerja Anda sendiri, karena itu selalu bergantung pada arsitektur Anda, data Anda, versi paket Anda ...
sumber
numba
fungsi sekali sebelum benchmark untuk memastikan itu dikompilasi JIT ?. Juga, jika Anda menggunakanipython
, untuk kesederhanaan, saya akan menyarankan Anda menggunakan%timeit whatever_code()
untuk mengukur eksekusi waktu.elif
memungkinkan minimum Anda menjadi lebih besar dari maks. Misalnya, dengan larik dengan panjang 1, nilai maks adalah berapa pun nilainya, sedangkan min adalah + tak terhingga. Bukan masalah besar untuk satu kali saja, tapi bukan kode yang baik untuk dimasukkan jauh ke dalam perut monster produksi.Secara umum, Anda dapat mengurangi jumlah perbandingan untuk algoritme minmax dengan memproses dua elemen sekaligus dan hanya membandingkan yang lebih kecil ke minimum sementara dan yang lebih besar dengan maksimum sementara. Rata-rata seseorang hanya membutuhkan 3/4 dari perbandingan daripada pendekatan yang naif.
Ini dapat diimplementasikan dalam c atau fortran (atau bahasa tingkat rendah lainnya) dan hampir tidak terkalahkan dalam hal kinerja. saya menggunakannumba untuk menggambarkan prinsip dan mendapatkan implementasi yang sangat cepat, tipe-independen:
Ini jelas lebih cepat daripada pendekatan naif yang disajikan Peque :
Seperti yang diharapkan, implementasi minmax baru hanya membutuhkan sekitar 3/4 dari waktu implementasi naif (
2.1 / 2.75 = 0.7636363636363637
)sumber
Hanya untuk mendapatkan beberapa ide tentang angka yang diharapkan, dengan pendekatan berikut:
(
extrema_loop_*()
pendekatannya mirip dengan yang diusulkan di sini , sedangkanextrema_while_*()
pendekatan didasarkan pada kode dari sini )Pengaturan waktu berikut:
menunjukkan bahwa
extrema_while_*()
yang tercepat, denganextrema_while_nb()
yang tercepat. Bagaimanapun, solusiextrema_loop_nb()
danextrema_loop_cy()
juga mengungguli pendekatan NumPy saja (menggunakannp.max()
dannp.min()
secara terpisah).Terakhir, perhatikan bahwa tidak ada yang sefleksibel
np.min()
/np.max()
(dalam hal dukungan n-dim,axis
parameter, dll.).(kode lengkap tersedia di sini )
sumber
extrema_while_nb
Tidak ada yang menyebutkan numpy.percentile , jadi saya pikir saya akan melakukannya. Jika Anda meminta
[0, 100]
persentil, itu akan memberi Anda larik dua elemen, min (persentil ke-0) dan maks (persentil ke-100).Namun, itu tidak memenuhi tujuan OP: itu tidak lebih cepat dari min dan max secara terpisah. Itu mungkin karena beberapa mesin yang memungkinkan persentil non-ekstrim (masalah yang lebih sulit, yang seharusnya membutuhkan waktu lebih lama).
Versi Numpy yang akan datang dapat dimasukkan ke dalam kasus khusus untuk melewati penghitungan persentil normal jika hanya
[0, 100]
diminta. Tanpa menambahkan apa pun ke antarmuka, ada cara untuk meminta Numpy min dan max dalam satu panggilan (bertentangan dengan apa yang dikatakan dalam jawaban yang diterima), tetapi implementasi standar pustaka tidak memanfaatkan kasus ini untuk membuatnya bermanfaat.sumber
Ini adalah utas lama, tapi bagaimanapun, jika ada yang melihat ini lagi ...
Saat mencari min dan max secara bersamaan, adalah mungkin untuk mengurangi jumlah perbandingan. Jika float yang Anda bandingkan (yang menurut saya memang demikian), ini mungkin menghemat waktu Anda, meskipun bukan kompleksitas komputasi.
Alih-alih (kode Python):
Anda dapat membandingkan dua nilai yang berdekatan dalam larik terlebih dahulu, lalu hanya membandingkan nilai yang lebih kecil dengan nilai minimum saat ini, dan nilai yang lebih besar dengan nilai maksimum saat ini:
Kode di sini ditulis dengan Python, jelas untuk kecepatan Anda akan menggunakan C atau Fortran atau Cython, tetapi dengan cara ini Anda melakukan 3 perbandingan per iterasi, dengan iterasi len (ar) / 2, memberikan perbandingan 3/2 * len (ar). Berbeda dengan itu, melakukan perbandingan "dengan cara yang jelas" Anda melakukan dua perbandingan per iterasi, yang mengarah ke perbandingan 2 * len (ar). Menghemat 25% waktu perbandingan.
Mungkin seseorang suatu hari akan menganggap ini berguna.
sumber
np.bincount
, lihat di sini . Itu tidak menggunakan trik yang Anda tunjukkan, karena ternyata hingga 2x lebih lambat dari pendekatan naif. Ada tautan dari PR ke beberapa tolok ukur komprehensif dari kedua metode tersebut.Pada pandangan pertama, tampaknya untuk melakukan trik:
numpy.histogram
... tetapi jika Anda melihat sumber untuk fungsi itu, itu hanya memanggil
a.min()
dana.max()
secara independen, dan karena itu gagal untuk menghindari masalah kinerja yang dibahas dalam pertanyaan ini. :-(Demikian pula,
scipy.ndimage.measurements.extrema
tampak seperti kemungkinan, tetapi itu juga hanya panggilana.min()
dana.max()
mandiri.sumber
np.histogram
tidak selalu berfungsi untuk ini karena nilai yang dikembalikan(amin, amax)
adalah untuk nilai minimum dan maksimum nampan. Jika saya memiliki, misalnyaa = np.zeros(10)
,np.histogram(a, bins=1)
pengembalian(array([10]), array([-0.5, 0.5]))
. Pengguna mencari(amin, amax)
= (0, 0) dalam kasus itu.Itu sepadan dengan usaha saya, jadi saya akan mengusulkan solusi paling sulit dan paling tidak elegan di sini untuk siapa pun yang mungkin tertarik. Solusi saya adalah mengimplementasikan multi-threaded min-max dalam algoritma one pass di C ++, dan menggunakannya untuk membuat modul ekstensi Python. Upaya ini membutuhkan sedikit overhead untuk mempelajari cara menggunakan Python dan NumPy C / C ++ API, dan di sini saya akan menunjukkan kode dan memberikan beberapa penjelasan dan referensi kecil untuk siapa pun yang ingin menempuh jalur ini.
Min / Maks multi-utas
Tidak ada yang terlalu menarik di sini. Array dipecah menjadi potongan-potongan ukuran
length / workers
. Min / max dihitung untuk setiap potongan di afuture
, yang kemudian dipindai untuk min / max global.Modul Ekstensi Python
Di sinilah segalanya mulai menjadi jelek ... Salah satu cara untuk menggunakan kode C ++ dengan Python adalah dengan menerapkan modul ekstensi. Modul ini dapat dibangun dan dipasang menggunakan
distutils.core
modul standar. Penjelasan lengkap tentang apa yang diperlukan tercakup dalam dokumentasi Python: https://docs.python.org/3/extending/extending.html . CATATAN: tentunya ada cara lain untuk mendapatkan hasil yang serupa, dengan mengutip https://docs.python.org/3/extending/index.html#extending-index :Pada dasarnya, rute ini mungkin lebih bersifat akademis daripada praktis. Dengan itu, apa yang saya lakukan selanjutnya adalah, menempel cukup dekat dengan tutorial, membuat file modul. Ini pada dasarnya adalah boilerplate untuk distutils untuk mengetahui apa yang harus dilakukan dengan kode Anda dan membuat modul Python darinya. Sebelum melakukan semua ini, mungkin bijaksana untuk membuat lingkungan virtual Python sehingga Anda tidak mencemari paket sistem Anda (lihat https://docs.python.org/3/library/venv.html#module-venv ).
Ini file modulnya:
Dalam file ini terdapat penggunaan signifikan dari Python serta NumPy API, untuk informasi lebih lanjut lihat: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple , dan untuk NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Memasang Modul
Hal berikutnya yang harus dilakukan adalah memanfaatkan distutils untuk menginstal modul. Ini membutuhkan file setup:
Untuk akhirnya menginstal modul, jalankan
python3 setup.py install
dari lingkungan virtual Anda.Menguji Modul
Terakhir, kita dapat menguji untuk melihat apakah implementasi C ++ benar-benar mengungguli penggunaan NumPy yang naif. Untuk melakukannya, berikut ini skrip pengujian sederhana:
Inilah hasil yang saya dapat dari melakukan semua ini:
Ini jauh kurang menggembirakan daripada hasil yang ditunjukkan sebelumnya di utas, yang menunjukkan sekitar 3,5x percepatan, dan tidak menyertakan multi-threading. Hasil yang saya capai agak masuk akal, saya berharap bahwa overhead threading dan akan mendominasi waktu sampai array menjadi sangat besar, di mana peningkatan kinerja akan mulai mendekati
std::thread::hardware_concurrency
peningkatan x.Kesimpulan
Jelas ada ruang untuk pengoptimalan khusus aplikasi untuk beberapa kode NumPy, tampaknya, khususnya yang berkaitan dengan multi-threading. Apakah itu sepadan atau tidak, tidak jelas bagi saya, tetapi itu jelas terlihat seperti latihan yang baik (atau sesuatu). Saya pikir mungkin mempelajari beberapa "alat pihak ketiga" seperti Cython mungkin penggunaan waktu yang lebih baik, tapi siapa tahu.
sumber
v = min_max_it->get();
. Theget
Metode blok sampai hasilnya siap dan kembali itu. Karena perulangan melewati setiap masa depan, itu tidak akan selesai sampai semuanya selesai. future.get ()Cara terpendek yang saya temukan adalah ini:
Tapi karena itu mengurutkan array, itu bukan yang paling efisien.
Cara singkat lainnya adalah:
Ini seharusnya lebih efisien, tetapi hasilnya dihitung, dan float dikembalikan.
sumber