Numpy'de 1D dizisi var ve bir değerin numpy dizisindeki değeri aştığı dizinin konumunu bulmak istiyorum.
Örneğin
aa = range(-10,10)
aa
Değerin 5
aşıldığı konumu bulun .
Numpy'de 1D dizisi var ve bir değerin numpy dizisindeki değeri aştığı dizinin konumunu bulmak istiyorum.
Örneğin
aa = range(-10,10)
aa
Değerin 5
aşıldığı konumu bulun .
Yanıtlar:
Bu biraz daha hızlı (ve daha güzel görünüyor)
np.argmax(aa>5)
Yana argmax
ilk olacak durağı True
( "azami değerine birden fazla kopyasını durumunda, ilk oluşumuna karşılık gelen endeksler. Döndürülür") ve başka bir liste tasarruf değil.
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
argmax
ilk başta durmuş gibi görünmüyor True
. (Bu True
, farklı konumlarda tekli boole dizileri oluşturularak test edilebilir .) Hız, muhtemelen argmax
bir çıktı listesi oluşturmaya gerek olmadığı gerçeğiyle açıklanır .
argmax
.
aa
, sıralanır @ Michael'ın cevabında olduğu gibi).
argmax
tek bir ile 10 milyon elementli Boole dizileri True
üzerinde çalıştım ve madde pozisyonu True
. Yani 1.11.2'ler argmax
Boole dizilerinde "kısa devre" yapıyor.
dizinizin sıralı içeriği göz önüne alındığında, daha da hızlı bir yöntem vardır: aranan .
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
+1
ilenp.searchsorted(..., side='right')
side
Argüman sadece sıralanmış dizide tekrarlanan değerler varsa bir fark yaratıyor düşünüyorum . Döndürülen dizinin anlamını değiştirmez; bu, her zaman sorgu değerini ekleyebileceğiniz dizindir, aşağıdaki tüm girişleri sağa kaydırarak ve sıralı bir diziyi korur.
side
her ikisinde de yinelenen değerlere bakılmaksızın , aynı değer hem sıralanmış hem de eklenen dizide olduğunda bir etkiye sahiptir . Sıralanan dizideki yinelenen değerler efekti abartır (kenarlar arasındaki fark, eklenen değerin sıralanmış dizide görüntülenme sayısıdır). side
gelmez bu o endekslerin en sıralanmış diziye değerleri ekleme kaynaklanan dizi değişmez rağmen iade indeksi anlamını değiştirir. İnce ama önemli bir ayrım; Aslında bu cevap yanlış endeksi verir N/2
değildir aa
.
N/2
değil aa
. Doğru form np.searchsorted(aa, N/2, side='right')
(olmadan +1
) olurdu . Her iki form da aynı dizini verir. Test durumunun N
garip olduğunu düşünün (ve N/2.0
python 2 kullanıyorsanız şamandırayı zorlayın).
Ben de bu ilgimi çekti ve önerilen tüm cevapları perfplot ile karşılaştırdım . (Feragatname: Ben perfplot'un yazarıyım.)
Aradığınız dizinin zaten sıralandığını biliyorsanız ,
numpy.searchsorted(a, alpha)
senin için. Bu sabit zamanlı bir işlemdir, yani hız dizinin boyutuna bağlı değildir . Bundan daha hızlı olamazsın.
Diziniz hakkında hiçbir şey bilmiyorsanız, yanlış gitmezsiniz
numpy.argmax(a > alpha)
Zaten sıralanmış:
Sınıflandırılmamış:
Grafiği yeniden oluşturmak için kod:
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.searchsorted
sabit zaman değildir. Aslında O(log(n))
. Ancak test durumunuz aslında searchsorted
(ki O(1)
) en iyi durumunu karşılaştırır .
searchsorted
(veya herhangi bir algoritma) O(log(n))
sıralı düzgün dağıtılmış veri için bir ikili arama yenebilir . DÜZENLEME: searchsorted
olan bir ikili arama.
Bir range
veya doğrusal olarak artan başka bir dizi durumunda , dizini programlı olarak hesaplayabilirsiniz, aslında dizi üzerinde yinelemeye gerek yoktur:
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
Muhtemelen bunu biraz geliştirebiliriz. Birkaç örnek dizisi ve değeri için doğru çalıştığından emin oldum, ancak bu, özellikle şamandıralar kullandığını göz önünde bulundurarak, orada hataların olmayacağı anlamına gelmez ...
>>> 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
Herhangi bir yineleme olmadan pozisyonu hesaplayabildiği göz önüne alındığında, sabit zaman ( O(1)
) olacak ve muhtemelen diğer tüm yaklaşımları yenebilir. Ancak dizide sabit bir adım gerektirir, aksi takdirde yanlış sonuçlar verir.
Daha genel bir yaklaşım bir numba işlevi kullanmak olacaktır:
@nb.njit
def first_index_numba(val, arr):
for idx in range(len(arr)):
if arr[idx] > val:
return idx
return -1
Bu herhangi bir dizi için çalışacaktır, ancak dizi üzerinde yineleme yapmak zorundadır, bu nedenle ortalama durumda O(n)
:
>>> first_index_numba(4.8, np.arange(-10, 10))
15
>>> first_index_numba(5, np.arange(-10, 10))
16
Nico Schlömer zaten bazı ölçütler sağlamasına rağmen yeni çözümlerimi eklemenin ve farklı "değerleri" test etmenin yararlı olabileceğini düşündüm.
Test kurulumu:
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
ve araziler aşağıdakiler kullanılarak üretildi:
%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")
Numba işlevi en iyi performansı, ardından hesapla işlevi ve aranan işlevi izler. Diğer çözümler çok daha kötü performans gösterir.
b = benchmark(
funcs,
{2**i: MultiArgument([2**i-2, np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
Küçük diziler için numba işlevi inanılmaz derecede hızlı performans gösterir, ancak daha büyük diziler için hesapla işlevi ve aranan işlevden daha iyi performans gösterir.
b = benchmark(
funcs,
{2**i: MultiArgument([np.sqrt(2**i), np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
Bu daha ilginç. Yine numba ve hesaplama fonksiyonu harika performans gösterir, ancak bu aslında bu durumda gerçekten iyi çalışmayan en kötü arama aramasını tetikler.
Bir başka ilginç nokta da, dizini döndürülmesi gereken bir değer yoksa bu işlevin nasıl davrandığıdır:
arr = np.ones(100)
value = 2
for func in funcs:
print(func.__name__)
try:
print('-->', func(value, arr))
except Exception as e:
print('-->', e)
Bu sonuçla:
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
Aranan, argmax ve numba sadece yanlış bir değer döndürür. Ancak searchsorted
ve numba
dizi için geçerli bir dizin değil bir dizin döndürür.
Fonksiyonlar where
, min
, nonzero
ve calculate
bir özel durum. Ancak sadece istisna calculate
aslında yararlı bir şey söylüyor.
Bu, en azından değerin dizide olup olmadığından emin değilseniz, bu çağrıları istisnaları veya geçersiz dönüş değerlerini yakalayan ve uygun şekilde işleyen uygun bir sarmalayıcı işlevine sarmak zorunda olduğu anlamına gelir.
Not: Hesaplama ve searchsorted
seçenekler yalnızca özel koşullarda çalışır. "Hesapla" işlevi sabit bir adım gerektirir ve aranan dizinin sıralanmasını gerektirir. Dolayısıyla bunlar doğru koşullarda yararlı olabilir, ancak bu soruna genel bir çözüm değildir . Uğraştığın durumunda sıralanmış Python listelenmektedir bir göz atmak isteyebilirsiniz kenarortay yerine Numpys searchsorted kullanmanın modülü.