Saya memiliki dua array numpy yang menentukan sumbu x dan y dari grid. Sebagai contoh:
x = numpy.array([1,2,3])
y = numpy.array([4,5])
Saya ingin menghasilkan produk Cartesian dari array ini untuk menghasilkan:
array([[1,4],[2,4],[3,4],[1,5],[2,5],[3,5]])
Dengan cara yang tidak terlalu tidak efisien karena saya perlu melakukan ini berkali-kali dalam satu lingkaran. Saya berasumsi bahwa mengonversi mereka ke daftar Python dan menggunakan itertools.product
dan kembali ke array numpy bukan bentuk yang paling efisien.
Jawaban:
Lihat Menggunakan numpy untuk membangun array dari semua kombinasi dari dua array untuk solusi umum untuk menghitung produk Cartesian dari array N.
sumber
meshgrid
+dstack
pendekatan, sementara lebih cepat dalam beberapa kasus, dapat menyebabkan bug jika Anda mengharapkan produk Cartesian yang akan dibangun dalam urutan yang sama untuk array dengan ukuran yang sama.meshgrid
+dstack
. Bisakah Anda memposting contoh?Kanonik
cartesian_product
(hampir)Ada banyak pendekatan untuk masalah ini dengan properti yang berbeda. Beberapa lebih cepat dari yang lain, dan beberapa lebih untuk tujuan umum. Setelah banyak pengujian dan penyesuaian, saya menemukan bahwa fungsi berikut, yang menghitung dimensi-n
cartesian_product
, lebih cepat daripada kebanyakan yang lain untuk banyak input. Untuk sepasang pendekatan yang sedikit lebih kompleks, tetapi bahkan sedikit lebih cepat dalam banyak kasus, lihat jawabannya oleh Paul Panzer .Mengingat jawaban itu, ini bukan lagi implementasi tercepat dari produk cartesian
numpy
yang saya sadari. Namun, saya pikir kesederhanaannya akan terus menjadikannya tolok ukur yang berguna untuk peningkatan di masa depan:Perlu disebutkan bahwa fungsi ini digunakan
ix_
dengan cara yang tidak biasa; sedangkan penggunaan terdokumentasiix_
adalah untuk menghasilkan indeks ke dalam array, kebetulan bahwa array dengan bentuk yang sama dapat digunakan untuk penugasan yang disiarkan. Terima kasih banyak kepada mgilson , yang mengilhami saya untuk mencoba menggunakanix_
cara ini, dan kepada unutbu , yang memberikan umpan balik yang sangat membantu pada jawaban ini, termasuk saran untuk menggunakannumpy.result_type
.Alternatif penting
Terkadang lebih cepat untuk menulis blok memori yang berdekatan dalam urutan Fortran. Itulah dasar dari alternatif ini
cartesian_product_transpose
,, yang telah terbukti lebih cepat pada beberapa perangkat keras daripadacartesian_product
(lihat di bawah). Namun, jawaban Paul Panzer, yang menggunakan prinsip yang sama, bahkan lebih cepat. Namun, saya memasukkan ini di sini untuk pembaca yang tertarik:Setelah memahami pendekatan Panzer, saya menulis versi baru yang hampir secepat miliknya, dan hampir sesederhana
cartesian_product
:Ini tampaknya memiliki beberapa overhead waktu konstan yang membuatnya berjalan lebih lambat daripada Panzer untuk input kecil. Tetapi untuk input yang lebih besar, dalam semua tes yang saya jalankan, performanya sama baiknya dengan implementasi tercepatnya (
cartesian_product_transpose_pp
).Di bagian berikut, saya menyertakan beberapa tes alternatif lain. Ini sekarang agak ketinggalan zaman, tetapi daripada upaya duplikat, saya telah memutuskan untuk meninggalkan mereka di sini karena kepentingan sejarah. Untuk tes terbaru, lihat jawaban Panzer, serta Nico Schlömer .
Tes terhadap alternatif
Berikut ini adalah serangkaian tes yang menunjukkan peningkatan kinerja yang disediakan beberapa fungsi ini relatif terhadap sejumlah alternatif. Semua tes yang ditunjukkan di sini dilakukan pada mesin quad-core, menjalankan Mac OS 10.12.5, Python 3.6.1, dan
numpy
1.12.1. Variasi pada perangkat keras dan lunak diketahui menghasilkan hasil yang berbeda, demikian YMMV. Jalankan tes ini untuk diri Anda sendiri untuk memastikan!Definisi:
Hasil tes:
Dalam semua kasus,
cartesian_product
sebagaimana didefinisikan pada awal jawaban ini adalah yang tercepat.Untuk fungsi-fungsi yang menerima jumlah array input yang sewenang-wenang, ada baiknya memeriksa kinerja
len(arrays) > 2
juga. (Sampai saya dapat menentukan mengapacartesian_product_recursive
melempar kesalahan dalam kasus ini, saya telah menghapusnya dari tes ini.)Seperti yang ditunjukkan oleh tes ini,
cartesian_product
tetap kompetitif hingga jumlah array input naik di atas (kira-kira) empat. Setelah itu,cartesian_product_transpose
memang ada sedikit keunggulan.Perlu ditegaskan kembali bahwa pengguna dengan perangkat keras dan sistem operasi lain mungkin melihat hasil yang berbeda. Misalnya, unutbu melaporkan melihat hasil berikut untuk pengujian ini menggunakan Ubuntu 14.04, Python 3.4.3, dan
numpy
1.14.0.dev0 + b7050a9:Di bawah ini, saya masuk ke beberapa detail tentang tes sebelumnya yang saya jalankan di sepanjang garis ini. Kinerja relatif dari pendekatan ini telah berubah dari waktu ke waktu, untuk berbagai perangkat keras dan versi Python dan
numpy
. Meskipun tidak segera bermanfaat bagi orang yang menggunakan versi terbarunumpy
, ini menggambarkan bagaimana banyak hal telah berubah sejak versi pertama dari jawaban ini.Alternatif sederhana:
meshgrid
+dstack
Jawaban yang saat ini diterima menggunakan
tile
danrepeat
untuk menyiarkan dua array bersama-sama. Tetapimeshgrid
fungsinya praktis melakukan hal yang sama. Inilah output daritile
danrepeat
sebelum diteruskan ke transpose:Dan inilah output dari
meshgrid
:Seperti yang Anda lihat, ini hampir identik. Kita hanya perlu membentuk ulang hasilnya untuk mendapatkan hasil yang persis sama.
Namun, alih-alih membentuk kembali pada titik ini, kita dapat meneruskan output dari
meshgrid
kedstack
dan membentuk kembali setelahnya, yang menghemat beberapa pekerjaan:Bertentangan dengan klaim dalam komentar ini , saya tidak melihat bukti bahwa input yang berbeda akan menghasilkan output yang berbeda bentuk, dan seperti ditunjukkan di atas, mereka melakukan hal yang sangat mirip, sehingga akan sangat aneh jika mereka melakukannya. Harap beri tahu saya jika Anda menemukan contoh tandingan.
Menguji
meshgrid
+dstack
vs.repeat
+transpose
Kinerja relatif dari kedua pendekatan ini telah berubah seiring waktu. Dalam versi Python sebelumnya (2.7), hasil menggunakan
meshgrid
+dstack
terasa lebih cepat untuk input kecil. (Perhatikan bahwa tes ini berasal dari versi lama dari jawaban ini.) Definisi:Untuk input berukuran sedang, saya melihat peningkatan yang signifikan. Tapi saya mencoba kembali tes ini dengan versi Python (3.6.1) dan
numpy
(1.12.1) yang lebih baru, pada mesin yang lebih baru. Kedua pendekatan itu hampir identik sekarang.Tes Lama
Tes Baru
Seperti biasa, YMMV, tetapi ini menunjukkan bahwa dalam versi terbaru Python dan numpy, ini dapat dipertukarkan.
Fungsi produk umum
Secara umum, kita mungkin berharap bahwa menggunakan fungsi bawaan akan lebih cepat untuk input kecil, sedangkan untuk input besar, fungsi yang dibangun khusus mungkin lebih cepat. Selanjutnya untuk produk n-dimensi umum,
tile
danrepeat
tidak akan membantu, karena mereka tidak memiliki analog dimensi yang lebih tinggi. Jadi ada baiknya menyelidiki perilaku fungsi yang dibangun juga.Sebagian besar tes yang relevan muncul di awal jawaban ini, tetapi di sini ada beberapa tes yang dilakukan pada versi Python sebelumnya dan
numpy
untuk perbandingan.The
cartesian
fungsi yang didefinisikan dalam jawaban lain digunakan untuk melakukan cukup baik untuk input yang lebih besar. (Ini sama dengan fungsi yang disebutcartesian_product_recursive
di atas.) Dalam rangka untuk membandingkancartesian
untukdstack_prodct
, kita menggunakan hanya dua dimensi.Di sini sekali lagi, tes lama menunjukkan perbedaan yang signifikan, sedangkan tes baru menunjukkan hampir tidak ada.
Tes Lama
Tes Baru
Seperti sebelumnya,
dstack_product
masih berdetakcartesian
pada skala yang lebih kecil.Tes Baru ( tes lama yang berlebihan tidak ditampilkan )
Menurut saya, perbedaan-perbedaan ini menarik dan layak dicatat; tetapi pada akhirnya mereka akademis. Seperti yang ditunjukkan oleh tes pada awal jawaban ini, semua versi ini hampir selalu lebih lambat dari yang
cartesian_product
didefinisikan pada awal jawaban ini - yang sedikit lebih lambat daripada implementasi tercepat di antara jawaban untuk pertanyaan ini.sumber
dtype=object
kearr = np.empty( )
akan memungkinkan untuk menggunakan berbagai jenis produk, misalnyaarrays = [np.array([1,2,3]), ['str1', 'str2']]
.cartesian_product_tranpose
lebih cepat daripadacartesian_product
tergantung pada OS mesin mereka, versi python atau numpy. Sebagai contoh, pada Ubuntu 14.04, python3.4.3, numpy 1.14.0.dev0 + b7050a9,%timeit cartesian_product_transpose(x500,y500)
menghasilkan1000 loops, best of 3: 682 µs per loop
sementara%timeit cartesian_product(x500,y500)
hasil1000 loops, best of 3: 1.55 ms per loop
. Saya juga menemukancartesian_product_transpose
mungkin lebih cepat kapanlen(arrays) > 2
.cartesian_product
mengembalikan array dari tipe floating-point sementaracartesian_product_transpose
mengembalikan array dengan tipe yang sama seperti array pertama (yang disiarkan). Kemampuan untuk mempertahankan dtype ketika bekerja dengan array integer mungkin menjadi alasan bagi pengguna untuk mendukungcartesian_product_transpose
.dtype = np.find_common_type([arr.dtype for arr in arrays], [])
dapat digunakan untuk menemukan dtype umum dari semua array, alih-alih memaksa pengguna untuk menempatkan array yang mengontrol dtype terlebih dahulu.Anda bisa melakukan pemahaman daftar normal dengan python
yang seharusnya memberi Anda
sumber
Saya juga tertarik dengan ini dan melakukan sedikit perbandingan kinerja, mungkin agak lebih jelas daripada di jawaban @ senderle.
Untuk dua array (kasus klasik):
Untuk empat array:
(Perhatikan bahwa panjang array hanya beberapa lusin entri di sini.)
Kode untuk mereproduksi plot:
sumber
Membangun di atas tanah contoh kerja @ senderle saya telah datang dengan dua versi - satu untuk C dan satu untuk tata letak Fortran - yang seringkali sedikit lebih cepat.
cartesian_product_transpose_pp
adalah - tidak seperti @ senderlecartesian_product_transpose
yang menggunakan strategi yang berbeda sama sekali - versicartesion_product
yang menggunakan tata letak memori transpose yang lebih menguntungkan + beberapa optimasi yang sangat kecil.cartesian_product_pp
tetap dengan tata letak memori asli. Apa yang membuatnya cepat adalah penggunaan salin yang berdekatan. Salinan yang berdekatan ternyata jauh lebih cepat sehingga menyalin blok memori penuh meskipun hanya sebagian yang berisi data yang valid lebih baik daripada hanya menyalin bit yang valid.Beberapa perflot. Saya membuat yang terpisah untuk tata letak C dan Fortran, karena ini adalah tugas yang berbeda IMO.
Nama yang berakhiran 'pp' adalah pendekatan saya.
1) banyak faktor kecil (masing-masing 2 elemen)
2) banyak faktor kecil (masing-masing 4 elemen)
3) tiga faktor dengan panjang yang sama
4) dua faktor dengan panjang yang sama
Kode (perlu melakukan run terpisah untuk setiap plot b / c Saya tidak tahu cara mengatur ulang; juga perlu mengedit / berkomentar masuk / keluar dengan tepat):
sumber
arrays
dalam cartesian_product_transpose_pp (array) melebihi ukuran tertentu,MemoryError
akan terjadi. Dalam situasi ini, saya ingin fungsi ini menghasilkan hasil yang lebih kecil. Saya telah mengirim pertanyaan tentang masalah ini. Bisakah Anda menjawab pertanyaan saya? Terima kasih.Pada Oktober 2017, numpy sekarang memiliki
np.stack
fungsi generik yang mengambil parameter sumbu. Dengan menggunakannya, kita dapat memiliki "produk cartesian umum" menggunakan teknik "dstack and meshgrid:Perhatikan pada
axis=-1
parameter. Ini adalah sumbu (paling dalam) terakhir dalam hasilnya. Ini setara dengan menggunakanaxis=ndim
.Satu komentar lain, karena produk Cartesian meledak sangat cepat, kecuali kita perlu menyadari susunan dalam memori untuk beberapa alasan, jika produk tersebut sangat besar, kita mungkin ingin menggunakan
itertools
dan menggunakan nilai-nilai on-the-fly.sumber
Saya menggunakan jawaban @kennytm untuk sementara waktu, tetapi ketika mencoba melakukan hal yang sama di TensorFlow, tetapi saya menemukan bahwa TensorFlow tidak ada bandingannya dengan
numpy.repeat()
. Setelah sedikit percobaan, saya pikir saya menemukan solusi yang lebih umum untuk vektor titik sembarang.Untuk numpy:
dan untuk TensorFlow:
sumber
Paket Scikit-learn memiliki implementasi cepat untuk hal ini:
Perhatikan bahwa konvensi implementasi ini berbeda dari yang Anda inginkan, jika Anda peduli dengan urutan output. Untuk pemesanan yang tepat, Anda bisa melakukannya
sumber
Secara lebih umum, jika Anda memiliki dua array numpy 2d dan a, dan Anda ingin menggabungkan setiap baris dari a ke setiap baris dari b (Produk cartesian dari baris, seperti gabungan dalam database), Anda dapat menggunakan metode ini :
sumber
Cara tercepat yang bisa Anda dapatkan adalah dengan menggabungkan ekspresi generator dengan fungsi peta:
Keluaran (sebenarnya seluruh daftar yang dihasilkan dicetak):
atau dengan menggunakan ekspresi generator ganda:
Output (seluruh daftar dicetak):
Perhatikan bahwa sebagian besar waktu perhitungan digunakan untuk perintah pencetakan. Perhitungan generator dinyatakan cukup efisien. Tanpa mencetak waktu perhitungan adalah:
untuk ekspresi generator + fungsi peta dan:
untuk ekspresi generator ganda.
Jika yang Anda inginkan adalah menghitung produk aktual dari masing-masing pasangan koordinat, yang tercepat adalah menyelesaikannya sebagai produk matriks numpy:
Output:
dan tanpa mencetak (dalam hal ini tidak menghemat banyak karena hanya sebagian kecil dari matriks yang benar-benar dicetak):
sumber
foo = a[:,None]*b
lebih cepat. Menggunakan metode pewaktuan Anda tanpaprint(foo)
, itu 0,001103 s vs 0,002225 s. Menggunakan timeit, ini 304 μs vs 1.6 ms. Matrix diketahui lebih lambat dari ndarray, jadi saya mencoba kode Anda dengan np.array tetapi masih lebih lambat (1,57 ms) daripada penyiaran.Ini juga dapat dengan mudah dilakukan dengan menggunakan metode itertools.product
Hasil: array ([
[1, 4],
[1, 5],
[2, 4],
[2, 5],
[3, 4],
[3, 5]], dtype = int32)
Waktu eksekusi: 0,000155 s
sumber
Dalam kasus khusus yang Anda perlukan untuk melakukan operasi sederhana seperti penambahan pada setiap pasangan, Anda dapat memperkenalkan dimensi ekstra dan membiarkan penyiaran melakukan pekerjaan:
Saya tidak yakin apakah ada cara serupa untuk benar-benar mendapatkan pasangan itu sendiri.
sumber
dtype
adalahfloat
yang dapat Anda lakukan(a[:, None, None] + 1j * b[None, :, None]).view(float)
yang sangat cepat.