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

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

  •  08-07-2019
  •  | 
  •  

Domanda

So che esiste un metodo per un elenco Python per restituire il primo indice di qualcosa:

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

Esiste qualcosa del genere per gli array NumPy?

È stato utile?

Soluzione

Sì, ecco la risposta fornita da un array NumPy, array e un valore, item , da 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 quindi

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

numpy.where

Altri suggerimenti

Se hai bisogno dell'indice della prima occorrenza di solo un valore , puoi utilizzare diverso da zero (o dove , che equivale al 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]),)

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

[ 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 t per ottenere ciò che desideri:

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

Puoi anche convertire un array NumPy in un elenco in aria e ottenere il suo indice. Ad 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.

Se lo utilizzerai come indice in qualcos'altro, puoi utilizzare 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 valori 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.

Solo per aggiungere un alternativa basata su np.ndenumerate per 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.where o np.nonzero .


Tuttavia np.argwhere potrebbe anche gestire con garbo con array multidimensionali (dovresti eseguirne il cast manualmente in una tupla e non è cortocircuitato) ma fallirebbe se nessuna corrispondenza è trovato:

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

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

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

Per trovare un elemento che si interrompe dopo la prima corrispondenza in un array NumPy usa un iteratore ( ndenumerate ).

In [67]: l=range(100)

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

NumPy array:

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

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

Nota che entrambi i metodi index () e next restituiscono un errore se l'elemento non viene trovato. Con next , è possibile utilizzare 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)

Esistono altre funzioni in NumPy ( argmax , dove e diverso da zero ) che possono essere utilizzate per trovare un elemento in un array, ma hanno tutti lo svantaggio di passare attraverso l'intero array alla ricerca di tutte occorrenze, quindi non essere ottimizzati per trovare il primo elemento. Si noti inoltre che dove e diverso da zero restituiscono array, 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 usando un iteratore sia più veloce quando l'elemento cercato è all'inizio dell'array (usando % timeit nella 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 NumPy GitHub aperto .

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

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]

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

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)

Potresti quindi prendere i primi elementi delle liste per ottenere un singolo elemento.

Per gli array unidimensionali ordinati , sarebbe molto più semplice ed efficiente O (log (n)) utilizzare numpy.searchsorted che restituisce un numero intero NumPy (posizione). Ad 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 gli indici in cui inserire gli elementi per mantenere l'ordine.

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

Un'alternativa alla selezione del primo elemento da np.where () consiste nell'utilizzare un'espressione di generatore insieme a enumerare, 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 che è stata trovata 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.

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

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.

Nota: questo è per la versione 2.7 di Python

Puoi usare una funzione lambda per affrontare 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)

anziché

result
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top