Точечное произведение двух массивов numpy с трехмерными векторами - PullRequest
1 голос
/ 07 августа 2020

Моя цель - найти сегмент, ближайший (в массиве сегментов) к одной точке. Получение точечного произведения между массивами двумерных координат работает, но использование трехмерных координат дает следующую ошибку:

*ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)*


A = np.array([[1,1,1],[2,2,2]])
B = np.array([[3,3,3], [4,4,4]])

dp = np.dot(A,B)

dp должно возвращать 2 значения, скалярное произведение [1,1,1]@[3,3,3] и [2,2,2]@[4,4,4]

// Спасибо всем.

Вот окончательное решение для поиска ближайшего к одной точке отрезка линии. Любая оптимизация приветствуется.

import numpy as np
import time

#find closest segment to single point

then = time.time()

#random line segment
l1 = np.random.rand(1000000, 3)*10   
l2 = np.random.rand(1000000, 3)*10

#single point
p = np.array([5,5,5]) #only single point

#set to origin
line = l2-l1
pv = p-l1  

#length of line squared
len_sq = np.sum(line**2, axis = 1) #len_sq = numpy.einsum("ij,ij->i", line, line)

#dot product of 3D vectors with einsum
dot = np.einsum('ij,ij->i',line,pv) #np.sum(line*pv,axis=1)


#percentage of line the pv vector travels in
param = np.array([dot/len_sq])

#param<0 projected point=l1, param>1 pp=l2
clamped_param = np.clip(param,0,1)

#add line fraction to l1 to get projected point
pp = l1+(clamped_param.T*line)

##distance vector between single point and projected point
pp_p = pp-p

#sort by smallest distance between projected point and l1
index_of_mininum_dist = np.sum(pp_p**2, axis = 1).argmin()

print(index_of_mininum_dist)
print("FINISHED IN: ", time.time()-then)

Ответы [ 5 ]

2 голосов
/ 07 августа 2020

Вы имеете в виду это:

np.einsum('ij,ij->i',A,B)

вывод:

[ 9 24]

Однако, если вы хотите получить скалярное произведение каждой строки в A с каждой строкой в ​​B, вам следует сделать :

A@B.T

вывод:

[[ 9 12]
 [18 24]]
2 голосов
/ 07 августа 2020

np.dot работает только с векторами, а не с матрицами. При передаче матриц ожидается выполнение умножения матриц, которое не удастся из-за переданных размеров.

С вектором это будет работать так, как вы ожидали: go:

np.sum(A*B,axis=1)
1 голос
/ 07 августа 2020
In [265]: A = np.array([[1,1,1],[2,2,2]]) 
     ...: B = np.array([[3,3,3], [4,4,4]]) 

Элементное умножение с последующим суммированием отлично работает:

In [266]: np.sum(A*B, axis=1)                                                                        
Out[266]: array([ 9, 24])

einsum также упрощает выражение:

In [267]: np.einsum('ij,ij->i',A,B)                                                                  
Out[267]: array([ 9, 24])

dot с 2d массивами (здесь (2,3) shape), выполняет матричное умножение, classi c по строкам, по столбцам вниз. В обозначении einsum это 'ij, jk-> ik'.

In [268]: np.dot(A,B)                                                                                
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-268-189f80e2c351> in <module>
----> 1 np.dot(A,B)

<__array_function__ internals> in dot(*args, **kwargs)

ValueError: shapes (2,3) and (2,3) not aligned: 3 (dim 1) != 2 (dim 0)

При транспонировании размеры совпадают (2,3) с (3,2), но результат (2, 2):

In [269]: np.dot(A,B.T)                                                                              
Out[269]: 
array([[ 9, 12],
       [18, 24]])

Желаемые значения указаны по диагонали.

Один из способов подумать о проблеме - это то, что мы хотим сделать партию продуктов 1d. matmul/@ был добавлен для выполнения пакетного умножения матриц (чего не может dot). Но массивы должны быть расширены до 3d, поэтому размер пакета является ведущим (и 3 находятся в соответствующем последнем и от второго до последнего измерения):

In [270]: A[:,None,:]@B[:,:,None]       # (2,1,3) with (2,3,1)                                                              
Out[270]: 
array([[[ 9]],

       [[24]]])

Но результат (2 , 1,1) фасонный. Правильные числа есть, но нам нужно выжать дополнительные измерения.

В целом, тогда первые 2 решения самые простые - сумма или произведение или einsum эквивалент.

1 голос
/ 07 августа 2020

Точечное произведение numpy очевидно не предназначено для использования с массивами. Довольно легко написать обёртку вокруг него. Например:

def array_dot(A, B):
    return [A[i]@B[i] for i in range(A.shape[0])]
0 голосов
/ 07 августа 2020

Функция numpy .dot работает с объектами numpy .ndarray. Пример ниже работает.

import numpy as np

A = np.zeros((3, 3))
B = np.zeros((3, 3))
print(type(A))
# <class 'numpy.ndarray'>
C = np.dot(A, B)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...