С Python Numpy, как я могу наиболее эффективно вычесть матрицу NxM из матрицы Nx1 поэлементно? - PullRequest
0 голосов
/ 29 августа 2018

Пусть x - матрица Numpy 3x4, определяемая как:

x = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
In: x
Out:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

Пусть y - матрица 3x1, определяемая как:

y = np.array([3, 6 ,9])
In: y
Out: array([3, 6, 9])

Как я мог бы наиболее эффективно вычесть y - x поэлементно, так что результат будет:

array([[ 2,  1,  0, -1],
       [ 1,  0, -1, -2],
       [ 0, -1, -2, -3]])

Единственный способ найти это:

-1.0*(x.T + (-1.0*y)).T

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

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Как уже отмечали другие, NumPy вещает ваш друг здесь. Обратите внимание, что из-за этих правил вещания в NumPy на самом деле гораздо реже используется операция транспонирования по сравнению с другими стеками, ориентированными на матрицу (читай: MATLAB / Octave).

ИЗД. (реорганизовано)

Ключ в том, чтобы получить массив правильной формы. Лучший способ - использовать нарезку с дополнительным значением np.newaxis / None. Но вы также можете использовать ndarray.reshape():

import numpy as np
x = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
y = np.array([3, 6 ,9]).reshape(-1, 1)  # same as: y = np.array([3, 6 ,9])[:, None]
y - x

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

import numpy as np
import numexpr as ne

x = np.random.randint(1, 100, (3, 4))
y = np.random.randint(1, 100, (3, 1))

%timeit y - x
# The slowest run took 43.14 times longer than the fastest. This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 879 ns per loop

%timeit ne.evaluate('y - x')
# The slowest run took 20.86 times longer than the fastest. This could mean that an intermediate result is being cached.
# 100000 loops, best of 3: 10.8 µs per loop

# not so exciting for small arrays, but for somewhat larger numbers...
x = np.random.randint(1, 100, (3000, 4000))
y = np.random.randint(1, 100, (3000, 1))

%timeit y - x
# 10 loops, best of 3: 33.1 ms per loop
%timeit ne.evaluate('y - x')
# 100 loops, best of 3: 10.7 ms per loop

# which is roughly a factor 3 faster on my machine

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

import numpy as np

# creating the array does not depend too much as long as its size is the same

%timeit y = np.zeros((3000000))
# 838 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit y = np.zeros((3000000, 1))
# 825 µs ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit y = np.zeros((3000, 1000))
# 827 µs ± 14.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)


# ...and reshaping / slicing is independent of the array size

x = np.zeros(3000000)
%timeit x[:, None]
# 147 ns ± 4.02 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit x.reshape(-1, 1)
# 214 ns ± 9.55 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

x = np.zeros(300)
%timeit x[:, None]
# 146 ns ± 0.659 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit x.reshape(-1, 1)
# 212 ns ± 1.56 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Само собой разумеется, что %timeit эталоны должны быть взяты с крошкой соли.

0 голосов
/ 29 августа 2018

Пусть y - матрица 3x1, определяемая как:

y = np.array([3, 6 ,9])

Это не матрица 3x1 ( подробнее здесь ):

>>> y.shape
(3,)

Матрица 3x1 производится с

>>> y_new = np.array([[3], [6], [9]])
>>> y_new.shape
(3, 1)

Или из вашего существующего y с:

>>> y_new = y[:, np.newaxis]

Когда у вас есть матрица 3x1 и 3x4, вы можете просто вычесть их

>>> x - y_new
...