Existe uma função NumPy para retornar o primeiro índice de algo em uma matriz?

Respostas:

523

Sim, aqui está a resposta dada a uma matriz NumPy,, arraye um valor ,, itempara procurar:

itemindex = numpy.where(array==item)

O resultado é uma tupla com primeiro todos os índices de linha, depois todos os índices de coluna.

Por exemplo, se uma matriz tiver duas dimensões e contiver seu item em dois locais,

array[itemindex[0][0]][itemindex[1][0]]

seria igual ao seu item e por isso seria

array[itemindex[0][1]][itemindex[1][1]]

numpy.where

Alex
fonte
1
Se você está procurando a primeira linha na qual um item existe na primeira coluna, isso funciona (embora ele irá lançar um erro índice se inexistente)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT
29
E se você deseja que ele pare de pesquisar depois de encontrar o primeiro valor? Eu não acho que () é comparável a find ()
Michael Clerx
2
Ah! Se você estiver interessado em desempenho, veja a resposta a esta pergunta: stackoverflow.com/questions/7632963/...
Michael Clerx
11
np.argwhereseria um pouco mais útil aqui:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric
3
Vale a pena notar que esta resposta assume que a matriz é 2D. wherefunciona em qualquer matriz, e retornará um tuplo de comprimento 3, quando utilizado em uma matriz 3D, etc.
P. Camilleri
70

Se você precisar do índice da primeira ocorrência de apenas um valor , poderá usar nonzero(ou where, o que equivale à mesma coisa neste caso):

>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6

Se você precisar do primeiro índice de cada um dos muitos valores , obviamente poderá fazer o mesmo acima, repetidamente, mas há um truque que pode ser mais rápido. A seguir, são encontrados os índices do primeiro elemento de cada subsequência :

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

Observe que ele encontra o início das subsequências de 3s e das subsequências de 8s:

[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]

Portanto, é um pouco diferente de encontrar a primeira ocorrência de cada valor. No seu programa, você poderá trabalhar com uma versão classificada tpara obter o que deseja:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
Vebjorn Ljosa
fonte
4
Poderia explicar o que r_é?
Geoff
1
@ Geoff, r_concatena; ou, mais precisamente, converte objetos de fatia em concatenação ao longo de cada eixo. Eu poderia ter usado em seu hstacklugar; isso pode ter sido menos confuso. Consulte a documentação para obter mais informações sobre r_. Há também um c_.
Vebjorn Ljosa 24/03
+1, bom! (vs NP.where) sua solução é muito mais simples (e provavelmente mais rápida) no caso em que é apenas a primeira ocorrência de um determinado valor em uma matriz 1D que precisamos
doug
3
O último caso (para encontrar o primeiro índice de todos os valores) é dada porvals, locs = np.unique(t, return_index=True)
askewchan
@askewchan sua versão é funcionalmente equivalente, mas muito, muito, muito mais lenta
Jivan
50

Você também pode converter uma matriz NumPy para listar no ar e obter seu índice. Por exemplo,

l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i

Ele imprimirá 1.

Hima
fonte
Pode ser que a biblioteca tenha mudado desde que foi escrita pela primeira vez. Mas essa foi a primeira solução que funcionou para mim.
Amracel 03/04/19
1
Eu fiz bom uso deste encontrar vários valores em uma lista usando uma compreensão da lista:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham
1
@ MattWenham Se for grande o suficiente, você pode converter o seu find_listem uma matriz NumPy object(ou qualquer coisa mais específica que seja apropriada) e apenas fazer find_arr[index_list].
Narfanar 30/04/19
Totalmente fora de tópico, mas é a primeira vez que vejo a frase "no ar" - o que mais vi, em seu lugar, é provavelmente "on the fly".
flow2k
18

Apenas para adicionar um desempenho muito útil alternativa baseada em np.ndenumeratepara encontrar o primeiro índice:

from numba import njit
import numpy as np

