6.10. 廣播¶
當二元運算子接收到兩個形狀並非完全相符的陣列時,numpy 並不會引發錯誤 -- 而是進行廣播(broadcasting)。廣播是一小組規則,用來決定兩個形狀是否相容,若相容,則決定較小的那個如何被虛擬地延展以符合較大的那個。
6.10.1. 規則¶
當兩個運算元的形狀分別為 A 與 B 時,numpy 會分兩個步驟處理它們。
對齊維度數(rank)。 若其中一個運算元的軸數比另一個少,
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() 之前逐步檢視這些規則。