मेरे पास संख्यात्मक में 1D सरणी है और मैं सूचकांक की स्थिति को ढूंढना चाहता हूं जहां मूल्य एक मान से अधिक होता है।
उदाहरण के लिए
aa = range(-10,10)
उस स्थिति में खोजें aa
जहाँ, मान 5
पार हो जाता है।
मेरे पास संख्यात्मक में 1D सरणी है और मैं सूचकांक की स्थिति को ढूंढना चाहता हूं जहां मूल्य एक मान से अधिक होता है।
उदाहरण के लिए
aa = range(-10,10)
उस स्थिति में खोजें aa
जहाँ, मान 5
पार हो जाता है।
जवाबों:
यह थोड़ा तेज है (और अच्छा लग रहा है)
np.argmax(aa>5)
चूंकि argmax
पहली बार बंद हो जाएगा True
("अधिकतम मूल्यों की कई घटनाओं के मामले में, पहली घटना के अनुरूप सूचकांक वापस आ जाते हैं।") और दूसरी सूची को नहीं बचाता है।
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
पहली बार में बंद नहीं लगता है True
। ( True
विभिन्न पदों पर एकल के साथ बूलियन सरणियों का निर्माण करके इसका परीक्षण किया जा सकता है ।) गति को संभवतः इस तथ्य से समझाया गया है कि argmax
आउटपुट सूची बनाने की आवश्यकता नहीं है।
argmax
।
aa
सॉर्ट किया गया है) @ माइकल के जवाब में)।
argmax
10 मिलियन-एलिमेंट बूलियन एरेज़ पर एक ही True
नंबर पर अलग-अलग स्थानों पर न्यूमपी 1.11.2 का उपयोग करके चला गया, और मामले की स्थिति True
। तो 1.11.2 argmax
बूलियन सरणियों पर "शॉर्ट-सर्किट" लगता है।
आपके सरणी की क्रमबद्ध सामग्री को देखते हुए, एक और भी तेज़ तरीका है: खोजा हुआ ।
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
np.searchsorted(..., side='right')
side
तर्क केवल तभी फर्क करता है जब सॉर्ट किए गए सरणी में दोहराया मान हो। यह लौटे इंडेक्स के अर्थ को नहीं बदलता है, जो हमेशा वह इंडेक्स होता है जिस पर आप क्वेरी मान सम्मिलित कर सकते हैं, सभी प्रविष्टियों को दाईं ओर शिफ्ट कर सकते हैं, और एक सॉर्ट किए गए सरणी को बनाए रख सकते हैं।
side
का प्रभाव तब होता है जब समान मान और सम्मिलित सरणी दोनों में होता है, चाहे किसी भी मान को दोहराया जाए। सॉर्ट किए गए सरणी में बार-बार मान केवल प्रभाव को अतिरंजित करते हैं (पक्षों के बीच का अंतर सॉर्ट किए गए सरणी में डाला जा रहा मूल्य की संख्या की संख्या है)। side
करता है , वापस आ सूचकांक का अर्थ बदल हालांकि यह उन सूचकांक पर क्रमबद्ध सरणी में मानों डालने से उत्पन्न सरणी नहीं बदलता है। एक सूक्ष्म लेकिन महत्वपूर्ण अंतर; वास्तव में यह उत्तर गलत इंडेक्स देता है यदि N/2
वह अंदर नहीं है aa
।
N/2
नहीं है aa
। सही रूप np.searchsorted(aa, N/2, side='right')
(बिना +1
) होगा। दोनों रूप अन्यथा एक ही सूचकांक देते हैं। N
विषम होने के परीक्षण के मामले पर विचार करें (और N/2.0
यदि पायथन 2 का उपयोग करने के लिए फ्लोट को मजबूर करने के लिए)।
मुझे भी इसमें दिलचस्पी थी और मैंने परफ्लोट के साथ सभी सुझाए गए उत्तरों की तुलना की है । (अस्वीकरण: मैं perfplot के लेखक हूँ।)
यदि आप जानते हैं कि जिस सरणी को आप देख रहे हैं वह पहले से ही हल है , तो
numpy.searchsorted(a, alpha)
आप के लिए है। यह एक निरंतर-समय ऑपरेशन है, अर्थात, गति सरणी के आकार पर निर्भर नहीं करती है। आप उससे तीव्र नहीं हो सकते।
यदि आपको अपनी सरणी के बारे में कुछ नहीं पता है, तो आप गलत नहीं होंगे
numpy.argmax(a > alpha)
पहले से ही क्रमबद्ध:
अवर्गीकृत:
प्लॉट को फिर से तैयार करने के लिए कोड:
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
निरंतर समय नहीं है। यह वास्तव में है O(log(n))
। लेकिन आपका परीक्षण मामला वास्तव में searchsorted
(जो है O(1)
) के सर्वश्रेष्ठ मामले को मापता है ।
searchsorted
(या कोई एल्गोरिथ्म) O(log(n))
एक बाइनरी खोज को समान रूप से वितरित डेटा के लिए हरा सकता है । संपादित करें: searchsorted
है एक द्विआधारी खोज।
range
या किसी अन्य रेखीय रूप से बढ़ती हुई सरणी के मामले में आप बस सूचकांक को प्रोग्रामेटिक रूप से गणना कर सकते हैं, वास्तव में सरणी पर इस पर पुनरावृति करने की कोई आवश्यकता नहीं है:
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
शायद वह थोड़ा सुधार कर सके। मुझे यकीन है कि यह कुछ नमूना सरणियों और मूल्यों के लिए सही ढंग से काम करता है, लेकिन इसका मतलब यह नहीं है कि वहाँ गलतियाँ नहीं हो सकती हैं, खासकर यह देखते हुए कि यह तैरता नहीं है ...
>>> 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
यह देखते हुए कि यह किसी भी पुनरावृत्ति के बिना स्थिति की गणना कर सकता है यह निरंतर समय ( O(1)
) होगा और संभवतः अन्य सभी उल्लिखित दृष्टिकोणों को हरा सकता है। हालाँकि इसके लिए सरणी में एक स्थिर कदम की आवश्यकता होती है, अन्यथा यह गलत परिणाम देगा।
एक अधिक सामान्य दृष्टिकोण एक सुन्न समारोह का उपयोग किया जाएगा:
@nb.njit
def first_index_numba(val, arr):
for idx in range(len(arr)):
if arr[idx] > val:
return idx
return -1
यह किसी भी एरे के लिए काम करेगा लेकिन इसे एरे पर चलना होगा, इसलिए औसत स्थिति में यह होगा O(n)
:
>>> first_index_numba(4.8, np.arange(-10, 10))
15
>>> first_index_numba(5, np.arange(-10, 10))
16
भले ही निको श्लोमर ने पहले से ही कुछ बेंचमार्क प्रदान किए हों, मुझे लगा कि यह मेरे नए समाधानों को शामिल करने और विभिन्न "मूल्यों" के लिए परीक्षण करने के लिए उपयोगी हो सकता है।
परीक्षण सेटअप:
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
और भूखंडों का उपयोग कर उत्पन्न किया गया था:
%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")
सुन्न समारोह और समारोह खोज समारोह के बाद सबसे अच्छा प्रदर्शन करता है। अन्य समाधान बहुत खराब प्रदर्शन करते हैं।
b = benchmark(
funcs,
{2**i: MultiArgument([2**i-2, np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
छोटे सरणियों के लिए सुंबा फ़ंक्शन आश्चर्यजनक तेजी से प्रदर्शन करता है, हालांकि बड़ी सरणियों के लिए यह गणना-फ़ंक्शन और खोज किए गए फ़ंक्शन द्वारा बेहतर प्रदर्शन करता है।
b = benchmark(
funcs,
{2**i: MultiArgument([np.sqrt(2**i), np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
यह अधिक दिलचस्प है। फिर से सुब्बा और गणना फ़ंक्शन बहुत अच्छा प्रदर्शन करते हैं, हालांकि यह वास्तव में खोज के सबसे खराब मामले को ट्रिगर कर रहा है जो वास्तव में इस मामले में अच्छी तरह से काम नहीं करता है।
एक और दिलचस्प बात यह है कि ये फ़ंक्शन व्यवहार करता है यदि कोई मूल्य नहीं है जिसका सूचकांक वापस आ जाना चाहिए:
arr = np.ones(100)
value = 2
for func in funcs:
print(func.__name__)
try:
print('-->', func(value, arr))
except Exception as e:
print('-->', e)
इस परिणाम के साथ:
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
खोजे गए, argmax, और सुंबा बस एक गलत मान लौटाते हैं। हालाँकि searchsorted
और numba
एक इंडेक्स लौटाते हैं जो कि ऐरे के लिए मान्य इंडेक्स नहीं है।
कार्यों where
, min
, nonzero
और calculate
एक अपवाद फेंक देते हैं। हालाँकि calculate
वास्तव में मददगार के लिए केवल अपवाद ही कुछ कहता है।
इसका मतलब है कि एक व्यक्ति को वास्तव में इन कॉल को एक उचित आवरण फ़ंक्शन में लपेटना पड़ता है जो अपवाद या अमान्य रिटर्न मान को पकड़ता है और उचित रूप से संभालता है, कम से कम यदि आप सुनिश्चित नहीं हैं कि मान सरणी में हो सकता है।
नोट: गणना और searchsorted
विकल्प केवल विशेष परिस्थितियों में काम करते हैं। "गणना" फ़ंक्शन को एक स्थिर कदम की आवश्यकता होती है और खोजे जाने वाले सरणी को क्रमबद्ध करने की आवश्यकता होती है। तो ये सही परिस्थितियों में उपयोगी हो सकते हैं लेकिन इस समस्या के लिए सामान्य समाधान नहीं हैं । मामले आप साथ काम कर रहे में हल कर अजगर सूचियों आप पर एक नज़र लेने के लिए चाहते हो सकता है द्विविभाजित बजाय Numpys searchsorted का उपयोग करने का मॉड्यूल।