Esiste una funzione NumPy per restituire il primo indice di qualcosa in un array?


Risposte:


523

Sì, ecco la risposta fornita da un array NumPy arraye da un valore itemper cercare:

itemindex = numpy.where(array==item)

Il risultato è una tupla con prima tutti gli indici di riga, quindi tutti gli indici di colonna.

Ad esempio, se un array ha due dimensioni e conteneva il tuo articolo in due posizioni, allora

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

sarebbe uguale al tuo articolo e così sarebbe

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

numpy.where


1
Se stai cercando la prima riga in cui esiste un elemento nella prima colonna, funziona (anche se genererà un errore di indice se non ne esiste nessuno)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT

29
Cosa succede se si desidera interrompere la ricerca dopo aver trovato il primo valore? Non penso dove () sia paragonabile a find ()
Michael Clerx,

2
Ah! Se sei interessato alle prestazioni, dai un'occhiata alla risposta a questa domanda: stackoverflow.com/questions/7632963/…
Michael Clerx,

11
np.argwheresarebbe leggermente più utile qui:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
Vale la pena notare che questa risposta presuppone che l'array sia 2D. wherefunziona su qualsiasi array e restituirà una tupla di lunghezza 3 se utilizzato su un array 3D, ecc.
P. Camilleri,

70

Se hai bisogno dell'indice della prima occorrenza di un solo valore , puoi usare nonzero(o where, che equivale alla stessa cosa in questo 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 hai bisogno del primo indice di ciascuno di molti valori , puoi ovviamente fare lo stesso come sopra ripetutamente, ma c'è un trucco che potrebbe essere più veloce. Di seguito sono riportati gli indici del primo elemento di ogni sottosequenza :

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

Si noti che trova l'inizio sia della sottosequenza di 3 secondi sia delle sottosequenze di 8 secondi:

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

Quindi è leggermente diverso dal trovare la prima occorrenza di ciascun valore. Nel tuo programma, potresti essere in grado di lavorare con una versione ordinata di tper ottenere ciò che desideri:

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

4
Potresti spiegare cos'è r_?
Geoff,

1
@Geoff, r_concatenati; o, più precisamente, traduce gli oggetti fetta in concatenazione lungo ciascun asse. Avrei potuto usare hstackinvece; che potrebbe essere stato meno confuso. Vedere la documentazione per ulteriori informazioni su r_. C'è anche un c_.
Vebjorn Ljosa,

+1, bello! (vs NP.where) la tua soluzione è molto più semplice (e probabilmente più veloce) nel caso in cui sia solo la prima occorrenza di un dato valore in un array 1D di cui abbiamo bisogno
doug

3
Quest'ultimo caso (trovare il primo indice di tutti i valori) è dato davals, locs = np.unique(t, return_index=True)
askewchan,

@askewchan la tua versione è funzionalmente equivalente, ma molto, molto, molto più lenta
Jivan

50

Puoi anche convertire un array NumPy in un elenco in aria e ottenere il suo indice. Per esempio,

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

Stampa 1.


È possibile che la libreria sia cambiata da quando è stata scritta per la prima volta. Ma questa è stata la prima soluzione che ha funzionato per me.
amracel,

1
Ne ho fatto buon uso per trovare più valori in un elenco usando una comprensione dell'elenco:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham,

1
@MattWenham Se è abbastanza grande, puoi convertirlo find_listin un array NumPy di object(o qualcosa di più specifico che è appropriato) e semplicemente fare find_arr[index_list].
Narfanar,

Totalmente fuori tema, ma questa è la prima volta che vedo la frase "nell'aria" - quello che ho visto di più, al suo posto, è probabilmente "al volo".
flow2k,

18

Solo per aggiungere un molto performante e utile alternativa basata su np.ndenumerateper trovare il primo indice:

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.

Questo è piuttosto veloce e si occupa naturalmente di array multidimensionali :

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

Questo può essere molto più veloce (perché sta cortocircuitando l'operazione) rispetto a qualsiasi approccio che utilizza np.whereo np.nonzero.


Tuttavia, np.argwherepotrebbe anche gestire con grazia array multidimensionali (è necessario eseguirne il cast manualmente su una tupla e non è in cortocircuito) ma fallirebbe se non viene trovata alcuna corrispondenza:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)

2
@njitè una scorciatoia di jit(nopython=True)cioè la funzione sarà completamente compilata al volo al momento della prima esecuzione in modo che le chiamate dell'interprete Python vengano completamente rimosse.
bartolo-otrit,

14

Se lo utilizzerai come indice in qualcos'altro, puoi utilizzare gli indici booleani se le matrici sono trasmissibili; non hai bisogno di indici espliciti. Il modo più semplice per farlo è semplicemente indicizzare in base a un valore di verità.

other_array[first_array == item]

Qualsiasi operazione booleana funziona:

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

Anche il metodo diverso da zero prende i booleani:

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

I due zeri sono per la tupla di indici (supponendo che first_array sia 1D) e quindi il primo elemento nella matrice di indici.


10

l.index(x)restituisce il più piccolo i tale che io sia l'indice della prima occorrenza di x nell'elenco.

Si può tranquillamente presumere che la index()funzione in Python sia implementata in modo che si arresti dopo aver trovato la prima corrispondenza, e ciò si traduce in una prestazione media ottimale.

