#python #numpy #matrix
#python #numpy #матрица
Вопрос:
У меня есть матрица M1
формы (N*2)
и другая матрица M2
(2*N)
, я хочу получить результат (N)
, каждый элемент i
является произведением i
й строки M1
и i
й колонки M2
. Я пытался использовать dot в NumPy, но это может дать мне только результат умножения матрицы, то есть (N*N)
, конечно, я могу взять диагональ, которая мне нужна, я хотел бы знать, есть ли лучший способ сделать это?
Ответ №1:
Подход #1
Вы можете использовать np.einsum
—
np.einsum('ij,ji->i',M1,M2)
Объяснение :
Исходное циклическое решение выглядело бы примерно так —
def original_app(M1,M2):
N = M1.shape[0]
out = np.zeros(N)
for i in range(N):
out[i] = M1[i].dot(M2[:,i])
return out
Таким образом, для каждой итерации мы имеем :
out[i] = M1[i].dot(M2[:,i])
Глядя на итератор, нам нужно выровнять первую ось M1
со второй осью M2
. Опять же, поскольку мы выполняем matrix-multiplication
, и это по самому своему определению выравнивает вторую ось M1
с первой осью M2
, а также уменьшает сумму этих элементов на каждой итерации.
При переносе на einsum
, держите оси выровненными между двумя входами, чтобы иметь одну и ту же строку при указании для нее строковой нотации. Таким образом, входные данные будут 'ij,ji
для M1
и M2
соответственно. Вывод после потери второй строки from M1
, которая совпадает с первой строкой from M2
в этом уменьшении суммы, должен быть оставлен как i
. Таким образом, полная строковая нотация будет : 'ij,ji->i'
и окончательное решение как : np.einsum('ij,ji->i',M1,M2)
.
Подход # 2
Количество столбцов в M1
или количество строк в M2
равно 2
. Итак, в качестве альтернативы, мы можем просто нарезать, выполнить поэлементное умножение и суммировать их, например —
M1[:,0]*M2[0] M1[:,1]*M2[1]
Тест во время выполнения
In [431]: # Setup inputs
...: N = 1000
...: M1 = np.random.rand(N,2)
...: M2 = np.random.rand(2,N)
...:
In [432]: np.allclose(original_app(M1,M2),np.einsum('ij,ji->i',M1,M2))
Out[432]: True
In [433]: np.allclose(original_app(M1,M2),M1[:,0]*M2[0] M1[:,1]*M2[1])
Out[433]: True
In [434]: %timeit original_app(M1,M2)
100 loops, best of 3: 2.09 ms per loop
In [435]: %timeit np.einsum('ij,ji->i',M1,M2)
100000 loops, best of 3: 13 µs per loop
In [436]: %timeit M1[:,0]*M2[0] M1[:,1]*M2[1]
100000 loops, best of 3: 14.2 µs per loop
Огромное ускорение есть!
Комментарии:
1. хороший ответ, позвольте мне переварить его на некоторое время. einsum является мощным, но, похоже, новичку нелегко понять.
2. не могли бы вы немного объяснить, как это работает? Я читаю документацию, это трудно понять
3. @1a1a11a Посмотрите, имеют ли смысл добавленные комментарии.
4. Спасибо за ответ, это здорово, оба решения, я попробовал оба, стоит отметить, что для второго подхода может потребоваться выполнить np.squeeze(np.asarray(M1[:,0])) и для всех следующих фрагментов, в противном случае это может стать матричным умножением и датьматрица
N*N
.5. Мне все еще трудно понять einsum, мне, вероятно, нужно снова прочитать документацию.