Como posso encontrar o índice da primeira ocorrência de um número em uma matriz Numpy? A velocidade é importante para mim. Não estou interessado nas seguintes respostas porque elas examinam todo o array e não param quando encontram a primeira ocorrência:
itemindex = numpy.where(array==item)[0][0]
nonzero(array == item)[0][0]
Nota 1: nenhuma das respostas dessa pergunta parece relevante. Existe uma função Numpy para retornar o primeiro índice de algo em um array?
Nota 2: usar um método compilado em C é preferível a um loop Python.
Embora seja tarde demais para você, mas para referência futura: Usar numba ( 1 ) é a maneira mais fácil até que numpy o implemente. Se você usa a distribuição anaconda python, ela já deve estar instalada. O código será compilado para que seja rápido.
e depois:
fonte
xrange
precisa ser alterado pararange
.enumerate
, as infor i, v in enumerate(vec):
;if v == item: return i
. (Esta não é uma boa ideia em Python <= 2.7, ondeenumerate
cria uma lista em vez de um iterador básico.)Eu fiz uma referência para vários métodos:
argwhere
nonzero
como na pergunta.tostring()
como na resposta de @Rob ReilinkOs códigos Python e Fortran estão disponíveis. Eu pulei os pouco promissores, como converter para uma lista.
Os resultados em escala logarítmica. O eixo X é a posição da agulha (leva mais tempo para descobrir se está mais abaixo na matriz); o último valor é uma agulha que não está na matriz. O eixo Y é a hora de encontrá-lo.
O array tinha 1 milhão de elementos e os testes foram executados 100 vezes. Os resultados ainda variam um pouco, mas a tendência qualitativa é clara: o Python e o f2py fecham no primeiro elemento, então eles escalam de forma diferente. Python fica muito lento se a agulha não estiver nos primeiros 1%, enquanto
f2py
é rápido (mas você precisa compilá-lo).Para resumir, f2py é a solução mais rápida , especialmente se o ponteiro aparecer bem cedo.
Não é embutido, o que é irritante, mas na verdade é apenas 2 minutos de trabalho. Adicione isso a um arquivo chamado
search.f90
:Se você estiver procurando por algo diferente
integer
, basta alterar o tipo. Em seguida, compile usando:depois disso, você pode fazer (do Python):
fonte
f2py
mais lento para 1 item do que 10?Você pode converter uma matriz booleana em uma string Python usando
array.tostring()
e, em seguida, usando o método find ():No entanto, isso envolve a cópia dos dados, já que as strings do Python precisam ser imutáveis. Uma vantagem é que você também pode pesquisar, por exemplo, uma borda ascendente, encontrando
\x00\x01
fonte
Em caso de
np.searchsorted
trabalhos de matrizes classificadas .fonte
Acho que você encontrou um problema em que um método diferente e algum conhecimento a priori do array realmente ajudariam. O tipo de coisa em que você tem uma probabilidade X de encontrar sua resposta nos primeiros Y por cento dos dados. A divisão do problema com a esperança de ter sorte e então fazer isso em python com uma compreensão de lista aninhada ou algo assim.
Escrever uma função C para fazer essa força bruta também não é muito difícil usando ctypes .
O código C que hackeado junto (index.c):
e o python:
e recebo 92.
Enrole o python em uma função adequada e pronto.
A versão C é muito (~ 20x) mais rápida para este seed (avisando que não sou bom com o timeit)
fonte
@tal já apresentou uma
numba
função para encontrar o primeiro índice, mas que só funciona para arrays 1D. Comnp.ndenumerate
você também pode encontrar o primeiro índice em uma matriz de dimensão arbitrária:Caso de amostra:
Os tempos mostram que é semelhante em desempenho à solução tals :
fonte
array
antes de alimentá-lonp.ndenumerate
, de forma que o eixo de interesse venha primeiro.np.argwhere
) a 717ns (sua solução), ambos para um array de forma(3000000, 12)
).Se sua lista estiver ordenada , você pode realizar uma busca muito rápida de índice com o pacote 'bisect'. É O (log (n)) em vez de O (n).
encontra x no array a, definitivamente mais rápido no caso classificado do que qualquer rotina C passando por todos os primeiros elementos (para listas longas o suficiente).
É bom saber às vezes.
fonte
>>> cond = "import numpy as np;a = np.arange(40)"
timeit("np.searchsorted(a, 39)", cond)
funciona por 3.47867107391 segundos.timeit("bisect.bisect(a, 39)", cond2)
funciona por 7.0661458969116 segundos. Parece quenumpy.searchsorted
é melhor para matrizes classificadas (pelo menos para ints).Até onde eu sei, apenas np.any e np.all em matrizes booleanas estão em curto-circuito.
No seu caso, numpy tem que percorrer todo o array duas vezes, uma para criar a condição booleana e uma segunda vez para encontrar os índices.
Minha recomendação neste caso seria usar cíton. Acho que deve ser fácil ajustar um exemplo para este caso, especialmente se você não precisa de muita flexibilidade para diferentes tipos e formatos.
fonte
Eu precisava disso para o meu trabalho, então aprendi a interface C do Python e do Numpy e escrevi minha própria. http://pastebin.com/GtcXuLyd É apenas para arrays 1-D, mas funciona para a maioria dos tipos de dados (int, float ou strings) e os testes mostraram que é novamente cerca de 20 vezes mais rápido do que a abordagem esperada em Python- puro entorpecido.
fonte
Este problema pode ser resolvido de forma eficaz em puro numpy, processando a matriz em blocos:
A matriz é processada em pedaços de tamanho
step
. Quantostep
mais longa a etapa, mais rápido é o processamento da matriz zerada (pior caso). Quanto menor for, mais rápido será o processamento da matriz com um valor diferente de zero no início. O truque é começar com um valor pequenostep
e aumentá-lo exponencialmente. Além disso, não há necessidade de incrementá-lo acima de algum limite devido aos benefícios limitados.Eu comparei a solução com a solução ndarary.nonzero e numba pura contra 10 milhões de conjuntos de flutuadores.
E os resultados na minha máquina:
Pure
ndarray.nonzero
é definitivamente mais solto. A solução numba é cerca de 5 vezes mais rápida para o melhor caso. É cerca de 3 vezes mais rápido no pior dos casos.fonte
Se você está procurando o primeiro elemento diferente de zero, pode usar o seguinte hack:
É uma solução "numpy-pura" muito rápida , mas falha em alguns casos discutidos abaixo.
A solução tira vantagem do fato de que praticamente toda representação de zero para tipos numéricos consiste em
0
bytes. Isso se aplica a numpy'sbool
também. Em versões recentes de numpy, aargmax()
função usa lógica de curto-circuito ao processar obool
tipo. O tamanho debool
é 1 byte.Então, é preciso:
bool
. Nenhuma cópia é criadaargmax()
para encontrar o primeiro byte diferente de zero usando lógica de curto-circuito//
) do deslocamento por um tamanho de um único elemento expresso em bytes (x.itemsize
)x[idx]
é realmente diferente de zero para identificar o caso quando nenhum diferente de zero está presenteEu fiz alguns benchmarks contra a solução numba e a construí
np.nonzero
.Os resultados em minha máquina são:
A solução é 33% mais rápida do que numba e é "numpy-pure".
As desvantagens:
object
float
oudouble
cálculosfonte
x
antes de ligarnonzero()
. Provavelmente será mais lento do que numba, mas ** não ** pesquisará por todo o array enquanto procura a primeira entrada de zero, portanto, pode ser rápido o suficiente para suas necessidades.Como usuário de matlab de longa data, há muito tempo procuro uma solução eficiente para esse problema. Finalmente, motivado por discussões sobre as proposições neste tópico , tentei encontrar uma solução que implementasse uma API semelhante à sugerida aqui , suportando por enquanto apenas matrizes 1D.
Você usaria assim
Os operadores de condição suportados são: cmp_equal, cmp_not_equal, cmp_larger, cmp_smaller, cmp_larger_eq, cmp_smaller_eq. Para eficiência, a extensão é escrita em c.
Você encontra a fonte, benchmarks e outros detalhes aqui:
https://pypi.python.org/pypi?name=py_find_1st&:action=display
para uso em nossa equipe (anaconda no linux e macos) eu fiz um instalador anaconda que simplifica a instalação, você pode usá-lo conforme descrito aqui
https://anaconda.org/roebel/py_find_1st
fonte
Apenas uma observação que se você estiver fazendo uma sequência de pesquisas, o ganho de desempenho de fazer algo inteligente como converter para string pode ser perdido no loop externo se a dimensão da pesquisa não for grande o suficiente. Veja como o desempenho de iteração de find1 que usa o truque de conversão de string proposto acima e find2 que usa argmax ao longo do eixo interno (mais um ajuste para garantir que uma não correspondência retorne como -1)
saídas
Dito isso, um achado escrito em C seria pelo menos um pouco mais rápido do que qualquer uma dessas abordagens
fonte
que tal agora
fonte
where(array==item)[0][0]
da pergunta ...Você pode converter sua matriz em um
list
e usar seuindex()
método:Pelo que eu sei, este é um método compilado em C.
fonte
timeit()
em uma matriz de 10.000 inteiros - a conversão para uma lista foi cerca de 100 vezes mais lenta! Eu tinha esquecido que a estrutura de dados subjacente para um array numpy é muito diferente de uma lista ..