Existe uma maneira numpy-tônica, por exemplo, função, para encontrar o valor mais próximo em uma matriz?
Exemplo:
np.find_nearest( array, value )
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
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 ...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 emidxmin
vez deargmin
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:
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.
fonte
np.searchsorted
leva cerca de 2 µs para o meu conjunto de testes, toda a função cerca de 10 µs. Usandonp.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 quearray
o maior elemento. Alterei aif
declaraçãoif 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, ...):
Ou, escrito como uma única linha:
fonte
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 classificadaarray
e, searray
for grande, considere primeiro usar uma classificação O (n logn) e, em seguida, bissecção, e searray
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:
Agora vou definir o código das outras respostas, cada uma retornando um índice:
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.
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.
fonte
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.
fonte
norm(..., axis=-1)
deveria ser mais rápido do que extrair osx,y
valores através da iteração Python. Além disso,x,y
existem escalares aqui? Entãonorm(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:
fonte
Aqui está uma versão que manipulará uma matriz de "valores" não escalar:
Ou uma versão que retorna um tipo numérico (por exemplo, int, float) se a entrada for escalar:
fonte
outer
método de um ufunc antes, acho que vou usá-lo mais no futuro. A primeira função deve retornararray[indices]
, a propósito.np.subtract.outer
irá gerar toda a matriz do produto externo, que é realmente lenta e consome muita memória searray
e / ouvalues
é 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 "
fonte
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):Benchmarks
> 100 vezes mais rápido do que usar um
for
loop com a solução da @ Demitri`fonte
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.
fonte
x = np.array([2038, 1758, 1721, 1637, 2097, 2047, 2205, 1787, 2287, 1940, 2311, 2054, 2406, 1471, 1460])
. Comfind_nearest(x, 1739.5)
(valor mais próximo do primeiro quantil), recebo1637
(razoável) e1
(bug?).Esta é uma versão vetorizada da resposta de unutbu :
fonte
Eu acho que a maneira mais pitônica seria:
Este é o código básico. Você pode usá-lo como uma função, se quiser
fonte
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.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
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
% 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.
fonte
Para matriz 2d, para determinar a posição i, j do elemento mais próximo:
fonte
fonte
Talvez útil para
ndarrays
:fonte