Векторизовать вычисление евклидова расстояния между матрицей и вектором

Я хочу рассчитать евклидово расстояние между матрицами и стандартным вектором. Все мои матрицы хранятся в списке, скажем, A, так что

A = [[1,2,3],[2,3,4]...,[8,9,10]],

И стандартный вектор, скажем, [1,1,1],

Я могу сделать это с помощью цикла for, но это действительно отнимает много времени, поскольку в A обычно сотни матриц. Как я могу векторизовать этот расчет, чтобы сократить время выполнения?


person HAOYANG MI    schedule 23.03.2019    source источник


Ответы (2)


Подход №1

Используйте np.einsum для вычисления расстояния. Чтобы решить наш случай здесь, мы могли бы сделать -

def dist_matrix_vec(matrix, vec):    
    d = np.subtract(matrix,vec)
    return np.sqrt(np.einsum('ij,ij->i',d,d))

Пробный прогон -

In [251]: A = [[1,2,3],[2,3,4],[8,9,10]]

In [252]: B = np.array([1,1,1])

In [253]: dist_matrix_vec(A,B)
Out[253]: array([ 2.23606798,  3.74165739, 13.92838828])

Подход №2

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

import numexpr as ne

def dist_matrix_vec_numexpr(matrix, vec): 
    matrix = np.asarray(matrix)
    vec = np.asarray(vec)
    return np.sqrt(ne.evaluate('sum((matrix-vec)**2,1)'))

Тайминги на больших массивах -

In [295]: np.random.seed(0)
     ...: A = np.random.randint(0,9,(10000,3))
     ...: B = np.random.randint(0,9,(3,))

In [296]: %timeit np.linalg.norm(A - B, axis = 1) #@Nathaniel's soln
     ...: %timeit dist_matrix_vec(A,B)
     ...: %timeit dist_matrix_vec_numexpr(A,B)
1000 loops, best of 3: 244 µs per loop
10000 loops, best of 3: 131 µs per loop
10000 loops, best of 3: 96.5 µs per loop

In [297]: np.random.seed(0)
     ...: A = np.random.randint(0,9,(100000,3))
     ...: B = np.random.randint(0,9,(3,))

In [298]: %timeit np.linalg.norm(A - B, axis = 1) #@Nathaniel's soln
     ...: %timeit dist_matrix_vec(A,B)
     ...: %timeit dist_matrix_vec_numexpr(A,B)
100 loops, best of 3: 5.31 ms per loop
1000 loops, best of 3: 1.43 ms per loop
1000 loops, best of 3: 918 µs per loop

На основе numexpr было 8 потоков. Таким образом, с увеличением количества потоков, доступных для вычислений, он должен улучшиться и дальше. Related post о том, как управлять многоядерными функциями.

person Divakar    schedule 24.03.2019

person    schedule
comment
Это самый быстрый способ? - person HAOYANG MI; 23.03.2019
comment
Вот один из нескольких вопросов, сравнивающих скорость разных методов. Я настоятельно рекомендую np.linalg.norm(), особенно для больших / многочисленных матриц. - person Nathaniel; 24.03.2019