Apa cara paling efisien untuk memetakan suatu fungsi pada array yang numpy? Cara saya melakukannya dalam proyek saya saat ini adalah sebagai berikut:
import numpy as np
x = np.array([1, 2, 3, 4, 5])
# Obtain array of square of each element in x
squarer = lambda t: t ** 2
squares = np.array([squarer(xi) for xi in x])
Namun, ini sepertinya sangat tidak efisien, karena saya menggunakan pemahaman daftar untuk membangun array baru sebagai daftar Python sebelum mengubahnya kembali menjadi array numpy.
Bisakah kita berbuat lebih baik?
python
performance
numpy
Ryan
sumber
sumber
squarer(x)
?x = np.array([1, 2, 3, 4, 5]); x**2
bekerjaJawaban:
Saya telah menguji semua metode yang disarankan ditambah
np.array(map(f, x))
denganperfplot
(proyek kecil saya).Jika fungsi yang Anda coba vektorkan sudah menjadi vektor (seperti
x**2
contoh di posting asli), menggunakan itu jauh lebih cepat daripada yang lain (perhatikan skala log):Jika Anda benar-benar membutuhkan vektorisasi, tidak terlalu penting varian mana yang Anda gunakan.
Kode untuk mereproduksi plot:
sumber
f(x)
rencana Anda. Itu mungkin tidak berlaku untuk setiap orangf
, tetapi itu berlaku di sini, dan itu dengan mudah solusi tercepat saat berlaku.vf = np.vectorize(f); y = vf(x)
menang untuk input pendek.pip install -U perfplot
), saya melihat pesan:AttributeError: 'module' object has no attribute 'save'
ketika menempelkan kode contoh.Bagaimana kalau menggunakan
numpy.vectorize
.sumber
The vectorize function is provided primarily for convenience, not for performance. The implementation is essentially a for loop.
Dalam pertanyaan lain saya menemukan bahwavectorize
mungkin menggandakan kecepatan iterasi pengguna. Tetapi speedup sebenarnya adalah dengannumpy
operasi array nyata .squarer(x)
sudah bekerja untuk array non-1d.vectorize
hanya benar-benar memiliki keunggulan dibandingkan pemahaman daftar (seperti yang ada di pertanyaan), tidak lebihsquarer(x)
.TL; DR
Seperti dicatat oleh @ user2357112 , metode "langsung" menerapkan fungsi selalu merupakan cara tercepat dan termudah untuk memetakan fungsi melalui array Numpy:
Umumnya menghindari
np.vectorize
, karena tidak berkinerja baik, dan memiliki (atau pernah) sejumlah masalah . Jika Anda menangani tipe data lain, Anda mungkin ingin menyelidiki metode lain yang ditunjukkan di bawah ini.Perbandingan metode
Berikut adalah beberapa tes sederhana untuk membandingkan tiga metode untuk memetakan suatu fungsi, contoh ini menggunakan dengan Python 3.6 dan NumPy 1.15.4. Pertama, fungsi pengaturan untuk pengujian:
Pengujian dengan lima elemen (diurutkan dari yang tercepat ke yang terlambat):
Dengan 100-an elemen:
Dan dengan 1000s elemen array atau lebih:
Versi berbeda dari Python / NumPy dan optimisasi kompiler akan memiliki hasil yang berbeda, jadi lakukan tes serupa untuk lingkungan Anda.
sumber
count
argumen dan ekspresi generator makanp.fromiter
secara signifikan lebih cepat.'np.fromiter((f(xi) for xi in x), x.dtype, count=len(x))'
f(x)
, yang mengalahkan segalanya dengan lebih dari satu urutan besarnya .f
memiliki 2 variabel dan array adalah 2D?Ada numexpr , numba dan cython di sekitar, tujuan dari jawaban ini adalah untuk mempertimbangkan kemungkinan-kemungkinan ini.
Tapi pertama-tama mari kita nyatakan yang jelas: tidak peduli bagaimana Anda memetakan fungsi-Python ke array-numpy, tetap fungsi Python, yang berarti untuk setiap evaluasi:
Float
).Jadi mesin mana yang digunakan untuk benar-benar loop melalui array tidak memainkan peran besar karena overhead yang disebutkan di atas - itu tetap jauh lebih lambat daripada menggunakan fungsi built-in numpy.
Mari kita lihat contoh berikut:
np.vectorize
dipilih sebagai perwakilan dari kelas fungsi python murni dari pendekatan. Dengan menggunakanperfplot
(lihat kode pada lampiran jawaban ini) kami mendapatkan waktu berjalan berikut:Kita bisa melihat, bahwa pendekatan numpy adalah 10x-100x lebih cepat daripada versi python murni. Penurunan kinerja untuk ukuran array yang lebih besar mungkin karena data tidak lagi sesuai dengan cache.
Perlu juga disebutkan, yang
vectorize
juga menggunakan banyak memori, jadi sering penggunaan memori adalah botol-leher (lihat pertanyaan SO terkait ). Juga perhatikan, bahwa dokumentasi numpynp.vectorize
menyatakan bahwa itu "disediakan terutama untuk kenyamanan, bukan untuk kinerja".Alat lain harus digunakan, ketika kinerja diinginkan, selain menulis ekstensi-C dari awal, ada beberapa kemungkinan berikut:
Orang sering mendengar, bahwa kinerja numpy sebagus yang didapat, karena murni C di bawah tenda. Namun ada banyak ruang untuk perbaikan!
Versi numpy vektor menggunakan banyak memori tambahan dan akses memori. Numexp-library mencoba untuk men-tile numpy-array dan karenanya mendapatkan pemanfaatan cache yang lebih baik:
Mengarah ke perbandingan berikut:
Saya tidak bisa menjelaskan semuanya dalam plot di atas: kita bisa melihat overhead yang lebih besar untuk numexpr-library di awal, tetapi karena menggunakan cache lebih baik, ini sekitar 10 kali lebih cepat untuk array yang lebih besar!
Pendekatan lain adalah untuk mengkompilasi fungsi dan dengan demikian mendapatkan UFunc pure-C yang nyata. Ini adalah pendekatan numba:
Ini 10 kali lebih cepat dari pendekatan numpy asli:
Namun, tugasnya memaralelkan, sehingga kita juga bisa menggunakan
prange
untuk menghitung loop secara paralel:Seperti yang diharapkan, fungsi paralel lebih lambat untuk input yang lebih kecil, tetapi lebih cepat (hampir faktor 2) untuk ukuran yang lebih besar:
Sementara numba berspesialisasi dalam mengoptimalkan operasi dengan numpy-array, Cython adalah alat yang lebih umum. Lebih rumit untuk mengekstraksi kinerja yang sama dengan numba - seringkali turun ke llvm (numba) vs kompiler lokal (gcc / MSVC):
Cython menghasilkan fungsi yang agak lambat:
Kesimpulan
Jelas, pengujian hanya untuk satu fungsi tidak membuktikan apa-apa. Juga harus diingat, bahwa untuk fungsi-contoh yang dipilih, bandwidth memori adalah leher botol untuk ukuran lebih besar dari 10 ^ 5 elemen - sehingga kami memiliki kinerja yang sama untuk numba, numexpr dan cython di wilayah ini.
Pada akhirnya, jawaban ultimatif tergantung pada jenis fungsi, perangkat keras, distribusi Python dan faktor lainnya. Misalnya Anaconda-distribusi menggunakan Intel VML untuk fungsi numpy dan dengan demikian melebihi Numba (kecuali menggunakan SVML, melihat ini SO-posting ) mudah untuk fungsi-fungsi transendental seperti
exp
,sin
,cos
dan sejenis - lihat misalnya berikut SO-post .Namun dari penyelidikan ini dan dari pengalaman saya sejauh ini, saya akan menyatakan, bahwa numba tampaknya menjadi alat yang paling mudah dengan kinerja terbaik selama tidak ada fungsi transendental yang terlibat.
Merencanakan waktu berjalan dengan perfplot -paket:
sumber
Operasi aritmatika pada array secara otomatis diterapkan dengan elemen, dengan loop tingkat-C yang efisien yang menghindari semua overhead juru yang akan berlaku untuk loop level Python atau pemahaman.
Sebagian besar fungsi yang ingin Anda terapkan ke array NumPy hanya akan berfungsi, meskipun beberapa mungkin perlu diubah. Misalnya,
if
tidak berfungsi secara elemen. Anda ingin mengonversi yang menggunakan konstruksi sepertinumpy.where
:menjadi
sumber
Dalam banyak kasus, numpy.apply_along_axis akan menjadi pilihan terbaik. Ini meningkatkan kinerja sekitar 100x dibandingkan dengan pendekatan lain - dan tidak hanya untuk fungsi tes sepele, tetapi juga untuk komposisi fungsi yang lebih kompleks dari numpy dan scipy.
Ketika saya menambahkan metode:
ke kode perfplot, saya mendapatkan hasil berikut:
sumber
Saya percaya pada versi yang lebih baru (saya menggunakan 1,13) numpy Anda cukup memanggil fungsi dengan meneruskan array numpy ke fungsinya yang Anda tulis untuk tipe skalar, itu akan secara otomatis menerapkan pemanggilan fungsi ke setiap elemen melalui array numpy dan mengembalikan Anda array numpy lainnya
sumber
**
operator yang melamar perhitungan untuk setiap t element
. Itu numpy biasa. Membungkusnya dilambda
tidak melakukan apa-apa ekstra.Tampaknya tidak ada yang menyebutkan metode pabrik built-in memproduksi
ufunc
dalam paket numpy:np.frompyfunc
yang telah saya uji laginp.vectorize
dan mengungguli sekitar 20 ~ 30%. Tentu saja itu akan bekerja dengan baik seperti kode C yang ditentukan atau bahkannumba
(yang belum saya uji), tetapi bisa menjadi alternatif yang lebih baik daripadanp.vectorize
Saya juga telah menguji sampel yang lebih besar, dan peningkatannya proporsional. Lihat dokumentasi juga di sini
sumber
Seperti yang disebutkan dalam posting ini , cukup gunakan ekspresi generator seperti:
sumber
Semua jawaban di atas sebanding dengan baik, tetapi jika Anda perlu menggunakan fungsi kustom untuk pemetaan, dan Anda miliki
numpy.ndarray
, dan Anda perlu mempertahankan bentuk array.Saya telah membandingkan hanya dua, tetapi akan mempertahankan bentuk
ndarray
. Saya telah menggunakan array dengan 1 juta entri untuk perbandingan. Di sini saya menggunakan fungsi persegi, yang juga built in numpy dan memiliki peningkatan kinerja yang hebat, karena di sana seperti membutuhkan sesuatu, Anda dapat menggunakan fungsi pilihan Anda.Keluaran
di sini Anda dapat melihat dengan jelas
numpy.fromiter
pekerjaan yang bagus mengingat pendekatan yang sederhana, dan jika tersedia fungsi inbuilt, gunakan itu.sumber
Menggunakan
numpy.fromfunction(function, shape, **kwargs)
Lihat " https://docs.scipy.org/doc/numpy/reference/generated/numpy.fromfunction.html "
sumber