Как уже отмечали другие, 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
эталоны должны быть взяты с крошкой соли.