Вещание
Примечание
См. эту статью для иллюстраций концепций вещания.
Термин вещание описывает, как NumPy обрабатывает массивы с различными формами во время арифметических операций. При определенных ограничениях меньший массив «вещательно» распространяется на больший массив, чтобы они имели совместимые формы. Вещание предоставляет способ векторизации операций с массивами, чтобы циклы выполнялись в C, а не в Python. Это делается без необходимости создавать лишние копии данных и обычно приводит к эффективной реализации алгоритмов. Однако есть случаи, когда вещание является плохой идеей, так как приводит к неэффективному использованию памяти, замедляя вычисления.
Операции NumPy обычно выполняются для пар массивов на элементной основе. В простейшем случае два массива должны иметь точно такую же форму, как в следующем примере:
>>> a = np.array([1.0, 2.0, 3.0]) >>> b = np.array([2.0, 2.0, 2.0]) >>> a * b array([ 2., 4., 6.])
Правило вещания NumPy ослабляет это ограничение, когда формы массивов удовлетворяют определенным ограничениям. Простейший пример вещания возникает, когда массив и скалярное значение объединяются в операции:
>>> a = np.array([1.0, 2.0, 3.0]) >>> b = 2.0 >>> a * b array([ 2., 4., 6.])
Результат эквивалентен предыдущему примеру, где b был массивом. Мы можем представить, что скаляр b растягивается во время арифметической операции в массив с той же формой, что и a. Новые элементы в b просто являются копиями исходного скаляра. Аналогия с растяжением — только концептуальная. NumPy достаточно умен, чтобы использовать исходное скалярное значение без фактического создания копий, чтобы операции вещания были максимально эффективными по памяти и вычислениям.
Код во втором примере более эффективен, чем в первом, потому что вещание перемещает меньше памяти во время умножения (b — скаляр, а не массив).
Общие правила вещания
При работе с двумя массивами NumPy сравнивает их формы поэлементно. Он начинает с последующих измерений и переходит вперед. Два измерения совместимы, когда
- они равны, или
- один из них равен 1
Если эти условия не выполнены, генерируется исключение ValueError: operands could not be broadcast together, указывающее на то, что массивы имеют несовместимые формы. Размер результирующего массива — это размер, который не равен 1 вдоль каждой оси входных данных.
Массивы не обязательно должны иметь одинаковое количество измерений. Например, если у вас есть 256x256x3 массив значений RGB, и вы хотите масштабировать каждый цвет на изображении на разное значение, вы можете умножить изображение на одномерный массив с 3 значениями. Выравнивание размеров последующих осей этих массивов в соответствии с правилами вещания показывает, что они совместимы:
Image (3d array): 256 x 256 x 3 Scale (1d array): 3 Result (3d array): 256 x 256 x 3
Когда одно из сравниваемых измерений равно единице, используется другое. Другими словами, измерения с размером 1 растягиваются или «копируются», чтобы соответствовать другому.
В следующем примере и массивы A, и массивы B имеют оси с длиной один, которые расширяются до большего размера во время операции вещания:
A (4d array): 8 x 1 x 6 x 1 B (3d array): 7 x 1 x 5 Result (4d array): 8 x 7 x 6 x 5
Вот еще несколько примеров:
A (2d array): 5 x 4 B (1d array): 1 Result (2d array): 5 x 4 A (2d array): 5 x 4 B (1d array): 4 Result (2d array): 5 x 4 A (3d array): 15 x 3 x 5 B (3d array): 15 x 1 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 1 Result (3d array): 15 x 3 x 5
Вот примеры форм, которые не вещаются:
A (1d array): 3 B (1d array): 4 # trailing dimensions do not match A (2d array): 2 x 1 B (3d array): 8 x 4 x 3 # second from last dimensions mismatched
Пример вещания на практике:
>>> x = np.arange(4)
>>> xx = x.reshape(4,1)
>>> y = np.ones(5)
>>> z = np.ones((3,4))
>>> x.shape
(4,)
>>> y.shape
(5,)
>>> x + y
ValueError: operands could not be broadcast together with shapes (4,) (5,)
>>> xx.shape
(4, 1)
>>> y.shape
(5,)
>>> (xx + y).shape
(4, 5)
>>> xx + y
array([[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.],
[ 4., 4., 4., 4., 4.]])
>>> x.shape
(4,)
>>> z.shape
(3, 4)
>>> (x + z).shape
(3, 4)
>>> x + z
array([[ 1., 2., 3., 4.],
[ 1., 2., 3., 4.],
[ 1., 2., 3., 4.]])
Вещание предоставляет удобный способ вычисления внешнего произведения (или любой другой внешней операции) двух массивов. Следующий пример показывает внешнюю операцию сложения двух одномерных массивов:
>>> a = np.array([0.0, 10.0, 20.0, 30.0])
>>> b = np.array([1.0, 2.0, 3.0])
>>> a[:, np.newaxis] + b
array([[ 1., 2., 3.],
[ 11., 12., 13.],
[ 21., 22., 23.],
[ 31., 32., 33.]])
Здесь оператор newaxis вставляет новую ось в a, превращая его в двумерный 4x1 массив. Объединение массива 4x1 с b, имеющим форму (3,), дает 4x3 массив.
© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.18/user/basics.broadcasting.html