вычитание среднего значения каждой строки в numpy с трансляцией

Я пытаюсь вычесть среднее значение каждой строки матрицы в numpy с помощью широковещания, но я получаю ошибку. Есть идеи почему?

вот код:

from numpy import *
X = random.rand(5, 10)
Y = X - X.mean(axis = 1)

ошибка:

ValueError: operands could not be broadcast together with shapes (5,10) (5,) 

спасибо!

2 ответов


на mean метод сокращение операция, то есть она преобразует 1-d коллекцию чисел в одно число. Когда вы применяете уменьшение к N-мерному массиву вдоль оси, numpy сворачивает это измерение до уменьшенного значения, в результате чего получается (n-1)-мерный массив. В вашем случае, так как X имеет форму (5, 10), и вы выполнили уменьшение вдоль оси 1, Вы получаете массив с формой (5,):

In [8]: m = X.mean(axis=1)

In [9]: m.shape
Out[9]: (5,)

при попытке вычитать этот результат от X, вы пытаетесь вычесть массив с формой (5,) из массива с формой (5, 10). Эти формы не совместимы для вещания. (Взгляните на описание вещания в руководстве пользователя.)

для вещания работать так, как вы хотите, результат mean операция должна быть массивом с формой (5, 1) (чтобы быть совместимой с формой (5, 10)). В последних версиях numpy сокращение операции, в том числе mean, есть аргумент под названием keepdims это говорит функции, чтобы не свернуть уменьшенное измерение. Вместо этого сохраняется тривиальное измерение с длиной 1:

In [10]: m = X.mean(axis=1, keepdims=True)

In [11]: m.shape
Out[11]: (5, 1)

С более старыми версиями numpy вы можете использовать reshape для восстановления свернутого измерения:

In [12]: m = X.mean(axis=1).reshape(-1, 1)

In [13]: m.shape
Out[13]: (5, 1)

Итак, в зависимости от вашей версии numpy вы можете сделать следующее:

Y = X - X.mean(axis=1, keepdims=True)

или такой:

Y = X - X.mean(axis=1).reshape(-1, 1)

если вы ищете производительность, вы можете также использовать np.einsum это предположительно быстрее, чем на самом деле с помощью np.sum или np.mean. Таким образом, желаемый результат может быть получен так -

X - np.einsum('ij->i',X)[:,None]/X.shape[1]

обратите внимание:[:,None] часть похожа на keepdims чтобы сохранить его размеры такими же, как у входного массива. Это может также использоваться в радиовещании.

во время выполнения тестов

1) Сравнение просто the mean расчет -

In [47]: X = np.random.rand(500, 1000)

In [48]: %timeit X.mean(axis=1, keepdims=True)
1000 loops, best of 3: 1.5 ms per loop

In [49]: %timeit X.mean(axis=1).reshape(-1, 1)
1000 loops, best of 3: 1.52 ms per loop

In [50]: %timeit np.einsum('ij->i',X)[:,None]/X.shape[1]
1000 loops, best of 3: 832 µs per loop

2) Сравнение всего расчета -

In [52]: X = np.random.rand(500, 1000)

In [53]: %timeit X - X.mean(axis=1, keepdims=True)
100 loops, best of 3: 6.56 ms per loop

In [54]: %timeit X - X.mean(axis=1).reshape(-1, 1)
100 loops, best of 3: 6.54 ms per loop

In [55]: %timeit X - np.einsum('ij->i',X)[:,None]/X.shape[1]
100 loops, best of 3: 6.18 ms per loop