2012-06-19 40 views
6

Ho 2 array in 2D, dove i vettori di colonna sono vettori di caratteristiche. Una matrice è di dimensione F x A, l'altra di F x B, dove A < < B. Ad esempio, per A = 2 e F = 3 (B può essere qualsiasi cosa):Distanza tra gli array numpy, in colonna

arr1 = np.array([[1, 4], 
        [2, 5], 
        [3, 6]]) 

arr2 = np.array([[1, 4, 7, 10, ..], 
        [2, 5, 8, 11, ..], 
        [3, 6, 9, 12, ..]]) 

voglio per calcolare la distanza tra arr1 e un frammento di arr2 di dimensioni uguali (in questo caso 3x2), per ogni possibile frammento di arr2. I vettori delle colonne sono indipendenti l'uno dall'altro, quindi credo che dovrei calcolare la distanza tra ciascun vettore di colonna in arr1 e una raccolta di vettori di colonna che vanno da i a i + A da arr2 e prendere la somma di queste distanze (non è sicuro però).

Numpy offre un modo efficace per farlo, o dovrò prendere fette dal secondo array e, utilizzando un altro ciclo, calcolare la distanza tra ciascun vettore di colonna in arr1 e il vettore di colonna corrispondente nella sezione?

Esempio per chiarezza, utilizzando le matrici di cui sopra:

>>> magical_distance_func(arr1, arr2[:,:2]) 
[0, 10.3923..] 
>>> # First, distance between arr2[:,:2] and arr1, which equals 0. 
>>> # Second, distance between arr2[:,1:3] and arr1, which equals 
>>> diff = arr1 - np.array([[4,7],[5,8],[6,9]]) 
>>> diff 
[[-3, -3], [-3, -3], [-3, -3]] 
>>> # this happens to consist only of -3's. Norm of each column vector is: 
>>> norm1 = np.linalg.norm([:,0]) 
>>> norm2 = np.linalg.norm([:,1]) 
>>> # would be extremely good if this worked for an arbitrary number of norms 
>>> totaldist = norm1 + norm2 
>>> totaldist 
10.3923... 

Naturalmente, trasposizione delle matrici è bene anche se ciò significa che cdist possa in qualche modo essere utilizzato qui.

+0

Domanda interessante, +1. Posso chiedere qual è la relazione tra i due set di funzionalità? Se non esiste una soluzione generale, è possibile trovare una soluzione specifica per il dominio. –

+0

Gli elementi negli array indicano la presenza (o il conteggio, se lo si desidera) delle caratteristiche spaziali in un'immagine. Sto cercando di trovare la corrispondenza più vicina, quindi immagino possa essere vista come un compito di classificazione. 'arr1' è una breve sequenza di, in questo caso, 2 timestep, che viene confrontato con un documento di B timesteps per trovare l'indice della sequenza di corrispondenza più vicina al suo interno. –

risposta

4

Se ho capito bene la tua domanda, funzionerà. Conoscendo numpy, c'è probabilmente un modo migliore, ma questo è almeno piuttosto semplice. Ho usato alcune coordinate forzate per mostrare che il calcolo funziona come previsto.

>>> arr1 
array([[0, 3], 
     [1, 4], 
     [2, 5]]) 
>>> arr2 
array([[ 3, 6, 5, 8], 
     [ 5, 8, 13, 16], 
     [ 2, 5, 2, 5]]) 

È possibile sottrarre arr1 da arr2 facendo in modo che essi trasmettono uno contro l'altro in modo corretto. Il modo migliore in cui potrei pensare riguarda l'adozione di una trasposizione e un rimodellamento. Questi non creano copie - creano viste - quindi non è così dispendioso. (dist è una copia però.)

>>> dist = (arr2.T.reshape((2, 2, 3)) - arr1.T).reshape((4, 3)) 
>>> dist 
array([[ 3, 4, 0], 
     [ 3, 4, 0], 
     [ 5, 12, 0], 
     [ 5, 12, 0]]) 

Ora tutto quello che dobbiamo fare è applicare numpy.linalg.norm attraverso asse 1. (È possibile scegliere tra diversi norms).

>>> numpy.apply_along_axis(numpy.linalg.norm, 1, dist) 
array([ 5., 5., 13., 13.]) 

Supponendo che si desideri una semplice distanza euclidea, si può anche farlo direttamente; non so se questo sarà più veloce o più lento in modo da provare entrambi:

>>> (dist ** 2).sum(axis=1) ** 0.5 
array([ 5., 5., 13., 13.]) 

base alla tua modifica, abbiamo a che fare solo un piccolo ritocco. Dal momento che si desidera testare le colonne a coppie, anziché a livello di blocco, è necessaria una finestra a rotazione. Questo può essere fatto molto semplicemente con indicizzazione piuttosto semplice:

