Найти ближайшее значение в массиве numpy
Как мне найти ближайшее значение в массиве numpy? Пример:
np.find_nearest(array, value)
Переведено автоматически
Ответ 1
import numpy as np
def find_nearest(array, value):
array = np.asarray(array)
idx = (np.abs(array - value)).argmin()
return array[idx]
Пример использования:
array = np.random.random(10)
print(array)
# [ 0.21069679 0.61290182 0.63425412 0.84635244 0.91599191 0.00213826
# 0.17104965 0.56874386 0.57319379 0.28719469]
print(find_nearest(array, value=0.5))
# 0.568743859261
Ответ 2
ЕСЛИ ваш массив отсортирован и очень большой, это гораздо более быстрое решение:
def find_nearest(array,value):
idx = np.searchsorted(array, value, side="left")
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
return array[idx-1]
else:
return array[idx]
Это масштабируется до очень больших массивов. Вы можете легко изменить приведенное выше для сортировки в методе, если вы не можете предположить, что массив уже отсортирован. Это перебор для небольших массивов, но как только они становятся большими, это делается намного быстрее.
Ответ 3
С небольшими изменениями приведенный выше ответ работает с массивами произвольной размерности (1d, 2d, 3d, ...):
def find_nearest(a, a0):
"Element in nd array `a` closest to the scalar value `a0`"
idx = np.abs(a - a0).argmin()
return a.flat[idx]
Или, записанный в виде одной строки:
a.flat[np.abs(a - a0).argmin()]
Ответ 4
Краткое изложение ответа: Если у вас есть отсортированное значение array
, то код деления пополам (приведенный ниже) выполняется быстрее всего. В ~ 100-1000 раз быстрее для больших массивов и в ~ 2-100 раз быстрее для небольших массивов. Также не требует numpy. Если у вас есть несортированный массив, array
то, если array
он большой, следует сначала рассмотреть возможность сортировки O (n logn), а затем деления пополам, а если array
он маленький, то метод 2 кажется самым быстрым.
Сначала вам следует уточнить, что вы подразумеваете под ближайшим значением. Часто требуется указать интервал по оси абсцисс, например array= [0,0.7,2.1], value= 1,95, ответом будет idx= 1. Я подозреваю, что это тот случай, который вам нужен (в противном случае следующее можно очень легко изменить с помощью следующего условного оператора, как только вы найдете интервал). Отмечу, что оптимальный способ выполнить это - разделить пополам (который я приведу в первую очередь - обратите внимание, что это вообще не требует numpy и быстрее, чем использование функций numpy, потому что они выполняют избыточные операции). Затем я приведу сравнение времени с другими, представленными здесь другими пользователями.
Разделение пополам:
def bisection(array,value):
'''Given an ``array`` , and given a ``value`` , returns an index j such that ``value`` is between array[j]
and array[j+1]. ``array`` must be monotonic increasing. j=-1 or j=len(array) is returned
to indicate that ``value`` is out of range below and above respectively.'''
n = len(array)
if (value < array[0]):
return -1
elif (value > array[n-1]):
return n
jl = 0# Initialize lower
ju = n-1# and upper limits.
while (ju-jl > 1):# If we are not yet done,
jm=(ju+jl) >> 1# compute a midpoint with a bitshift
if (value >= array[jm]):
jl=jm# and replace either the lower limit
else:
ju=jm# or the upper limit, as appropriate.
# Repeat until the test condition is satisfied.
if (value == array[0]):# edge cases at bottom
return 0
elif (value == array[n-1]):# and top
return n-1
else:
return jl
Теперь я определю код из других ответов, каждый из них возвращает индекс:
import math
import numpy as np
def find_nearest1(array,value):
idx,val = min(enumerate(array), key=lambda x: abs(x[1]-value))
return idx
def find_nearest2(array, values):
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
return indices
def find_nearest3(array, values):
values = np.atleast_1d(values)
indices = np.abs(np.int64(np.subtract.outer(array, values))).argmin(0)
out = array[indices]
return indices
def find_nearest4(array,value):
idx = (np.abs(array-value)).argmin()
return idx
def find_nearest5(array, value):
idx_sorted = np.argsort(array)
sorted_array = np.array(array[idx_sorted])
idx = np.searchsorted(sorted_array, value, side="left")
if idx >= len(array):
idx_nearest = idx_sorted[len(array)-1]
elif idx == 0:
idx_nearest = idx_sorted[0]
else:
if abs(value - sorted_array[idx-1]) < abs(value - sorted_array[idx]):
idx_nearest = idx_sorted[idx-1]
else:
idx_nearest = idx_sorted[idx]
return idx_nearest
def find_nearest6(array,value):
xi = np.argmin(np.abs(np.ceil(array[None].T - value)),axis=0)
return xi
Теперь я буду засекать коды:
Примечание методы 1,2,4,5 неправильно задают интервал. Методы 1,2,4 округляют до ближайшей точки в массиве (например, >= 1.5 -> 2), а метод 5 всегда округляет в большую сторону (например, 1.45 -> 2). Только методы 3 и 6 и, конечно, деление пополам дают правильный интервал.
array = np.arange(100000)
val = array[50000]+0.55
print( bisection(array,val))
%timeit bisection(array,val)
print( find_nearest1(array,val))
%timeit find_nearest1(array,val)
print( find_nearest2(array,val))
%timeit find_nearest2(array,val)
print( find_nearest3(array,val))
%timeit find_nearest3(array,val)
print( find_nearest4(array,val))
%timeit find_nearest4(array,val)
print( find_nearest5(array,val))
%timeit find_nearest5(array,val)
print( find_nearest6(array,val))
%timeit find_nearest6(array,val)
(50000, 50000)
100000 loops, best of 3: 4.4 µs per loop
50001
1 loop, best of 3: 180 ms per loop
50001
1000 loops, best of 3: 267 µs per loop
[50000]
1000 loops, best of 3: 390 µs per loop
50001
1000 loops, best of 3: 259 µs per loop
50001
1000 loops, best of 3: 1.21 ms per loop
[50000]
1000 loops, best of 3: 746 µs per loop
Для большого массива деление пополам дает 4 единицы по сравнению со следующими лучшими 180 единицами и длится 1,21 мс (в ~ 100-1000 раз быстрее). Для массивов меньшего размера это в ~ 2-100 раз быстрее.