Per trovare un elemento che si interrompe dopo la prima corrispondenza in un array NumPy, utilizzare un iteratore ( ndenumerato ).

In [67]: l=range(100)

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

Matrice NumPy:

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

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

Si noti che entrambi i metodi index()e nextrestituiscono un errore se l'elemento non viene trovato. Con next, si può usare un secondo argomento per restituire un valore speciale nel caso in cui l'elemento non venga trovato, ad es

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

Ci sono altre funzioni in NumPy ( argmax, wheree nonzero) che può essere utilizzato per trovare un elemento in un array, ma tutti presentano l'inconveniente di passare attraverso l'intero array cercando tutte le occorrenze, quindi non ottimizzata per reperire il primo elemento. Notare anche che wheree nonzerorestituire le matrici, quindi è necessario selezionare il primo elemento per ottenere l'indice.

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

Confronto temporale

Basta verificare che per array di grandi dimensioni la soluzione utilizzando un iteratore sia più veloce quando l'elemento cercato è all'inizio dell'array (utilizzando %timeitnella 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

Questo è un problema di NumPy GitHub aperto .

Vedi anche: Numpy: trova velocemente il primo indice di valore


1
Penso che dovresti anche includere una tempistica per il caso peggiore (ultimo elemento) solo così i lettori sanno cosa succede loro nel caso peggiore quando usano il tuo approccio.
MSeifert,

@MSeifert Non riesco a ottenere un tempismo ragionevole per la soluzione dell'iteratore nel peggiore dei casi
Eliminerò

1
non %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))funziona? Se ti stai chiedendo perché è 1000 volte più lento - è perché i loop di pitone su array intorpiditi sono notoriamente lenti.
MSeifert,

@MSeifert no, non lo sapevo, ma sto anche perplesso per il fatto che argmaxe wheresono molto più veloci in questo caso (cercato elemento alla fine di array)
user2314737

Dovrebbero essere veloci come se l'elemento fosse all'inizio. Elaborano sempre l'intero array in modo che prendano sempre lo stesso tempo (almeno dovrebbero).
MSeifert,

9

Per le matrici ordinate unidimensionali , sarebbe molto più semplice ed efficiente O (log (n)) utilizzare numpy.searchsorted che restituisce un numero intero NumPy (posizione). Per esempio,

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

Assicurati solo che l'array sia già ordinato

Controlla anche se l'indice restituito contiene effettivamente l'elemento cercato, poiché l'obiettivo principale di searchsorted è trovare indici in cui gli elementi devono essere inseriti per mantenere l'ordine.

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

2
searchsorted non è nlog (n) poiché non ordina l'array prima della ricerca, presuppone che l'array di argomenti sia già ordinato. controlla la documentazione di numpy.searchsorted (link sopra)
Alok Nayak

6

Per indicizzare su qualsiasi criterio, puoi fare qualcosa di simile al seguente:

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

Ed ecco una rapida funzione per fare ciò che fa list.index (), tranne che non genera un'eccezione se non viene trovata. Attenzione: questo è probabilmente molto lento su array di grandi dimensioni. Probabilmente puoi applicare questa patch alle matrici se preferisci usarla come metodo.

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]

5

Per gli array 1D, consiglierei np.flatnonzero(array == value)[0], che equivale a entrambi np.nonzero(array == value)[0][0]e np.where(array == value)[0][0]che evita la bruttezza di decomprimere una tupla a 1 elemento.


4

Un'alternativa alla selezione del primo elemento da np.where () consiste nell'utilizzare un'espressione di generatore insieme a enumerate, come:

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

Per un array bidimensionale uno dovrebbe fare:

>>> 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)

Il vantaggio di questo approccio è che smette di controllare gli elementi dell'array dopo aver trovato la prima corrispondenza, mentre np.where controlla tutti gli elementi per una corrispondenza. Un'espressione del generatore sarebbe più veloce se c'è una corrispondenza all'inizio dell'array.


Nel caso in cui non vi sia alcuna corrispondenza nell'array, questo metodo consente anche di specificare comodamente un valore di fallback. Se il primo esempio dovesse tornare Nonecome fallback, lo sarebbe next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen,

4

Ci sono molte operazioni in NumPy che potrebbero forse essere messe insieme per raggiungere questo obiettivo. Ciò restituirà indici di elementi uguali all'elemento:

numpy.nonzero(array - item)

È quindi possibile prendere i primi elementi degli elenchi per ottenere un singolo elemento.


5
ciò non darebbe gli indici di tutti gli elementi che non sono uguali all'elemento?
Autoplettico,

3

Il pacchetto numpy_indexed (dichiarazione di non responsabilità, sono il suo autore) contiene un equivalente vettoriale di list.index per numpy.ndarray; questo è:

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]

Questa soluzione ha prestazioni vettoriali, generalizza a ndarrays e ha vari modi di gestire i valori mancanti.


-1

Nota: questo è per la versione 2.7 di Python

È possibile utilizzare una funzione lambda per gestire il problema e funziona sia sull'array che sull'elenco 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 puoi usare

result[0]

per ottenere il primo indice degli elementi filtrati.

Per python 3.6, utilizzare

list(result)

invece di

result

Ciò si traduce in <filter object at 0x0000027535294D30>Python 3 (testato su Python 3.6.3). Forse l'aggiornamento per Python 3?
Peter Mortensen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.