Saya memiliki array 1D di numpy dan saya ingin menemukan posisi indeks di mana nilai melebihi nilai dalam array numpy.
Misalnya
aa = range(-10,10)
Temukan posisi di aamana, nilai 5terlampaui.
Saya memiliki array 1D di numpy dan saya ingin menemukan posisi indeks di mana nilai melebihi nilai dalam array numpy.
Misalnya
aa = range(-10,10)
Temukan posisi di aamana, nilai 5terlampaui.
Jawaban:
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 = 10000
In [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
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.
argmax.
aadiurutkan, seperti pada jawaban @ Michael).
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.
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]
# Output
100000 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
+1dengannp.searchsorted(..., side='right')
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.
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). 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 jika N/2tidak ada aa.
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).
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
numpy.argmax(a > alpha)
Sudah disortir:
Tidak disortir:
Kode untuk mereproduksi plot:
import numpy
import perfplot
alpha = 0.5
def argmax(data):
return numpy.argmax(data > alpha)
def where(data):
return numpy.where(data > alpha)[0][0]
def nonzero(data):
return numpy.nonzero(data > alpha)[0][0]
def searchsorted(data):
return numpy.searchsorted(data, alpha)
out = perfplot.show(
# setup=numpy.random.rand,
setup=lambda n: numpy.sort(numpy.random.rand(n)),
kernels=[
argmax, where,
nonzero,
searchsorted
],
n_range=[2**k for k in range(2, 20)],
logx=True,
logy=True,
xlabel='len(array)'
)
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)).
searchsorted(atau algoritma apa pun) dapat mengalahkan O(log(n))pencarian biner untuk data yang didistribusikan secara seragam. EDIT: searchsorted adalah pencarian biner.
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:
raise ValueError('no value greater than {}'.format(val))
elif len(arr) == 1:
if arr[0] > val:
return 0
else:
raise ValueError('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:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
calculated_position = (val - first_value) / step
if calculated_position < 0:
return 0
elif calculated_position > len(arr) - 1:
raise ValueError('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 ...
>>> import numpy as np
>>> first_index_calculate_range_like(5, np.arange(-10, 10))
16
>>> np.arange(-10, 10)[16] # double check
6
>>> first_index_calculate_range_like(4.8, np.arange(-10, 10))
15
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.
Pendekatan yang lebih umum akan menggunakan fungsi numba:
@nb.njit
def first_index_numba(val, arr):
for idx in range(len(arr)):
if arr[idx] > val:
return idx
return -1
Itu akan bekerja untuk array apa pun tetapi harus beralih di atas array, jadi dalam kasus rata-rata akan menjadi O(n):
>>> first_index_numba(4.8, np.arange(-10, 10))
15
>>> first_index_numba(5, np.arange(-10, 10))
16
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) + 1
def 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:
raise ValueError('empty array')
elif len(arr) == 1:
if arr[0] > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
first_value = arr[0]
step = arr[1] - first_value
if step <= 0:
if first_value > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
calculated_position = (val - first_value) / step
if calculated_position < 0:
return 0
elif calculated_position > len(arr) - 1:
raise ValueError('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()
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.
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.
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.
Poin menarik lainnya adalah bagaimana fungsi ini berperilaku jika tidak ada nilai yang indeksnya harus dikembalikan:
arr = np.ones(100)
value = 2
for func in funcs:
print(func.__name__)
try:
print('-->', func(value, arr))
except Exception as 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 0 is out of bounds for axis 0 with 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 0 is out of bounds for axis 0 with 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.