Saya memiliki array numpy seperti ini: [1 2 2 0 0 1 3 5]
Apakah mungkin untuk mendapatkan indeks elemen sebagai array 2d? Misalnya jawaban untuk input di atas adalah[[3 4], [0 5], [1 2], [6], [], [7]]
Saat ini saya harus mengulang nilai-nilai yang berbeda dan memanggil numpy.where(input == i)
untuk setiap nilai, yang memiliki kinerja mengerikan dengan input yang cukup besar.
python
numpy
numpy-ndarray
Frederico Schardong
sumber
sumber
np.argsort([1, 2, 2, 0, 0, 1, 3, 5])
memberiarray([3, 4, 0, 5, 1, 2, 6, 7], dtype=int64)
. maka Anda bisa membandingkan elemen berikutnya.Jawaban:
Berikut ini adalah pendekatan O (maks (x) + len (x)) menggunakan
scipy.sparse
:Ini bekerja dengan membuat matriks jarang dengan entri pada posisi (x [0], 0), (x [1], 1), ... Menggunakan format
CSC
(kolom jarang dikompresi) ini agak sederhana. Matriks tersebut kemudian dikonversi keLIL
format (daftar tertaut). Format ini menyimpan indeks kolom untuk setiap baris sebagai daftar dirows
atributnya, jadi yang perlu kita lakukan adalah mengambilnya dan mengubahnya menjadi daftar.Perhatikan bahwa untuk
argsort
solusi berbasis array kecil mungkin lebih cepat tetapi pada beberapa ukuran tidak gila besar ini akan menyeberang.EDIT:
argsort
-berbasisnumpy
-hanya solusi:Jika urutan indeks dalam grup tidak masalah Anda juga dapat mencoba
argpartition
(kebetulan tidak ada bedanya dalam contoh kecil ini tetapi ini tidak dijamin secara umum):EDIT:
@Ivakar merekomendasikan untuk tidak menggunakan
np.split
. Alih-alih, satu loop mungkin lebih cepat:Atau Anda bisa menggunakan operator walrus baru (Python3.8 +):
EDIT (Diedit):
(Tidak murni numpy): Sebagai alternatif untuk numba (lihat posting @ senderle) kita juga bisa menggunakan pythran.
Kompilasi dengan
pythran -O3 <filename.py>
Di sini
numba
dimenangkan oleh penampilan kumis:Hal-hal yang lebih tua:
Pengaturan waktu vs. numba (lama)
sumber
np.split
.Salah satu opsi potensial tergantung pada ukuran data Anda adalah hanya keluar
numpy
dan menggunakancollections.defaultdict
:Kemudian Anda berakhir dengan kamus
{value1: [index1, index2, ...], value2: [index3, index4, ...]}
. Penskalaan waktu cukup dekat dengan linier dengan ukuran array, jadi 10.000.000 membutuhkan ~ 2.7 pada mesin saya, yang tampaknya cukup masuk akal.sumber
Meskipun permintaan adalah
numpy
solusi, saya memutuskan untuk melihat apakah adanumba
solusi berbasis- menarik . Dan memang ada! Berikut adalah pendekatan yang mewakili daftar dipartisi sebagai array kasar yang disimpan dalam satu buffer yang dialokasikan sebelumnya. Ini mengambil beberapa inspirasi dariargsort
pendekatan yang diusulkan oleh Paul Panzer . (Untuk versi yang lebih lama yang tidak melakukannya juga, tetapi lebih sederhana, lihat di bawah.)Ini memproses daftar sepuluh juta item dalam 75ms, yang hampir 50x percepatan dari versi berbasis daftar yang ditulis dengan Python murni.
Untuk versi yang lebih lambat tetapi agak lebih mudah dibaca, inilah yang saya miliki sebelumnya, berdasarkan pada dukungan eksperimental yang baru-baru ini ditambahkan untuk "daftar yang diketik," yang berukuran dinamis yang memungkinkan kami mengisi setiap nampan dengan cara yang tidak sesuai pesanan jauh lebih cepat.
Ini sedikit bergulat dengan
numba
tipe mesin inferensi, dan saya yakin ada cara yang lebih baik untuk menangani bagian itu. Ini juga ternyata hampir 10x lebih lambat dari yang di atas.Saya menguji ini terhadap yang berikut:
Saya juga mengujinya terhadap versi cython yang dikompilasi mirip dengan
enum_bins_numba_buffer
(dijelaskan secara rinci di bawah).Pada daftar sepuluh juta int acak (
ints = np.random.randint(0, 100, 10000000)
) saya mendapatkan hasil berikut:Secara mengesankan, cara ini bekerja dengan
numba
mengunggulicython
versi dari fungsi yang sama, bahkan dengan memeriksa batas dimatikan. Saya belum memiliki cukup keakrabanpythran
untuk menguji pendekatan ini menggunakannya, tetapi saya akan tertarik untuk melihat perbandingan. Tampaknya berdasarkan pada percepatan ini bahwapythran
versi mungkin juga sedikit lebih cepat dengan pendekatan ini.Inilah
cython
versi untuk referensi, dengan beberapa instruksi pembuatan. Setelah Andacython
menginstal, Anda akan memerlukansetup.py
file sederhana seperti ini:Dan modul Cython,
enum_bins_cython.pyx
:Dengan dua file ini di direktori kerja Anda, jalankan perintah ini:
Anda kemudian dapat mengimpor fungsi menggunakan
from enum_bins_cython import enum_bins_cython
.sumber
Inilah cara yang sangat aneh untuk melakukan ini, itu mengerikan, tetapi saya merasa terlalu lucu untuk tidak berbagi - dan semuanya
numpy
!EDIT: ini adalah metode terbaik yang bisa saya temukan di sepanjang jalan ini. Masih 10x lebih lambat dari
argsort
solusi @PaulPanzer :sumber
Anda dapat melakukannya dengan membuat kamus angka, kunci akan menjadi angka dan nilai harus menjadi indeks yang dilihat angka, ini adalah salah satu cara tercepat untuk melakukannya, Anda dapat melihat kode di bawah ini:
sumber
Kodesemu:
dapatkan "jumlah array 1d dalam array 2d", dengan mengurangi nilai minimum array numpy Anda dari nilai maksimum dan kemudian ditambah satu. Dalam kasus Anda, itu akan menjadi 5-0 + 1 = 6
inisialisasi array 2d dengan jumlah array 1d di dalamnya. Dalam kasus Anda, inisialisasi array 2d dengan 6 array 1d di dalamnya. Setiap array 1d sesuai dengan elemen unik dalam array numpy Anda, misalnya, array 1d pertama akan sesuai dengan '0', array 1d kedua akan sesuai dengan '1', ...
loop melalui array numpy Anda, masukkan indeks elemen ke dalam array 1d yang tepat. Dalam kasus Anda, indeks elemen pertama di array numpy Anda akan dimasukkan ke array 1d kedua, indeks elemen kedua di array numpy Anda akan dimasukkan ke array 1d ketiga, ....
Pseudocode ini akan membutuhkan waktu linier untuk berjalan karena tergantung pada panjang array numpy Anda.
sumber
Ini memberi Anda apa yang Anda inginkan dan akan memakan waktu 2,5 detik selama 10.000.000 pada mesin saya:
sumber
Jadi diberi daftar elemen, Anda ingin membuat (elemen, indeks) pasangan. Dalam waktu linier, ini dapat dilakukan sebagai:
Ini membutuhkan waktu O (n). Saya tidak bisa memikirkan solusi yang lebih cepat seperti sekarang, tetapi akan memperbarui di sini jika saya lakukan.
sumber