Orang harus jelas apakah tidak akan ada solusi (karena misalnya jawaban argmax tidak akan berfungsi dalam kasus itu (maks. (0,0,0,0) = 0) seperti komentar
ambrus
Jawaban:
199
Ini sedikit lebih cepat (dan terlihat lebih bagus)
np.argmax(aa>5)
Karena argmaxakan berhenti pada yang pertama True("Jika terjadi beberapa kali nilai maksimum, indeks yang sesuai dengan kejadian pertama dikembalikan.") Dan tidak menyimpan daftar lain.
In[2]: N =10000In[3]: aa = np.arange(-N,N)In[4]: timeit np.argmax(aa>N/2)100000 loops, best of 3:52.3 us per loop
In[5]: timeit np.where(aa>N/2)[0][0]10000 loops, best of 3:141 us per loop
In[6]: timeit np.nonzero(aa>N/2)[0][0]10000 loops, best of 3:142 us per loop
Hanya kata peringatan: jika tidak ada nilai True dalam array inputnya, np.argmax akan dengan senang hati mengembalikan 0 (yang bukan yang Anda inginkan dalam kasus ini).
ambrus
8
Hasilnya benar, tetapi saya menemukan penjelasannya agak mencurigakan. argmaxsepertinya tidak berhenti pada awalnya True. (Ini dapat diuji dengan membuat array boolean dengan satu Truedi posisi yang berbeda.) Kecepatan mungkin dijelaskan oleh fakta bahwa argmaxtidak perlu membuat daftar output.
DrV
1
Saya pikir Anda benar, @ DVD. Penjelasan saya dimaksudkan tentang mengapa itu memberikan hasil yang benar meskipun maksud aslinya tidak benar-benar mencari yang maksimal, bukan mengapa itu lebih cepat karena saya tidak dapat mengklaim untuk memahami detail bagian dalam argmax.
askewchan
1
@ George, aku takut aku tidak tahu persis mengapa. Saya hanya bisa mengatakan itu lebih cepat dalam contoh khusus yang saya tunjukkan, jadi saya tidak akan menganggapnya lebih cepat secara umum tanpa (i) mengetahui mengapa demikian (lihat komentar @ DrV) atau (ii) menguji lebih banyak kasus (misalnya, apakah aadiurutkan, seperti pada jawaban @ Michael).
askewchan
3
@DrV, saya hanya berlari argmaxpada array 10 juta elemen Boolean dengan satu Truedi posisi yang berbeda menggunakan NumPy 1.11.2, dan posisi yang Truepenting. Jadi 1.11.2 argmaxtampaknya "hubungan pendek" pada array Boolean.
Ulrich Stern
96
diberikan konten yang diurutkan dari array Anda, ada metode yang lebih cepat: searchsorted .
import time
N =10000
aa = np.arange(-N,N)%timeit np.searchsorted(aa, N/2)+1%timeit np.argmax(aa>N/2)%timeit np.where(aa>N/2)[0][0]%timeit np.nonzero(aa>N/2)[0][0]# Output100000 loops, best of 3:5.97µs per loop
10000 loops, best of 3:46.3µs per loop
10000 loops, best of 3:154µs per loop
10000 loops, best of 3:154µs per loop
Ini benar-benar jawaban terbaik dengan asumsi array diurutkan (yang sebenarnya tidak ditentukan dalam pertanyaan). Anda dapat menghindari canggung +1dengannp.searchsorted(..., side='right')
askewchan
3
Saya pikir sideargumen hanya membuat perbedaan jika ada nilai yang diulang dalam array yang diurutkan. Itu tidak mengubah arti dari indeks yang dikembalikan, yang selalu merupakan indeks tempat Anda dapat memasukkan nilai kueri, menggeser semua entri berikut ke kanan, dan mempertahankan array yang diurutkan.
Gus
@ Geus, sidememiliki efek ketika nilai yang sama di kedua diurutkan dan array yang dimasukkan, terlepas dari nilai yang diulang di kedua. Nilai berulang dalam array yang disortir hanya melebih-lebihkan efeknya (perbedaan antara sisi adalah berapa kali nilai yang dimasukkan muncul dalam array yang diurutkan). sidetidak mengubah arti dari indeks yang dikembalikan, meskipun itu tidak mengubah array yang dihasilkan dari memasukkan nilai-nilai ke dalam array yang diurutkan pada indeks tersebut. Perbedaan yang halus tapi penting; sebenarnya jawaban ini memberikan indeks yang salah jika N/2tidak ada aa.
askewchan
Seperti yang ditunjukkan dalam komentar di atas, jawaban ini salah jika N/2tidak ada di aa. Bentuk yang benar adalah np.searchsorted(aa, N/2, side='right')(tanpa +1). Kedua bentuk memberikan indeks yang sama jika tidak. Pertimbangkan kasus uji untuk Nmenjadi aneh (dan N/2.0untuk memaksa mengambang jika menggunakan python 2).
askewchan
21
Saya juga tertarik dengan ini dan saya telah membandingkan semua jawaban yang disarankan dengan perfplot . (Penafian: Saya penulis perfplot.)
Jika Anda tahu bahwa array yang Anda cari sudah diurutkan , maka
numpy.searchsorted(a, alpha)
adalah untukmu. Ini adalah operasi waktu konstan, yaitu, kecepatan tidak tergantung pada ukuran array. Anda tidak bisa lebih cepat dari itu.
Jika Anda tidak tahu apa-apa tentang array Anda, Anda tidak akan salah
np.searchsortedbukan waktu yang konstan. Sebenarnya O(log(n)). Tetapi test case Anda benar-benar memberikan tolok ukur pada kasus terbaik searchsorted(yaitu O(1)).
MSeifert
@ MSeifert Array input / alpha seperti apa yang Anda perlukan untuk melihat O (log (n))?
Nico Schlömer
1
Mendapatkan item pada indeks sqrt (panjang) memang menyebabkan kinerja yang sangat buruk. Saya juga menulis jawaban di sini termasuk tolok ukur itu.
MSeifert
Saya ragu searchsorted(atau algoritma apa pun) dapat mengalahkan O(log(n))pencarian biner untuk data yang didistribusikan secara seragam. EDIT: searchsortedadalah pencarian biner.
Mateen Ulhaq
16
In[34]: a=np.arange(-10,10)In[35]: a
Out[35]:
array([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9])In[36]: np.where(a>5)Out[36]:(array([16,17,18,19]),)In[37]: np.where(a>5)[0][0]Out[37]:16
Jika rangearray atau peningkatan linear lainnya, Anda dapat menghitung indeks secara terprogram, tidak perlu benar-benar beralih ke array sama sekali:
def first_index_calculate_range_like(val, arr):if len(arr)==0:raiseValueError('no value greater than {}'.format(val))elif len(arr)==1:if arr[0]> val:return0else:raiseValueError('no value greater than {}'.format(val))
first_value = arr[0]
step = arr[1]- first_value
# For linearly decreasing arrays or constant arrays we only need to check# the first element, because if that does not satisfy the condition# no other element will.if step <=0:if first_value > val:return0else:raiseValueError('no value greater than {}'.format(val))
calculated_position =(val - first_value)/ step
if calculated_position <0:return0elif calculated_position > len(arr)-1:raiseValueError('no value greater than {}'.format(val))return int(calculated_position)+1
Seseorang mungkin bisa sedikit memperbaiki itu. Saya telah memastikan itu berfungsi dengan benar untuk beberapa sampel array dan nilai-nilai tetapi itu tidak berarti tidak mungkin ada kesalahan di sana, terutama mengingat bahwa ia menggunakan pelampung ...
Mengingat bahwa ia dapat menghitung posisi tanpa iterasi apa pun, itu akan menjadi waktu yang konstan ( O(1)) dan mungkin dapat mengalahkan semua pendekatan yang disebutkan lainnya. Namun itu membutuhkan langkah konstan dalam array, jika tidak maka akan menghasilkan hasil yang salah.
Solusi umum menggunakan numba
Pendekatan yang lebih umum akan menggunakan fungsi numba:
Meskipun Nico Schlömer sudah memberikan beberapa tolok ukur, saya pikir mungkin berguna untuk memasukkan solusi baru saya dan untuk menguji "nilai" yang berbeda.
Pengaturan tes:
import numpy as np
import math
import numba as nb
def first_index_using_argmax(val, arr):return np.argmax(arr > val)def first_index_using_where(val, arr):return np.where(arr > val)[0][0]def first_index_using_nonzero(val, arr):return np.nonzero(arr > val)[0][0]def first_index_using_searchsorted(val, arr):return np.searchsorted(arr, val)+1def first_index_using_min(val, arr):return np.min(np.where(arr > val))def first_index_calculate_range_like(val, arr):if len(arr)==0:raiseValueError('empty array')elif len(arr)==1:if arr[0]> val:return0else:raiseValueError('no value greater than {}'.format(val))
first_value = arr[0]
step = arr[1]- first_value
if step <=0:if first_value > val:return0else:raiseValueError('no value greater than {}'.format(val))
calculated_position =(val - first_value)/ step
if calculated_position <0:return0elif calculated_position > len(arr)-1:raiseValueError('no value greater than {}'.format(val))return int(calculated_position)+1@nb.njit
def first_index_numba(val, arr):for idx in range(len(arr)):if arr[idx]> val:return idx
return-1
funcs =[
first_index_using_argmax,
first_index_using_min,
first_index_using_nonzero,
first_index_calculate_range_like,
first_index_numba,
first_index_using_searchsorted,
first_index_using_where
]from simple_benchmark import benchmark,MultiArgument
dan plot dihasilkan menggunakan:
%matplotlib notebook
b.plot()
item di awal
b = benchmark(
funcs,{2**i:MultiArgument([0, np.arange(2**i)])for i in range(2,20)},
argument_name="array size")
Fungsi numba berkinerja terbaik diikuti oleh fungsi penghitungan dan fungsi yang disortir. Solusi lain berperforma jauh lebih buruk.
barang ada di akhir
b = benchmark(
funcs,{2**i:MultiArgument([2**i-2, np.arange(2**i)])for i in range(2,20)},
argument_name="array size")
Untuk array kecil, fungsi numba berkinerja sangat cepat, namun untuk array yang lebih besar, kinerjanya lebih baik dari fungsi penghitungan dan fungsi yang dicari.
item ada di sqrt (len)
b = benchmark(
funcs,{2**i:MultiArgument([np.sqrt(2**i), np.arange(2**i)])for i in range(2,20)},
argument_name="array size")
Ini lebih menarik. Lagi-lagi numba dan fungsi kalkulasi bekerja sangat baik, namun ini sebenarnya memicu kasus pencarian terburuk yang benar-benar tidak berfungsi dengan baik dalam kasus ini.
Perbandingan fungsi ketika tidak ada nilai yang memenuhi kondisi
Poin menarik lainnya adalah bagaimana fungsi ini berperilaku jika tidak ada nilai yang indeksnya harus dikembalikan:
arr = np.ones(100)
value =2for func in funcs:print(func.__name__)try:print('-->', func(value, arr))exceptExceptionas e:print('-->', e)
Dengan hasil ini:
first_index_using_argmax
-->0
first_index_using_min
--> zero-size array to reduction operation minimum which has no identity
first_index_using_nonzero
--> index 0is out of bounds for axis 0with size 0
first_index_calculate_range_like
--> no value greater than 2
first_index_numba
-->-1
first_index_using_searchsorted
-->101
first_index_using_where
--> index 0is out of bounds for axis 0with size 0
Pencarian yang disortir, argmax, dan numba hanya mengembalikan nilai yang salah. Namun searchsorteddan numbamengembalikan indeks yang bukan indeks yang valid untuk array.
Fungsi where, min, nonzerodan calculatemelemparkan sebuah pengecualian. Namun hanya pengecualian untuk calculatebenar - benar mengatakan sesuatu yang bermanfaat.
Itu berarti kita harus membungkus panggilan ini dalam fungsi wrapper yang sesuai yang menangkap pengecualian atau nilai pengembalian yang tidak valid dan menangani dengan tepat, setidaknya jika Anda tidak yakin apakah nilainya bisa dalam array.
Catatan: Perhitungan dan searchsortedopsi hanya berfungsi dalam kondisi khusus. Fungsi "menghitung" memerlukan langkah konstan dan pencarian disortir membutuhkan array yang akan diurutkan. Jadi ini bisa berguna dalam situasi yang tepat tetapi bukan solusi umum untuk masalah ini. Jika Anda berurusan dengan daftar Python yang diurutkan, Anda mungkin ingin melihat modul bisect daripada menggunakan Numpys yang dicari.
Ini akan mengembalikan indeks terkecil di mana kondisi terpenuhi, sementara mengembalikan infinity jika kondisi tidak pernah terpenuhi (dan wheremengembalikan array kosong).
Jawaban:
Ini sedikit lebih cepat (dan terlihat lebih bagus)
Karena
argmax
akan berhenti pada yang pertamaTrue
("Jika terjadi beberapa kali nilai maksimum, indeks yang sesuai dengan kejadian pertama dikembalikan.") Dan tidak menyimpan daftar lain.sumber
argmax
sepertinya tidak berhenti pada awalnyaTrue
. (Ini dapat diuji dengan membuat array boolean dengan satuTrue
di posisi yang berbeda.) Kecepatan mungkin dijelaskan oleh fakta bahwaargmax
tidak perlu membuat daftar output.argmax
.aa
diurutkan, seperti pada jawaban @ Michael).argmax
pada array 10 juta elemen Boolean dengan satuTrue
di posisi yang berbeda menggunakan NumPy 1.11.2, dan posisi yangTrue
penting. Jadi 1.11.2argmax
tampaknya "hubungan pendek" pada array Boolean.diberikan konten yang diurutkan dari array Anda, ada metode yang lebih cepat: searchsorted .
sumber
+1
dengannp.searchsorted(..., side='right')
side
argumen hanya membuat perbedaan jika ada nilai yang diulang dalam array yang diurutkan. Itu tidak mengubah arti dari indeks yang dikembalikan, yang selalu merupakan indeks tempat Anda dapat memasukkan nilai kueri, menggeser semua entri berikut ke kanan, dan mempertahankan array yang diurutkan.side
memiliki efek ketika nilai yang sama di kedua diurutkan dan array yang dimasukkan, terlepas dari nilai yang diulang di kedua. Nilai berulang dalam array yang disortir hanya melebih-lebihkan efeknya (perbedaan antara sisi adalah berapa kali nilai yang dimasukkan muncul dalam array yang diurutkan).side
tidak mengubah arti dari indeks yang dikembalikan, meskipun itu tidak mengubah array yang dihasilkan dari memasukkan nilai-nilai ke dalam array yang diurutkan pada indeks tersebut. Perbedaan yang halus tapi penting; sebenarnya jawaban ini memberikan indeks yang salah jikaN/2
tidak adaaa
.N/2
tidak ada diaa
. Bentuk yang benar adalahnp.searchsorted(aa, N/2, side='right')
(tanpa+1
). Kedua bentuk memberikan indeks yang sama jika tidak. Pertimbangkan kasus uji untukN
menjadi aneh (danN/2.0
untuk memaksa mengambang jika menggunakan python 2).Saya juga tertarik dengan ini dan saya telah membandingkan semua jawaban yang disarankan dengan perfplot . (Penafian: Saya penulis perfplot.)
Jika Anda tahu bahwa array yang Anda cari sudah diurutkan , maka
adalah untukmu. Ini adalah operasi waktu konstan, yaitu, kecepatan tidak tergantung pada ukuran array. Anda tidak bisa lebih cepat dari itu.
Jika Anda tidak tahu apa-apa tentang array Anda, Anda tidak akan salah
Sudah disortir:
Tidak disortir:
Kode untuk mereproduksi plot:
sumber
np.searchsorted
bukan waktu yang konstan. SebenarnyaO(log(n))
. Tetapi test case Anda benar-benar memberikan tolok ukur pada kasus terbaiksearchsorted
(yaituO(1)
).searchsorted
(atau algoritma apa pun) dapat mengalahkanO(log(n))
pencarian biner untuk data yang didistribusikan secara seragam. EDIT:searchsorted
adalah pencarian biner.sumber
Array yang memiliki langkah konstan antara elemen
Jika
range
array atau peningkatan linear lainnya, Anda dapat menghitung indeks secara terprogram, tidak perlu benar-benar beralih ke array sama sekali:Seseorang mungkin bisa sedikit memperbaiki itu. Saya telah memastikan itu berfungsi dengan benar untuk beberapa sampel array dan nilai-nilai tetapi itu tidak berarti tidak mungkin ada kesalahan di sana, terutama mengingat bahwa ia menggunakan pelampung ...
Mengingat bahwa ia dapat menghitung posisi tanpa iterasi apa pun, itu akan menjadi waktu yang konstan (
O(1)
) dan mungkin dapat mengalahkan semua pendekatan yang disebutkan lainnya. Namun itu membutuhkan langkah konstan dalam array, jika tidak maka akan menghasilkan hasil yang salah.Solusi umum menggunakan numba
Pendekatan yang lebih umum akan menggunakan fungsi numba:
Itu akan bekerja untuk array apa pun tetapi harus beralih di atas array, jadi dalam kasus rata-rata akan menjadi
O(n)
:Tolok ukur
Meskipun Nico Schlömer sudah memberikan beberapa tolok ukur, saya pikir mungkin berguna untuk memasukkan solusi baru saya dan untuk menguji "nilai" yang berbeda.
Pengaturan tes:
dan plot dihasilkan menggunakan:
item di awal
Fungsi numba berkinerja terbaik diikuti oleh fungsi penghitungan dan fungsi yang disortir. Solusi lain berperforma jauh lebih buruk.
barang ada di akhir
Untuk array kecil, fungsi numba berkinerja sangat cepat, namun untuk array yang lebih besar, kinerjanya lebih baik dari fungsi penghitungan dan fungsi yang dicari.
item ada di sqrt (len)
Ini lebih menarik. Lagi-lagi numba dan fungsi kalkulasi bekerja sangat baik, namun ini sebenarnya memicu kasus pencarian terburuk yang benar-benar tidak berfungsi dengan baik dalam kasus ini.
Perbandingan fungsi ketika tidak ada nilai yang memenuhi kondisi
Poin menarik lainnya adalah bagaimana fungsi ini berperilaku jika tidak ada nilai yang indeksnya harus dikembalikan:
Dengan hasil ini:
Pencarian yang disortir, argmax, dan numba hanya mengembalikan nilai yang salah. Namun
searchsorted
dannumba
mengembalikan indeks yang bukan indeks yang valid untuk array.Fungsi
where
,min
,nonzero
dancalculate
melemparkan sebuah pengecualian. Namun hanya pengecualian untukcalculate
benar - benar mengatakan sesuatu yang bermanfaat.Itu berarti kita harus membungkus panggilan ini dalam fungsi wrapper yang sesuai yang menangkap pengecualian atau nilai pengembalian yang tidak valid dan menangani dengan tepat, setidaknya jika Anda tidak yakin apakah nilainya bisa dalam array.
Catatan: Perhitungan dan
searchsorted
opsi hanya berfungsi dalam kondisi khusus. Fungsi "menghitung" memerlukan langkah konstan dan pencarian disortir membutuhkan array yang akan diurutkan. Jadi ini bisa berguna dalam situasi yang tepat tetapi bukan solusi umum untuk masalah ini. Jika Anda berurusan dengan daftar Python yang diurutkan, Anda mungkin ingin melihat modul bisect daripada menggunakan Numpys yang dicari.sumber
Saya ingin melamar
Ini akan mengembalikan indeks terkecil di mana kondisi terpenuhi, sementara mengembalikan infinity jika kondisi tidak pernah terpenuhi (dan
where
mengembalikan array kosong).sumber
Saya akan pergi dengan
di mana
V
vektor (array 1d),x
adalah nilai dani
merupakan indeks yang dihasilkan.sumber