>>> arr2.T[numpy.array(zip(range(0, 3), range(1, 4)))] 
array([[[ 3, 5, 2], 
     [ 6, 8, 5]], 

     [[ 6, 8, 5], 
     [ 5, 13, 2]], 

     [[ 5, 13, 2], 
     [ 8, 16, 5]]]) 

Unendo che con gli altri trucchi:

>>> arr2_pairs = arr2.T[numpy.array(zip(range(0, 3), range(1, 4)))] 
>>> dist = arr2_pairs - arr1.T 
>>> (dist ** 2).sum(axis=2) ** 0.5 
array([[ 5.  , 5.  ], 
     [ 9.69535971, 9.69535971], 
     [ 13.  , 13.  ]]) 

Tuttavia, la conversione da array list comprehensions tende ad essere lenta.Si potrebbe essere più veloce da usare stride_tricks - qui ancora una volta, vedere quale si adatta alle vostre scopi migliori:

>>> as_strided(arr2.T, strides=(8, 8, 32), shape=(3, 2, 3)) 
array([[[ 3, 5, 2], 
     [ 6, 8, 5]], 

     [[ 6, 8, 5], 
     [ 5, 13, 2]], 

     [[ 5, 13, 2], 
     [ 8, 16, 5]]]) 

Questa manipola in realtà il modo numpy si muove su un blocco di memoria, permettendo una piccola serie di emulare un grande array.

>>> arr2_pairs = as_strided(arr2.T, strides=(8, 8, 32), shape=(3, 2, 3)) 
>>> dist = arr2_pairs - arr1.T 
>>> (dist ** 2).sum(axis=2) ** 0.5 
array([[ 5.  , 5.  ], 
     [ 9.69535971, 9.69535971], 
     [ 13.  , 13.  ]]) 

Così ora si dispone di un semplice array 2-d corrispondente alle distanze per ogni coppia di colonne. Ora è solo questione di ottenere il numero mean e chiamare argmin.

+0

Non è esattamente quello che sto cercando, ma è incredibile quello che hai fatto per rimodellare e posso bisogno di questo in un prossimo futuro, uno a te. Le mie scuse per non essere chiaro come dovrei essere. L'uscita dovrebbe consistere di solo 3 valori per le matrici esempio che date, come io sto cercando una "migliore corrispondenza" dato arr1 e ogni combinazione delle stesse dimensioni in arr2, vale a dire che indice (indici) in 'arr2' fa in modo che 'dist (arr2 [i: i + 2], arr1)' sia il più piccolo? –

+0

Ahhh, vuoi una finestra scorrevole. Vedi la mia modifica. – senderle

+0

Wow. Tante funzioni di cui non ho mai sentito parlare e che probabilmente avrei trovato solo dopo una meticolosa scansione della documentazione. Molte grazie! –

1

scipy.spatial.distance.cdist?

+0

Credo che calcoli la distanza euclidea tra due array in cui ogni colonna in arr1 viene confrontata con ogni colonna di arr2. –

2

È possibile ottenere la matrice della distanza utilizzando cdist da scipy.spatial.distance. Una volta che hai la matrice della distanza, puoi semplicemente sommare le colonne e normalizzarle per ottenere la distanza media, se è quello che stai cercando.

Nota: Invece di colonne, cdist utilizza righe per calcolare le distanze coppie.

Ecco un esempio utilizzando la distanza 'cosine':

from scipy.spatial.distance import cdist 

arr1 = np.array([[1, 7], 
       [4, 8], 
       [4, 0]]) 

arr2 = array([[1, 9, 3, 6, 2], 
       [3, 9, 0, 2, 3], 
       [6, 0, 2, 7, 4]]) 

# distance matrix 
D = cdist(arr1.transpose(), arr2.transpose(), 'cosine') 

# average distance array (each position corresponds to each column of arr1) 
d1 = D.mean(axis=1) 

# average distance array (each position corresponds to each column of arr2) 
d2 = D.mean(axis=0) 

# Results 
d1 = array([ 0.23180963, 0.35643282]) 
d2 = array([ 0.31018485, 0.19337869, 0.46050302, 0.3233269 , 0.18321265]) 

Ci sono molte distanze disponibili. Controlla il documentation.

+0

Grazie per l'esempio, ma penso che non sia esattamente quello che sto cercando. Quello che sto cercando è qualcosa di simile: 'arr1 = [[1,2], [1,2]], arr2 = [[1,2], [1,2], [1,3] ] 'dà' [0, 1] ': 0 perché il primo frammento' [[1,2], [1,2]] 'di arr2 è uguale a arr1, e 1 perché la distanza euclidea tra' [1, 2] 'e' [1,2] 'è 0 + distanza fra' [1,2] 'e' [1,3] ', che è 1. –

+0

Se mettete questi valori nel mio esempio, e si utilizza il distanza 'euclidea' invece di 'coseno', ottieni quanto segue: D = [[0,0,1], [0,0,1]]. Forse puoi usare questo risultato per il tuo scopo? –

Problemi correlati