Вещание
Примечание
См. эту статью для иллюстраций концепций вещания.
Термин вещание описывает, как 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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/user/basics.broadcasting.html