@njit
def index(array, item):
    for idx, val in np.ndenumerate(array):
        if val == item:
            return idx
    # If no item was found return None, other return types might be a problem due to
    # numbas type inference.

Isso é muito rápido e lida naturalmente com matrizes multidimensionais :

>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2

>>> index(arr1, 2)
(2, 2, 2)

>>> arr2 = np.ones(20)
>>> arr2[5] = 2

>>> index(arr2, 2)
(5,)

Isso pode ser muito mais rápido (porque está causando um curto-circuito na operação) do que qualquer abordagem usando np.whereou np.nonzero.


No entanto np.argwhere, também poderia lidar graciosamente com arrays multidimensionais (você precisará lançá-lo manualmente para uma tupla e ele não está em curto-circuito), mas que iria falhar se nenhuma correspondência for encontrada:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
MSeifert
fonte
2
@njité uma abreviação de jit(nopython=True)ie a função será totalmente compilada em tempo real no momento da primeira execução, para que as chamadas do interpretador Python sejam completamente removidas.
22320 bartolo-otrit #
14

Se você usar isso como um índice em outra coisa, poderá usar índices booleanos se as matrizes forem transmissíveis; você não precisa de índices explícitos. A maneira mais simples e absoluta de fazer isso é simplesmente indexar com base em um valor verdadeiro.

other_array[first_array == item]

Qualquer operação booleana funciona:

a = numpy.arange(100)
other_array[first_array > 50]

O método diferente de zero também aceita booleanos:

index = numpy.nonzero(first_array == item)[0][0]

Os dois zeros são para a tupla de índices (assumindo que first_array é 1D) e, em seguida, o primeiro item na matriz de índices.

Matt
fonte
10

l.index(x)retorna o menor i, de modo que i é o índice da primeira ocorrência de x na lista.

Pode-se supor com segurança que a index()função no Python é implementada para que pare após encontrar a primeira correspondência, e isso resulta em um desempenho médio ideal.

Para localizar um elemento que para após a primeira correspondência em uma matriz NumPy, use um iterador ( ndenumerate ).

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

Matriz NumPy:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

Observe que ambos os métodos index()e nextretornam um erro se o elemento não for encontrado. Com next, pode-se usar um segundo argumento para retornar um valor especial caso o elemento não seja encontrado, por exemplo

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

Existem outras funções no NumPy ( argmax, wheree nonzero) que pode ser usado para encontrar um elemento em uma matriz, mas todos eles têm a desvantagem de passar por todo o conjunto em busca de todas as ocorrências, portanto, não sendo otimizado para encontrar o primeiro elemento. Observe também isso wheree nonzeroretorne matrizes, portanto, você precisa selecionar o primeiro elemento para obter o índice.

In [71]: np.argmax(a==2)
Out[71]: 2

In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)

In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)

Comparação de tempo

Apenas verificando se, para matrizes grandes, a solução usando um iterador é mais rápida quando o item pesquisado está no início da matriz (usando %timeitno shell IPython):

In [285]: a = np.arange(100000)

In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop

In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop

In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop

Este é um problema aberto do NumPy GitHub .

Veja também: Numpy: encontre o primeiro índice de valor rapidamente

user2314737
fonte
1
Acho que você também deve incluir um momento para o pior caso (último elemento), apenas para que os leitores saibam o que acontece com eles no pior caso, quando usam sua abordagem.
MSeifert
@MSeifert eu não posso obter um tempo razoável para a solução pior caso iterador - eu vou apagar esta resposta até eu descobrir o que há de errado com ele
user2314737
1
não %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))funciona? Se você está se perguntando por que é 1000 vezes mais lento - é porque os loops python sobre matrizes numpy são notoriamente lentos.
MSeifert
@MSeifert não, eu não sabia disso, mas eu também estou intrigado com o fato de que argmaxe wheresão muito mais rápidos neste caso (procurou elemento no final do array)
user2314737
Eles devem ser tão rápidos como se o elemento estivesse no começo. Eles sempre processam toda a matriz para sempre levar o mesmo tempo (pelo menos deveriam).
MSeifert
9

