Відповіді:
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]
value = 0.5
print(find_nearest(array, value))
# 0.568743859261
FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.
Використання idxmin
замість цього argmin
працює для мене з рішенням вище. (v3.6.4)
ЯКЩО ваш масив відсортований і дуже великий, це набагато швидше рішення:
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]
Це масштабується до дуже великих масивів. Ви можете легко змінити вищезазначене для сортування в методі, якщо ви не можете припустити, що масив вже відсортований. Для невеликих масивів це надмірно, але коли вони набудуть великого розміру, це набагато швидше.
np.searchsorted
Для мого тестового набору рівнина займає приблизно 2 мкс, вся функція - близько 10 мкс. Використання np.abs
стає ще гірше. Немає поняття, що пітон там робить.
math
підпрограми, див. Цю відповідь .
if/else
потрібно замінити наidx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
value
більший за array
найбільший елемент '. Я змінив if
заяву, if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
щоб змусити мене працювати!
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
З невеликою модифікацією відповідь вище працює з масивами довільного розміру (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()]
a[np.abs(a-a0).argmin)]
працює чудово.
a[np.sum(np.square(np.abs(a-a0)),1).argmin()]
.
Короткий зміст відповіді : Якщо впорядковано, array
то бісекційний код (наведений нижче) виконує найшвидше. ~ 100-1000 разів швидше для великих масивів і ~ 2-100 разів швидше для малих масивів. Він також не вимагає нумету. Якщо у вас несортовано, array
тоді, якщо array
він великий, слід спершу використати сортування O (n logn), а потім бісекцію, а якщо array
мало, то метод 2 здається найшвидшим.
Спочатку слід уточнити, що ви маєте на увазі під найближчим значенням . Часто хочеться, щоб інтервал в абсцисі, наприклад, масив = [0,0.7,2.1], значення = 1,95, відповідь буде idx = 1. Я вважаю, що це вам потрібно (інакше наступне можна легко змінити за допомогою наступного умовного твердження, як тільки ви знайдете інтервал). Зауважу, що оптимальним способом для цього є бісекція (яку я надам по-перше - зауважте, що вона взагалі не потребує нумерування та швидша, ніж використання функцій 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
Для великого масиву бісекція дає 4us порівняно з наступним кращим 180us і найдовшим 1,21ms (~ 100 - 1000 разів швидше). Для менших масивів це ~ 2-100 разів швидше.
array
мало, то метод 2 здається найшвидшим". як малий ти мав на увазі @JoshAlbert?
Ось розширення для пошуку найближчого вектора в масиві векторів.
import numpy as np
def find_nearest_vector(array, value):
idx = np.array([np.linalg.norm(x+y) for (x,y) in array-value]).argmin()
return array[idx]
A = np.random.random((10,2))*100
""" A = array([[ 34.19762933, 43.14534123],
[ 48.79558706, 47.79243283],
[ 38.42774411, 84.87155478],
[ 63.64371943, 50.7722317 ],
[ 73.56362857, 27.87895698],
[ 96.67790593, 77.76150486],
[ 68.86202147, 21.38735169],
[ 5.21796467, 59.17051276],
[ 82.92389467, 99.90387851],
[ 6.76626539, 30.50661753]])"""
pt = [6, 30]
print find_nearest_vector(A,pt)
# array([ 6.76626539, 30.50661753])
norm(..., axis=-1)
має бути швидше, ніж вилучення x,y
значень за допомогою ітерації Python. Також x,y
тут скаляри? Тоді norm(x+y)
помилка, оскільки, наприклад, відстань (+1, -1)
буде розглядатись як 0.
idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
Якщо ви не хочете використовувати numpy, зробіть це:
def find_nearest(array, value):
n = [abs(i-value) for i in array]
idx = n.index(min(n))
return array[idx]
Ось версія, яка буде обробляти не скалярний масив "значень":
import numpy as np
def find_nearest(array, values):
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
return array[indices]
Або версія, яка повертає числовий тип (наприклад, int, float), якщо вхід скалярний:
def find_nearest(array, values):
values = np.atleast_1d(values)
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
out = array[indices]
return out if len(out) > 1 else out[0]
outer
метод ufunc, думаю, буду використовувати його більше в майбутньому. Перша функція повинна array[indices]
, до речі, повертатися .
np.subtract.outer
буде генерувати всю матрицю зовнішнього продукту, яка є дуже повільною і об'ємною, якщо array
та / або values
дуже велика.
Ось версія з scipy для @Ari Onasafari, відповідь " знайти найближчий вектор у масиві векторів "
In [1]: from scipy import spatial
In [2]: import numpy as np
In [3]: A = np.random.random((10,2))*100
In [4]: A
Out[4]:
array([[ 68.83402637, 38.07632221],
[ 76.84704074, 24.9395109 ],
[ 16.26715795, 98.52763827],
[ 70.99411985, 67.31740151],
[ 71.72452181, 24.13516764],
[ 17.22707611, 20.65425362],
[ 43.85122458, 21.50624882],
[ 76.71987125, 44.95031274],
[ 63.77341073, 78.87417774],
[ 8.45828909, 30.18426696]])
In [5]: pt = [6, 30] # <-- the point to find
In [6]: A[spatial.KDTree(A).query(pt)[1]] # <-- the nearest point
Out[6]: array([ 8.45828909, 30.18426696])
#how it works!
In [7]: distance,index = spatial.KDTree(A).query(pt)
In [8]: distance # <-- The distances to the nearest neighbors
Out[8]: 2.4651855048258393
In [9]: index # <-- The locations of the neighbors
Out[9]: 9
#then
In [10]: A[index]
Out[10]: array([ 8.45828909, 30.18426696])
Ось швидка векторизована версія рішення @ Димитрі, якщо вам потрібно багато values
шукати ( values
може бути багатовимірний масив):
#`values` should be sorted
def get_closest(array, values):
#make sure array is a numpy array
array = np.array(array)
# get insert positions
idxs = np.searchsorted(array, values, side="left")
# find indexes where previous index is closer
prev_idx_is_less = ((idxs == len(array))|(np.fabs(values - array[np.maximum(idxs-1, 0)]) < np.fabs(values - array[np.minimum(idxs, len(array)-1)])))
idxs[prev_idx_is_less] -= 1
return array[idxs]
Орієнтири
> У 100 разів швидше, ніж використання for
циклу з рішенням @ Demitri '
>>> %timeit ar=get_closest(np.linspace(1, 1000, 100), np.random.randint(0, 1050, (1000, 1000)))
139 ms ± 4.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit ar=[find_nearest(np.linspace(1, 1000, 100), value) for value in np.random.randint(0, 1050, 1000*1000)]
took 21.4 seconds
idx = np.searchsorted(array, values)
тоді: idx[array[idx] - values>np.diff(array).mean()*0.5]-=1
і нарештіreturn array[idx]
Для великих масивів (відмінна) відповідь, яку дав @Demitri, набагато швидша, ніж відповідь, яка в даний час позначена як найкраща. Я адаптував його точний алгоритм наступними двома способами:
Функція нижче працює, сортуючи вхідний масив чи ні.
Функція нижче повертає індекс вхідного масиву, що відповідає найближчому значенню, яке дещо більш загальне.
Зауважте, що функція нижче також обробляє конкретний край, який може призвести до помилки в початковій функції, написаній @Demitri. Інакше мій алгоритм ідентичний його.
def find_idx_nearest_val(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
x = np.array([2038, 1758, 1721, 1637, 2097, 2047, 2205, 1787, 2287, 1940, 2311, 2054, 2406, 1471, 1460])
. З find_nearest(x, 1739.5)
(найближче значення до першого квантиля) я отримую 1637
(розумне) і 1
(помилка?).
Це векторизована версія відповіді унутбу :
def find_nearest(array, values):
array = np.asarray(array)
# the last dim must be 1 to broadcast in (array - values) below.
values = np.expand_dims(values, axis=-1)
indices = np.abs(array - values).argmin(axis=-1)
return array[indices]
image = plt.imread('example_3_band_image.jpg')
print(image.shape) # should be (nrows, ncols, 3)
quantiles = np.linspace(0, 255, num=2 ** 2, dtype=np.uint8)
quantiled_image = find_nearest(quantiles, image)
print(quantiled_image.shape) # should be (nrows, ncols, 3)
Я думаю, що найбільш пітонічним способом було б:
num = 65 # Input number
array = n.random.random((10))*100 # Given array
nearest_idx = n.where(abs(array-num)==abs(array-num).min())[0] # If you want the index of the element of array (array) nearest to the the given number (num)
nearest_val = array[abs(array-num)==abs(array-num).min()] # If you directly want the element of array (array) nearest to the given number (num)
Це основний код. Ви можете використовувати його як функцію, якщо хочете
Усі відповіді корисні для збору інформації для написання ефективного коду. Однак я написав невеликий сценарій Python для оптимізації для різних випадків. Найкраще буде, якщо поданий масив буде відсортований. Якщо шукати індекс найближчої точки визначеного значення, bisect
модуль є найбільш ефективним у часі. Коли за одним пошуком індекси відповідають масиву, numpy searchsorted
найефективнішим є.
import numpy as np
import bisect
xarr = np.random.rand(int(1e7))
srt_ind = xarr.argsort()
xar = xarr.copy()[srt_ind]
xlist = xar.tolist()
bisect.bisect_left(xlist, 0.3)
In [63]:% time bisect.bisect_left (xlist, 0,3) CPU разів: користувач 0 ns, sys: 0 ns, всього: 0 ns Час стіни: 22,2 µs
np.searchsorted(xar, 0.3, side="left")
У [64]:% час np.searchsorted (xar, 0,3, сторона = "ліворуч") CPU разів: користувач 0 ns, sys: 0 ns, всього: 0 ns Час стіни: 98,9 µs
randpts = np.random.rand(1000)
np.searchsorted(xar, randpts, side="left")
% час np.searchsorted (xar, randpts, side = "зліва") Часи процесора: користувач 4 мс, систем: 0 нс, всього: 4 мс Час стіни: 1,2 мс
Якщо ми дотримуємось мультиплікативного правила, то numpy повинен приймати ~ 100 мс, що означає ~ 83X швидше.
Для масиву 2d для визначення i, j положення найближчого елемента:
import numpy as np
def find_nearest(a, a0):
idx = (np.abs(a - a0)).argmin()
w = a.shape[1]
i = idx // w
j = idx - i * w
return a[i,j], i, j
import numpy as np
def find_nearest(array, value):
array = np.array(array)
z=np.abs(array-value)
y= np.where(z == z.min())
m=np.array(y)
x=m[0,0]
y=m[1,0]
near_value=array[x,y]
return near_value
array =np.array([[60,200,30],[3,30,50],[20,1,-50],[20,-500,11]])
print(array)
value = 0
print(find_nearest(array, value))
Може бути корисним для ndarrays
:
def find_nearest(X, value):
return X[np.unravel_index(np.argmin(np.abs(X - value)), X.shape)]
return np.abs(array-value).min()
дає неправильну відповідь. Це дає вам хв абсолютної величини відстані, і нам якось потрібно повернути фактичне значення масиву. Ми могли б додатиvalue
і наблизитись, але абсолютна величина кидає ключ до речей ...