Вычислительное произведение i-й строки массива 1 и i-го столбца массива 2 — NumPy

#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, мне, вероятно, нужно снова прочитать документацию.