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 idxminvez de argminfunciona 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.searchsortedleva cerca de 2 µs para o meu conjunto de testes, toda a função cerca de 10 µs. Usando np.absestá ficando ainda pior. Nenhuma pista do que o python está fazendo lá.
mathrotinas, consulte esta resposta .
if/elseprecisa ser substituído comidx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
valuefor maior que arrayo maior elemento. Alterei a ifdeclaraçã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 arraye, se arrayfor grande, considere primeiro usar uma classificação O (n logn) e, em seguida, bissecção, e se arrayfor 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.
arrayfor 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,yvalores através da iteração Python. Além disso, x,yexistem 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]
outermé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.outerirá gerar toda a matriz do produto externo, que é realmente lenta e consome muita memória se arraye / 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 valuespara pesquisar ( valuespode 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 forloop 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]-=1e 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 bisectmó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 acrescentarvaluee chegar perto, mas o valor absoluto joga uma chave para as coisas ...