Numpy Vectorize Signature - PullRequest
       9

Numpy Vectorize Signature

0 голосов
/ 24 марта 2020

Постановка задачи

У меня есть два массива (X и Y) с размерами (n,d) и (m,d) соответственно, и я хочу применить функцию к каждому комбинация строк между двумя матрицами. Я хочу применить следующую функцию:

def norm(x, y):
    return np.linalg.norm(x-y)

Где x и y - соответствующие векторы строк для разных матриц. Вывод, который я ищу, представляет собой матрицу формы (n,m), где каждый индекс является нормой разности между соответствующими векторами строк в X и Y.

Ожидаемое решение:

Таким образом, если входные матрицы:

X = [[1,1,1,1],
    [2,2,2,2]]

Y = [[3,3,3,3],
    [4,4,4,4]]

Я хотел бы получить следующий вывод:

[[4,6],
[2,4]]

Текущее решение

На данном этапе единственное решение, которое у меня есть, - это использовать функцию np.vectorize, чтобы векторизовать мою функцию, отобразить различные строки в функцию и получить матричный вывод. Однако при применении к функции следующей векторизации:

norm_vec = np.vectorize(norm, signature='(d),(d)->()')

я получаю следующий вывод:

[[4,4]]

Похоже, что функция отображает функцию только на каждую пару строк, а не каждая комбинация строк. Я все еще очень плохо знаком с функцией vectorize и не уверен, как работает аргумент signature, но моя интуиция подсказывает мне, что эта функция с правильной сигнатурой даст мне правильное решение.

Есть ли кто-нибудь, кто может дать мне некоторое дополнительное представление о том, как работает функция np.vectorize и построить правильный signature, чтобы получить решение, которое я хочу, или указать мне правильное направление, если я полностью отстают. Я бы не хотел использовать циклы for, так как я планирую применить эту функцию на некоторых больших матрицах

1 Ответ

0 голосов
/ 24 марта 2020

Ваше использование signature в порядке; ему просто нужно использовать broadcasting:

In [374]: def norm(x, y): 
     ...:     return np.linalg.norm(x-y) 
     ...:                                                                                      

In [376]: f = np.vectorize(norm, signature='(d),(d)->()')                                      
In [377]:                                                                                      
In [377]: X = np.array([[1,1,1,1], 
     ...:     [2,2,2,2]]) 
     ...:  
     ...: Y = np.array([[3,3,3,3], 
     ...:     [4,4,4,4]])                                                                      
In [378]: f(X,Y)                                                                               
Out[378]: array([4., 4.])

In [379]: f(X[:,None,:], Y[None,:,:])                                                           
Out[379]: 
array([[4., 6.],
       [2., 4.]])

Ваша функция сначала выполняет x-y; Итак, давайте сосредоточимся на этом:

In [383]: f = np.vectorize(lambda x,y:x-y, signature='(d),(d)->(d)')                           
In [384]: f(X[:,None,:], Y[None,:,:])                                                           
Out[384]: 
array([[[-2, -2, -2, -2],
        [-3, -3, -3, -3]],

       [[-1, -1, -1, -1],
        [-2, -2, -2, -2]]])

, но нам не нужно vectorize, чтобы сделать это:

In [385]: X[:,None,:]-Y[None,:,:]                                                              
Out[385]: 
array([[[-2, -2, -2, -2],
        [-3, -3, -3, -3]],

       [[-1, -1, -1, -1],
        [-2, -2, -2, -2]]])

norm принимает параметр axis; указание последнего дает тот же результат выполнения построчной калибровки c с vectorize:

In [387]: np.linalg.norm(X[:,None,:]-Y[None,:,:], axis=-1)                                     
Out[387]: 
array([[4., 6.],
       [2., 4.]])

Недостатком этого правильно «векторизованного» подхода является то, что транслируемая разница может получить очень большой, что приводит к ошибкам в памяти.

Так что np.vectorize является чем-то полезным, когда функция не может использовать более быстрые методы скомпилированного массива. Но это не инструмент скорости. Это медленнее, чем более явная итерация. Добавление signature интересно, но еще медленнее.

scipy.spatial.distance.pdist - хороший инструмент для парных расстояний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...