6.10. Широковещание¶
Когда бинарный оператор получает два массива, формы которых не совпадают в точности, numpy не вызывает ошибку – он выполняет широковещание. Широковещание – это небольшой набор правил, которые определяют, совместимы ли две формы, и если да, то как меньшая виртуально растягивается до совпадения с большей.
6.10.1. Правила¶
Когда два операнда имеют формы A и B, numpy обрабатывает их в два шага.
Выравнивание рангов. Если у одного операнда меньше осей, чем у другого,
numpyвиртуально дополняет начало его формы осями размера 1, пока обе формы не получат одинаковое число осей. Одномерный операнд формы(3,)в паре с двумерным операндом формы(2, 3)превращается в(1, 3)против(2, 3).Проверка каждой оси. Проходя по теперь равным по длине формам ось за осью, каждая пара размеров должна удовлетворять одному из двух условий: размеры равны или один из них равен 1. Ось размера 1 виртуально растягивается до размера противоположной стороны для выполнения операции. Пара
(1, 3)против(2, 3)совместима, потому что первая ось содержит 1 (растягивается до 2), а вторая ось совпадает (3 == 3); результат имеет форму(2, 3).
Если какая-либо пара осей не удовлетворяет ни одному из условий, формы несовместимы и оператор вызывает ValueError.
6.10.2. Примеры¶
Скаляр против любого массива. Скаляр действует как форма (1,) и растягивается до чего угодно:
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10 # (2, 3) + scalar -> (2, 3)
Одномерный вектор по двумерной матрице. Правило 1 добавляет в начало ось размера 1, превращая (3,) в (1, 3); правило 2 затем растягивает эту строку вниз по каждому столбцу a:
row = np.array([100, 200, 300], dtype=np.float)
a + row # (2, 3) + (3,) -> (2, 3)
Два одномерных массива равной длины складываются поэлементно – широковещание не требуется:
np.arange(4) + np.arange(4)
Вектор-столбец против вектора-строки образует двумерную «внешнюю» форму: (4, 1) в паре с (3,) после добавления ранга становится (4, 1) против (1, 3), и правило 2 растягивает каждый операнд вдоль его оси размера 1:
x = np.array([1, 2, 3, 4]).reshape((4, 1)) # column
y = np.array([10, 20, 30]) # row
x + y # (4, 3) matrix
Те же правила формы применяются к любой двухаргументной ufunc, включая arctan2():
np.arctan2(y, 1.0)
np.arctan2(y, x)
6.10.3. Что широковещание не выделяет¶
Растяжение виртуально. numpy проходит оба операнда совместно, перечитывая меньший вдоль его оси широковещания, а не копируя его. Данные меньшего массива никогда не дублируются в памяти.
Для памяти важен размер выходного массива. a + row выделяет выход формы a, а не формы a плюс формы row. Длинные цепочки широковещания всё же могут порождать большие промежуточные результаты.
6.10.4. Когда широковещание идёт не так¶
Классический сбой – две формы, ни у одной из которых нет оси размера 1 для растяжения, а размеры не совпадают – например, (3, 4) против (4, 3). Правило 2 не может сопоставить 3 с 4, поэтому numpy вызывает ValueError.
Более тонкая проблема – широковещание, которое проходит успешно, но не так, как задумало приложение. (5,) против (5, 1) – канонический случай: добавление ранга превращает (5,) в (1, 5), что широковещается против (5, 1), образуя матрицу (5, 5) – внешнюю комбинацию двух векторов, а не поэлементный результат длины 5, который, вероятно, нужен был приложению. В случае сомнений выведите shape для обеих сторон и пройдите по правилам, прежде чем прибегать к reshape() или transpose().