Para matrizes unidimensionais classificadas , seria muito mais simples e eficiente O (log (n)) usar numpy.searchsorted que retorna um número inteiro NumPy (posição). Por exemplo,

arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)

Apenas verifique se a matriz já está classificada

Verifique também se o índice retornado i realmente contém o elemento pesquisado, já que o principal objetivo do searchsorted é encontrar índices onde os elementos devem ser inseridos para manter a ordem.

if arr[i] == 3:
    print("present")
else:
    print("not present")
Alok Nayak
fonte
2
searchsorted não é nlog (n), uma vez que não classifica a matriz antes da pesquisa, assume que a matriz de argumentos já está classificada. confira a documentação de numpy.searchsorted (link acima)
Alok Nayak
6

Para indexar em qualquer critério, você pode fazer algo como o seguinte:

In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
   .....:         print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4

E aqui está uma função rápida para fazer o que list.index () faz, exceto que não gera uma exceção se não for encontrada. Cuidado - isso provavelmente é muito lento em matrizes grandes. Provavelmente, você pode aplicar o patch em matrizes se preferir usá-lo como método.

def ndindex(ndarray, item):
    if len(ndarray.shape) == 1:
        try:
            return [ndarray.tolist().index(item)]
        except:
            pass
    else:
        for i, subarray in enumerate(ndarray):
            try:
                return [i] + ndindex(subarray, item)
            except:
                pass

In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]
Autoplectic
fonte
5

Para arrays 1D, eu recomendo np.flatnonzero(array == value)[0], o que equivale a ambos np.nonzero(array == value)[0][0]e np.where(array == value)[0][0], mas evita a feiúra do unboxing a 1 elemento de tupla.

1 ''
fonte
4

Uma alternativa para selecionar o primeiro elemento de np.where () é usar uma expressão geradora junto com enumerate, como:

>>> import numpy as np
>>> x = np.arange(100)   # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2

Para uma matriz bidimensional, seria necessário:

>>> x = np.arange(100).reshape(10,10)   # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x) 
...            for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)

A vantagem dessa abordagem é que ela para de verificar os elementos da matriz após a primeira correspondência ser encontrada, enquanto o np.where verifica todos os elementos em busca de uma correspondência. Uma expressão de gerador seria mais rápida se houver correspondência no início da matriz.

Noyer282
fonte
Caso não haja uma correspondência na matriz, esse método também permite especificar convenientemente um valor de fallback. Se o primeiro exemplo retornasse Nonecomo um substituto, ele se tornaria next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen
4

Existem muitas operações no NumPy que talvez possam ser reunidas para isso. Isso retornará índices de elementos iguais ao item:

numpy.nonzero(array - item)

Você pode pegar os primeiros elementos das listas para obter um único elemento.

Ned Batchelder
fonte
5
isso não forneceria os índices de todos os elementos que não são iguais ao item?
Autoplectic
3

O pacote numpy_indexed (exoneração de responsabilidade, eu sou seu autor) contém um equivalente vetorizado de list.index para numpy.ndarray; isso é:

sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]

import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx)   # [2, -1]

Essa solução tem desempenho vetorizado, generaliza para ndarrays e possui várias maneiras de lidar com valores ausentes.

Eelco Hoogendoorn
fonte
-1

Nota: isto é para a versão python 2.7

Você pode usar uma função lambda para lidar com o problema e funciona tanto na matriz quanto na lista NumPy.

your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]

import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]

E você pode usar

result[0]

para obter o primeiro índice dos elementos filtrados.

Para python 3.6, use

list(result)

ao invés de

result
Statham
fonte
Isso resulta no <filter object at 0x0000027535294D30>Python 3 (testado no Python 3.6.3). Talvez atualizar para Python 3?
Peter Mortensen