Respostas:
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.
Usar em idxmin
vez de argmin
funciona para mim com a solução acima. (v3.6.4)
Se sua matriz é classificada e é muito grande, esta é uma solução muito mais rápida:
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]
Isso é dimensionado para matrizes muito grandes. Você pode modificar facilmente o que foi descrito acima para classificar o método se não puder assumir que a matriz já está classificada. É um exagero para pequenas matrizes, mas uma vez que elas crescem, isso é muito mais rápido.
np.searchsorted
leva cerca de 2 µs para o meu conjunto de testes, toda a função cerca de 10 µs. Usando np.abs
está ficando ainda pior. Nenhuma pista do que o python está fazendo lá.
math
rotinas, consulte esta resposta .
if/else
precisa ser substituído comidx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
value
for maior que array
o maior elemento. Alterei a if
declaração if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
para fazê-la funcionar para mim!
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
Com pequenas modificações, a resposta acima funciona com matrizes de dimensão arbitrária (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]
Ou, escrito como uma única linha:
a.flat[np.abs(a - a0).argmin()]
a[np.abs(a-a0).argmin)]
funciona bem.
a[np.sum(np.square(np.abs(a-a0)),1).argmin()]
.
Resumo da resposta : se alguém tiver uma ordenada array
, o código de bissecção (fornecido abaixo) executa o mais rápido. ~ 100-1000 vezes mais rápido para matrizes grandes e ~ 2-100 vezes mais rápido para matrizes pequenas. Também não requer dormência. Se você tiver uma classificação não classificada array
e, se array
for grande, considere primeiro usar uma classificação O (n logn) e, em seguida, bissecção, e se array
for pequena, o método 2 parecerá o mais rápido.
Primeiro, você deve esclarecer o que você quer dizer com valor mais próximo . Freqüentemente, se deseja o intervalo em uma abcissa, por exemplo, array = [0,0,7,2.1], valor = 1,95, a resposta seria idx = 1. Suspeito que você precise desse caso (caso contrário, o seguinte pode ser modificado com muita facilidade com uma instrução condicional de acompanhamento depois que você encontrar o intervalo). Observarei que a melhor maneira de fazer isso é com a bissecção (que fornecerei primeiro - note que não requer numpy e é mais rápido do que usar funções numpy porque elas executam operações redundantes). Então, fornecerei uma comparação de tempo com as outras apresentadas aqui por outros usuários.
Bissecção:
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
Agora vou definir o código das outras respostas, cada uma retornando um índice:
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
Agora cronometrarei os códigos: Observe que os métodos 1,2,4,5 não fornecem o intervalo corretamente. Os métodos 1,2,4 arredondam para o ponto mais próximo na matriz (por exemplo,> = 1,5 -> 2) e o método 5 sempre arredonda para cima (por exemplo, 1,45 -> 2). Somente os métodos 3, 6 e, é claro, a bissecção fornecem o intervalo corretamente.
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
Para uma grande matriz, a bissecção fornece 4us em comparação aos próximos melhores 180us e 1,21ms mais longos (~ 100 - 1000 vezes mais rápido). Para matrizes menores, é ~ 2-100 vezes mais rápido.
array
for pequeno, o método 2 parece o mais rápido". quão pequeno você quis dizer @ JoshAlbert?
Aqui está uma extensão para encontrar o vetor mais próximo em uma matriz de vetores.
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)
deveria ser mais rápido do que extrair os x,y
valores através da iteração Python. Além disso, x,y
existem escalares aqui? Então norm(x+y)
é um bug, já que, por exemplo, a distância (+1, -1)
será tratada como 0.
idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
Se você não quiser usar o numpy, isso será feito:
def find_nearest(array, value):
n = [abs(i-value) for i in array]
idx = n.index(min(n))
return array[idx]
Aqui está uma versão que manipulará uma matriz de "valores" não escalar:
import numpy as np
def find_nearest(array, values):
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
return array[indices]
Ou uma versão que retorna um tipo numérico (por exemplo, int, float) se a entrada for escalar:
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
método de um ufunc antes, acho que vou usá-lo mais no futuro. A primeira função deve retornar array[indices]
, a propósito.
np.subtract.outer
irá gerar toda a matriz do produto externo, que é realmente lenta e consome muita memória se array
e / ou values
é muito grande.
Aqui está uma versão com scipy para @Ari Onasafari, responda " para encontrar o vetor mais próximo em uma matriz de vetores "
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])
Aqui está uma versão vetorizada rápida da solução do @ Dimitri, se você tiver muitos values
para pesquisar ( values
pode ser um array multidimensional):
#`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]
Benchmarks
> 100 vezes mais rápido do que usar um for
loop com a solução da @ 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)
então: idx[array[idx] - values>np.diff(array).mean()*0.5]-=1
e finalmentereturn array[idx]
Para matrizes grandes, a resposta (excelente) dada por @Demitri é muito mais rápida que a resposta atualmente marcada como melhor. Eu adaptei o algoritmo exato das duas maneiras a seguir:
A função abaixo funciona se a matriz de entrada é ou não classificada.
A função abaixo retorna o índice da matriz de entrada correspondente ao valor mais próximo, que é um pouco mais geral.
Observe que a função abaixo também lida com um caso de borda específico que levaria a um erro na função original escrita por @Demitri. Caso contrário, meu algoritmo é idêntico ao dele.
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])
. Com find_nearest(x, 1739.5)
(valor mais próximo do primeiro quantil), recebo 1637
(razoável) e 1
(bug?).
Esta é uma versão vetorizada da resposta de 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)
Eu acho que a maneira mais pitônica seria:
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)
Este é o código básico. Você pode usá-lo como uma função, se quiser
Todas as respostas são benéficas para reunir as informações para escrever um código eficiente. No entanto, escrevi um pequeno script Python para otimizar para vários casos. Será o melhor caso se a matriz fornecida for classificada. Se alguém pesquisar o índice do ponto mais próximo de um valor especificado, o bisect
módulo será o mais eficiente em termos de tempo. Quando uma pesquisa nos índices corresponde a uma matriz, numpy searchsorted
é mais eficiente.
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)
Em [63]:% de tempo bisect.bisect_left (xlist, 0,3) Tempo de CPU: usuário 0 ns, sys: 0 ns, total: 0 ns Tempo de parede: 22,2 µs
np.searchsorted(xar, 0.3, side="left")
Em [64]:% time np.searchsorted (xar, 0,3, side = "left") tempos de CPU: usuário 0 ns, sys: 0 ns, total: 0 ns tempo de parede: 98,9 µs
randpts = np.random.rand(1000)
np.searchsorted(xar, randpts, side="left")
% time np.searchsorted (xar, randpts, side = "left") Tempos de CPU: usuário 4 ms, sys: 0 ns, total: 4 ms Tempo de parede: 1,2 ms
Se seguirmos a regra multiplicativa, numpy deve demorar ~ 100 ms, o que implica ~ 83X mais rápido.
Para matriz 2d, para determinar a posição i, j do elemento mais próximo:
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))
Talvez útil para 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()
dá a resposta errada. Isso fornece o mínimo da distância do valor absoluto e, de alguma forma, precisamos retornar o valor real da matriz. Poderíamos acrescentarvalue
e chegar perto, mas o valor absoluto joga uma chave para as coisas ...