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

StackOverflow https://stackoverflow.com/questions/432112

  •  08-07-2019
  •  | 
  •  

Pergunta

Eu sei que há um método para obter uma lista Python para retornar o primeiro índice de algo:

>>> l = [1, 2, 3]
>>> l.index(2)
1

Existe algo parecido para arrays numpy?

Foi útil?

Solução

Sim, aqui está a resposta dada uma matriz NumPy, array, e um valor, item, para procurar por:

itemindex = numpy.where(array==item)

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

Por exemplo, se uma matriz é duas dimensões e que continha o seu item em dois locais, em seguida,

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

seria igual ao seu item e assim que

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

numpy.where

Outras dicas

Se você precisar o índice da primeira ocorrência de apenas um valor , você pode 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 precisar do primeiro índice de cada um dos muitos valores , você pode, obviamente, fazer o mesmo que acima repetidamente, mas há um truque que pode ser mais rápido. A seguir encontra os índices do primeiro elemento de cada subsequence :

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

Observe que ele encontra no início de ambos subsequência de 3s e ambas as subseqüências de 8s:

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

Por isso, é um pouco diferente do que encontrar o primeiro ocorrência de cada valor. Em seu programa, você pode ser capaz de trabalhar com uma versão classificada de t para obter o que deseja:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)

Você também pode converter uma matriz NumPy a lista 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 irá imprimir 1.

Se você estiver indo para usar isso como um índice em outra coisa, você pode usar índices boolean se as matrizes são irradiável; você não precisa de índices explícitas. A maneira mais simples absoluta de fazer isso é simplesmente índice com base em um valor de verdade.

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 leva booleans, também:

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

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

Apenas para adicionar um muito alto desempenho e acessível alternativa baseada na np.ndenumerate para 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 arrays 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 é um curto-circuito a operação) do que qualquer abordagem utilizando np.where ou np.nonzero.


No entanto np.argwhere também poderia lidar graciosamente com arrays multidimensionais (você precisará lançá-lo manualmente para uma tupla e 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,)

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

Pode-se seguramente assumir que a função index() em Python é implementado para que ele pára após encontrar o primeiro jogo, e isso resulta em um desempenho médio ideal.

Para encontrar um elemento parar após a primeira partida em um uso conjunto NumPy um iterador ( ndenumerate ).

In [67]: l=range(100)

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

array NumPy:

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

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

Note que ambos os métodos index() e next retornar um erro se o elemento não foi encontrado. Com next, pode-se usar um segundo argumento para devolver um valor especial no caso do elemento não é 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, where e nonzero) que pode ser usado para encontrar um elemento em uma matriz, mas todos eles têm a desvantagem de passar por toda a matriz procurando todas ocorrências, portanto, não sendo otimizado para encontrar o primeiro elemento. Note também que where e retorno nonzero matrizes, então 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),)

Tempo comparação

Basta verificar que para grandes matrizes da solução usando um iterador é mais rápido quando o procurou artigo é no início do array (usando %timeit no 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 open NumPy GitHub questão .

Veja também: Numpy: encontrar primeiro índice de valor rápido

Para indexar em qualquer critério, você pode assim 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ápido para fazer o que list.index () faz, exceto não levanta uma exceção se não for encontrada. Cuidado - este é, provavelmente, muito lento em grandes matrizes. Você pode provavelmente monkey patch isso para matrizes se você preferir usá-lo como um 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]

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 de unboxing a 1 elemento de tupla.

Existem muitas operações no NumPy que poderiam talvez ser colocados juntos para alcançar este objetivo. Isso irá retornar índices de elementos igual ao item:

numpy.nonzero(array - item)

Você pode então dar os primeiros elementos das listas para obter um único elemento.

Para unidimensional ordenada matrizes, seria muito mais simples e eficiente O (log (n)) para uso 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 certifique-se a matriz já é classificada

Além disso, verifique se o índice voltou i realmente contém o procurou elemento, desde principal objetivo do searchsorted é encontrar índices que elementos devem ser inseridos para manter a ordem.

if arr[i] == 3:
    print("present")
else:
    print("not present")

Uma alternativa para seleccionar o primeiro elemento de np.where () é a utilização de um gerador de expressão em conjunto com enumerar, tais 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 bidimensional uma variedade faria:

>>> 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 deixa de verificar os elementos do array após a primeira correspondência for encontrada, Considerando que os controlos np.where todos os elementos para uma partida. A expressão gerador seria mais rápido se há corresponder no início da matriz.

O numpy_indexed pacote (disclaimer: Eu sou seu autor) contém um equivalente vetorizado de list.index para numpy.ndarray; isto é:

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]

Esta solução tem vetorizado desempenho, generaliza a ndarrays, e tem várias maneiras de lidar com valores em falta.

Nota: este é para a versão Python 2.7

Você pode usar uma função lambda para lidar com o problema, e ele funciona tanto em variedade NumPy e lista.

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, uso

list(result)

em vez de

result
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top