2011-11-24 25 views
24

Ho due array 1D, x & y, uno più piccolo dell'altro. Sto cercando di trovare l'indice di ogni elemento di y in x.Numpy: per ogni elemento in un array, trovare l'indice in un altro array

Ho trovato due modi ingenui per fare ciò, il primo è lento e il secondo a uso intensivo di memoria.

Il modo lento

indices= [] 
for iy in y: 
    indices += np.where(x==iy)[0][0] 

L'ingordo di memoria

xe = np.outer([1,]*len(x), y) 
ye = np.outer(x, [1,]*len(y)) 
junk, indices = np.where(np.equal(xe, ye)) 

C'è un modo più veloce o approccio intensivo meno memoria? Idealmente la ricerca trarrebbe vantaggio dal fatto che non stiamo cercando una sola cosa in una lista, ma molte cose, e quindi è leggermente più adatta alla parallelizzazione. Punti bonus se non si assume che ogni elemento di y sia effettivamente in x.

risposta

15

Come ha detto Joe Kington, searchsorted() può cercare l'elemento molto rapidamente. Per far fronte a elementi che non sono in x, è possibile controllare il risultato cercato con y originale e creare un array mascherato:

import numpy as np 
x = np.array([3,5,7,1,9,8,6,6]) 
y = np.array([2,1,5,10,100,6]) 

index = np.argsort(x) 
sorted_x = x[index] 
sorted_index = np.searchsorted(sorted_x, y) 

yindex = np.take(index, sorted_index, mode="clip") 
mask = x[yindex] != y 

result = np.ma.array(yindex, mask=mask) 
print result 

il risultato è:

[-- 3 1 -- -- 6] 
+0

Ah, un grande pezzo di codice. Grazie per aver generalizzato il codice di Joe! – Chris

12

Che ne dici di questo?

Presuppone che ogni elemento di y è in x, (e restituirà risultati anche per elementi che non lo sono!) Ma è molto più veloce.

import numpy as np 

# Generate some example data... 
x = np.arange(1000) 
np.random.shuffle(x) 
y = np.arange(100) 

# Actually preform the operation... 
xsorted = np.argsort(x) 
ypos = np.searchsorted(x[xsorted], y) 
indices = xsorted[ypos] 
+0

Fantastic. Molto più veloce davvero. Sto includendo 'assert na.all (na.intersect1d (x, y) == na.sort (y))' per limitare l'input in modo che y sia un sottoinsieme di x. Grazie! – Chris

15

voglio suggerire one soluzione line:

indices = np.where(np.in1d(x, y))[0] 

Il risultato è un array con indici per x matrice che corrisponde agli elementi da y che sono stati trovati in x.

Si può usare senza numpy.where se necessario.

+0

Questa dovrebbe essere la risposta scelta. Funziona anche quando i valori di x sono ripetuti o inesistenti. La risposta che implica la ricerca è complessa, strana, innaturale. –

+5

Mentre ciò restituisce gli indici degli elementi in y che esistono in x, l'ordine degli indici restituiti non corrisponde all'ordine dei valori in x. Considerare: x = np.array ([1,2,3,4,5]; y = np.array ([5,4,3,2,1]). Il metodo sopra riportato restituisce l'array ([0,1, 2,3,4]), quindi x [0] = 1 è abbinato a y [0] = 5, che non è ciò che si desidera ... – ccbunney

+0

Codice grande ed elegante! – Orangeblue

0

Il pacchetto numpy_indexed (disclaimer: Sono suo autore) contiene una funzione che fa esattamente questo:

import numpy_indexed as npi 
indices = npi.indices(x, y, missing='mask') 

Sarà attualmente alzare una KeyError se sono presenti in x non tutti gli elementi di y; ma forse dovrei aggiungere un kwarg in modo che si possa scegliere di contrassegnare tali elementi con un -1 o qualcosa del genere.

Dovrebbe avere la stessa efficienza della risposta attualmente accettata, poiché l'implementazione è simile. numpy_indexed è tuttavia più flessibile e consente anche di cercare indici di righe di array multidimensionali, ad esempio.

MODIFICA: ive ha cambiato la gestione dei valori mancanti; il kwarg 'mancante' ora può essere impostato con 'raise', 'ignore' o 'mask'. In quest'ultimo caso si ottiene un array mascherato della stessa lunghezza di y, sul quale è possibile chiamare .compressed() per ottenere gli indici validi. Nota che c'è anche npi.contains (x, y) se questo è tutto ciò che devi sapere.

4

Vorrei solo fare questo:

indices = np.where(y[:, None] == x[None, :])[1] 

differenza vostro modo di memoria-hog, questo fa uso di trasmissione per generare direttamente 2D matrice booleana, senza la creazione di array 2D sia per xe y.

+1

Per la cronaca, questa memoria si appanna come beh – romeric

+0

Sì, quello che intendevo è che è meno memoria-hogging. Penso che la mia versione sia un buon compromesso nel mantenere pulito il codice mentre si occupa meno memoria. –

0

Una soluzione più diretta, che non prevede l'ordinamento dell'array.

import pandas as pd 
A = pd.Series(['amsterdam', 'delhi', 'chromepet', 'tokyo', 'others']) 
B = pd.Series(['chromepet', 'tokyo', 'tokyo', 'delhi', 'others']) 

# Find index position of B's items in A 
B.map(lambda x: np.where(A==x)[0][0]).tolist() 

risultato è:

[2, 3, 3, 1, 4] 
Problemi correlati