2012-06-19 3 views
6

У меня есть 2 массива в 2D, где векторы столбцов являются векторами признаков. Один массив имеет размер F х А, другой из F х B, где А < < В. В качестве примера, для А = 2 и Р = 3 (B может быть любым):Расстояние между массивами numpy, columnwise

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

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

Я хочу для вычисления расстояния между arr1 и фрагментом arr2, который имеет одинаковый размер (в данном случае 3x2) для каждого возможного фрагмента arr2. Векторы столбцов независимы друг от друга, поэтому я считаю, что я должен рассчитать расстояние между каждым столбчатым вектором в arr1 и коллекцию векторов столбцов от i до i + A от arr2 и взять сумму этих расстояний (не уверен, хотя).

Предлагает ли numpy эффективный способ сделать это, или мне нужно взять срезы из второго массива и, используя другой цикл, рассчитать расстояние между каждым столбцом в arr1 и соответствующим вектором столбца в срезе?

Пример для наглядности, используя массивы, указанные выше:

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

Конечно, перенося массивы это тоже хорошо, если это означает, что cdist каким-то образом может быть использован здесь.

+0

Интересный вопрос, +1. Могу ли я спросить, какова связь между двумя наборами функций? Если нет общего решения, может быть найдено решение, специфичное для домена. –

+0

Элементы в массивах указывают на наличие (или подсчет, если потребуется) пространственных объектов в изображении. Я пытаюсь найти ближайшее совпадение, поэтому, я думаю, это можно рассматривать как задачу классификации. 'arr1' - это короткая последовательность, в данном случае, 2 временных меток, которая сравнивается с документом B timesteps, чтобы найти индекс ближайшей совпадающей последовательности в нем. –

ответ

4

Если я правильно понял ваш вопрос, это сработает. Зная numpy, есть, вероятно, лучший способ, но это, по крайней мере, довольно прямолинейно. Я использовал некоторые надуманные координаты, чтобы показать, что расчет работает как ожидалось.

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

Вы можете вычесть из arr1arr2, гарантируя, что они передают друг против друга правильно. Лучший способ, о котором я мог думать, - это взять транспонирование и сделать некоторые изменения. Они не создают копии - они создают представления - так что это не так расточительно. (dist является копией, хотя.)

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

Теперь все, что мы должны сделать, это применить numpy.linalg.norm через ось 1. (Вы можете выбрать один из нескольких norms).

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

Предполагая, что вы хотите простое евклидово расстояние, вы также можете сделать это напрямую; не уверен, будет ли это быстрее или медленнее, так что постарайтесь как:

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

На основе вашего редактирования, мы должны сделать только один небольшой настройки. Поскольку вы хотите протестировать столбцы пополам, а не по блоку, вам нужно окно для катания. Это может быть сделано очень просто с довольно простой индексации:

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

Объединение, что с другими трюками:

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

Однако преобразование массивов из списковых имеет тенденцию быть медленным.Это может быстрее использовать stride_tricks - здесь, какой из них подходит для ваших целей: лучшие

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

Это фактически манипулирует путь numpy движется над блоком памяти, что позволяет небольшой массив эмулировать больше массив.

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

Итак, теперь у вас есть простой 2-й массив, соответствующий расстояниям для каждой пары колонок. Теперь дело только в том, чтобы получить mean и позвонить 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

Это не совсем то, что я ищу, но удивительно, что вы сделали, изменив форму, и мне может понадобиться это в ближайшем будущем, +1 к вам. Приношу свои извинения за то, что я не настолько ясен, как должен. Результат должен состоять только из 3 значений для массивов примеров, которые вы даете, поскольку я ищу «наилучшее соответствие» данным arr1 и каждой комбинации того же размера в arr2, то есть какой индекс (индексы) в 'arr2' делает так, что 'dist (arr2 [i: i + 2], arr1)' является наименьшим? –

+0

Ахх, тебе нужно окно. См. Мое редактирование. – senderle

+0

Ничего себе. Так много функций я никогда не слышал, и, вероятно, нашел бы только после тщательного сканирования документации. Большое спасибо! –

1

scipy.spatial.distance.cdist?

+0

Я считаю, что вычисляет эвклидовое расстояние между двумя массивами, где каждый столбец в arr1 сравнивается с каждым столбцом в arr2. –

2

Вы можете получить матрицу расстояний, используя cdist из scipy.spatial.distance. Когда у вас есть матрица расстояний, вы можете просто суммировать столбцы и нормализовать, чтобы получить среднее расстояние, если это то, что вы ищете.

Примечание: вместо столбцов cdist использует строки для вычисления попарных расстояний.

Вот вам пример использования «» косинуса расстояния:

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

Есть много дистанций. Проверьте documentation.

+0

Спасибо, например, но я думаю, что это не совсем то, что я ищу. То, что я ищу, это примерно так: 'arr1 = [[1,2], [1,2]], arr2 = [[1,2], [1,2], [1,3] ] 'дает' [0, 1] ': 0, потому что первый фрагмент' [[1,2], [1,2]] of arr2 равен arr1 и 1, потому что евклидово расстояние между '[1, 2] 'и' [1,2] 'равно 0 + расстоянию между' [1,2] 'и' [1,3] ', что равно 1. –

+0

Если вы поместите эти значения в мой пример и используете «евклидовое расстояние» вместо «косинус», вы получаете следующее: D = [[0,0,1], [0,0,1]]. Может быть, вы можете использовать этот результат для своей цели? –

Смежные вопросы