2012-06-19 25 views
6

Tengo 2 matrices en 2D, donde los vectores de columna son vectores de características. Una matriz es de tamaño F x A, el otro de F x B, donde A < < B. Como un ejemplo, para A = 2 y F = 3 (B puede ser cualquier cosa):Distancia entre matrices numpy, columna

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

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

Quiero para calcular la distancia entre arr1 y un fragmento de arr2 que es del mismo tamaño (en este caso, 3x2), para cada posible fragmento de arr2. Los vectores de columna son independientes entre sí, por lo que creo que debería calcular la distancia entre cada vector de columna en arr1 y una colección de vectores de columna que van desde i hasta i + A desde arr2 y tomar la suma de estas distancias (aunque no estoy seguro).

¿Numpy ofrece una forma eficiente de hacerlo, o tendré que tomar rodajas de la segunda matriz y, utilizando otro bucle, calcular la distancia entre cada vector de columna en arr1 y el vector de columna correspondiente en la división?

Ejemplo para mayor claridad, utilizando las matrices mencionadas anteriormente:

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

Por supuesto, la transposición de las matrices está muy bien también, si eso significa que cdist alguna manera se puede utilizar aquí.

+0

Interesante pregunta, +1. ¿Puedo preguntar cuál es la relación entre los dos conjuntos de características? Si no hay una solución general, tal vez se encuentre una solución específica de dominio. –

+0

Los elementos en las matrices indican la presencia (o conteos, si se quiere) de características espaciales en una imagen. Estoy tratando de encontrar la coincidencia más cercana, así que supongo que se puede ver como una tarea de clasificación. 'arr1' es una secuencia corta de, en este caso, 2 pasos de tiempo, que se compara con un documento de B timesteps para encontrar el índice de la secuencia de coincidencia más cercana en él. –

Respuesta

4

Si entiendo su pregunta correctamente, esto funcionará. Sabiendo numpy, es probable que haya una mejor manera, pero esto es al menos bastante sencillo. Usé algunas coordenadas artificiales para mostrar que el cálculo está funcionando como se esperaba.

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

Puede restar arr1 de arr2 asegurando que transmiten unos contra otros correctamente. La mejor manera que se me ocurre es tomar una transposición y hacer una remodelación. Estos no crean copias, crean vistas, así que esto no es tan desperdiciado. (dist es una copia sin embargo.)

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

Ahora todo lo que tenemos que hacer es aplicar numpy.linalg.norm través del eje 1. (Se puede seleccionar entre varias norms).

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

Suponiendo que usted desea la distancia euclidiana simple, también puede hacerlo directamente; no está seguro de si esto será rápido o más lento por lo tanto tratar:

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

Sobre la base de su edición, tenemos que hacer sólo un pequeño pellizco. Como desea probar las columnas por pares, en lugar de por bloques, necesita una ventana móvil. Esto se puede hacer de manera muy sencilla con la indexación bastante sencillo:

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

combinación que con los otros trucos:

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

Sin embargo, la conversión de las matrices de las listas por comprensión tiende a ser lento.Se podría ser más rápido de usar stride_tricks - aquí de nuevo, ver cuál se adapta a sus mejores propósitos:

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

En realidad, esto manipula la forma numpy deja atrás un bloque de memoria, lo que permite una pequeña gama de emular una más grande formación.

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

Ahora tiene una matriz simple de 2 d correspondiente a las distancias para cada par de columnas. Ahora solo se trata de obtener el mean y llamar al argmin.

>>> normed = (dist ** 2).sum(axis=2) ** 0.5 
>>> normed.mean(axis=1) 
array([ 5.  , 9.69535971, 13.  ]) 
>>> min_window = normed.mean(axis=1).argmin() 
>>> arr2[:,[min_window, min_window + 1]] 
array([[3, 6], 
     [5, 8], 
     [2, 5]]) 
+0

No es exactamente lo que estoy buscando, pero es sorprendente lo que hizo mediante la remodelación y es posible que necesite esto en un futuro próximo, +1 para usted. Mis disculpas por no ser tan claro como debería ser. La salida debe consistir en solo 3 valores para las matrices de ejemplo que proporcione, ya que estoy buscando una "mejor coincidencia" dada arr1 y cada combinación del mismo tamaño en arr2, es decir, qué índice (índices) en 'arr2' hace que 'dist (arr2 [i: i + 2], arr1)' sea el más pequeño? –

+0

Ahhh, quieres una ventana móvil. Ver mi edición – senderle

+0

Wow. Tantas funciones de las que nunca había oído hablar, y que probablemente las habría encontrado solo después de un escaneo meticuloso de la documentación. ¡Muchas gracias! –

1

scipy.spatial.distance.cdist?

+0

Creo que calcula la distancia euclidiana entre dos matrices donde cada columna en arr1 se compara con cada columna en arr2. –

2

Puede obtener la matriz de distancia utilizando cdist desde scipy.spatial.distance. Una vez que tenga la matriz de distancia, puede sumar columnas y normalizar para obtener la distancia promedio, si eso es lo que está buscando.

Nota: en lugar de columnas, cdist usa filas para calcular las distancias por pares.

Aquí tienes un ejemplo utilizando la distancia 'coseno':

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

Hay muchas distancias disponibles. Consulte el documentation.

+0

Gracias por el ejemplo, pero creo que no es exactamente lo que estoy buscando. Lo que estoy buscando es algo como esto: 'arr1 = [[1,2], [1,2]], arr2 = [[1,2], [1,2], [1,3] ] 'da' [0, 1] ': 0 porque el primer fragmento' [[1,2], [1,2]] 'de arr2 es igual a arr1, y 1 porque la distancia euclidiana entre' [1, 2] 'y' [1,2] 'es 0 + distancia entre' [1,2] 'y' [1,3] ', que es 1. –

+0

Si pone estos valores en mi ejemplo, y usa el distancia "euclidiana" en lugar de "coseno", se obtiene lo siguiente: D = [[0,0,1], [0,0,1]]. ¿Tal vez puedas usar este resultado para tu propósito? –

Cuestiones relacionadas