6.10. 廣播

當二元運算子接收到兩個形狀並非完全相符的陣列時,numpy 並不會引發錯誤 -- 而是進行廣播(broadcasting)。廣播是一小組規則,用來決定兩個形狀是否相容,若相容,則決定較小的那個如何被虛擬地延展以符合較大的那個。

6.10.1. 規則

當兩個運算元的形狀分別為 AB 時,numpy 會分兩個步驟處理它們。

  1. 對齊維度數(rank)。 若其中一個運算元的軸數比另一個少,numpy 會在其形狀的前端虛擬地補上大小為 1 的軸,直到兩個形狀的軸數相同。形狀為 (3,) 的一維運算元與形狀為 (2, 3) 的二維運算元配對時,會變成 (1, 3) 對上 (2, 3)

  2. 檢查每個軸。 逐軸走過現在已等長的形狀,每一對大小都必須滿足下列兩個條件之一:兩者大小相等,或其中一個為 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() 之前逐步檢視這些規則。