Найти ближайшее значение в массиве NumPy - PullRequest
277 голосов
/ 02 апреля 2010

Есть ли "тупой" способ, например, функция, чтобы найти ближайшее значение в массиве?

Пример:

np.find_nearest( array, value )

Ответы [ 16 ]

441 голосов
/ 02 апреля 2010
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
67 голосов
/ 25 сентября 2014

IF ваш массив отсортирован и очень большой, это гораздо более быстрое решение:

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]

Это масштабируется до очень больших массивов. Вы можете легко изменить вышеупомянутое для сортировки в методе, если вы не можете предположить, что массив уже отсортирован. Для небольших массивов это излишне, но как только они становятся большими, это намного быстрее.

43 голосов
/ 06 мая 2012

С небольшой модификацией ответ выше работает с массивами произвольной размерности (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()]
17 голосов
/ 25 января 2017

Сводка ответа : Если у вас есть отсортированный array, то код деления пополам (приведенный ниже) выполняется быстрее всего. ~ 100-1000 раз быстрее для больших массивов и ~ 2-100 раз быстрее для маленьких массивов. Это также не требует NumPy. Если у вас есть несортированная array, то, если array велико, следует сначала рассмотреть использование сортировки O (n logn), а затем разделить пополам, а если array мало, то метод 2 кажется самым быстрым.

Сначала вы должны уточнить, что вы подразумеваете под ближайшим значением . Часто требуется интервал в абсциссе, например, массив = [0,0.7,2.1], значение = 1,95, ответом будет idx = 1. Я подозреваю, что это именно тот случай (в противном случае следующее очень легко можно изменить с помощью условного оператора последующего действия, как только вы найдете интервал). Я отмечу, что оптимальный способ сделать это - разделить пополам (что я предоставлю первым - заметьте, что он вообще не требует numpy и работает быстрее, чем использование numpy функций, поскольку они выполняют избыточные операции). Затем я приведу сравнение времени с другими, представленными здесь другими пользователями.

Bisection:

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,21 мс (~ 100 - 1000 раз быстрее). Для небольших массивов это в 2-100 раз быстрее.

16 голосов
/ 16 июля 2013

Вот расширение, чтобы найти ближайший вектор в массиве векторов.

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])
9 голосов
/ 29 августа 2013

Если вы не хотите использовать numpy, это сделает это:

def find_nearest(array, value):
    n = [abs(i-value) for i in array]
    idx = n.index(min(n))
    return array[idx]
9 голосов
/ 11 февраля 2013

Вот версия, которая будет обрабатывать нескалярный массив «значений»:

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]
8 голосов
/ 23 сентября 2015

Вот версия со 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])
7 голосов
/ 08 апреля 2015

Для больших массивов (превосходный) ответ, данный @Demitri, намного быстрее, чем ответ, отмеченный в настоящее время как лучший. Я адаптировал его точный алгоритм следующими двумя способами:

  1. Функция ниже работает независимо от того, отсортирован ли входной массив.

  2. Приведенная ниже функция возвращает index входного массива, соответствующего ближайшему значению, которое несколько более общее.

Обратите внимание, что функция ниже также обрабатывает определенный крайний случай, который может привести к ошибке в исходной функции, написанной @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
3 голосов
/ 02 ноября 2018

Это векторизованная версия ответа unutbu :

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...