6.10. Broadcasting

เมื่อตัวดำเนินการไบนารีได้รับอาร์เรย์สองตัวที่มีรูปร่างไม่ตรงกัน numpy จะไม่ raise -- มัน broadcast Broadcasting คือชุดกฎเล็กๆ ที่ตัดสินว่ารูปร่างสองแบบเข้ากันได้หรือไม่ และถ้าได้ จะขยายตัวที่เล็กกว่าเสมือนจริงให้ตรงกับตัวที่ใหญ่กว่าอย่างไร

6.10.1. กฎ

เมื่อตัวดำเนินการสองตัวมีรูปร่าง A และ B numpy จะทำงานผ่านกัน 2 ขั้นตอน

  1. จับคู่ rank หากตัวดำเนินการตัวหนึ่งมีแกนน้อยกว่าอีกตัว numpy จะเพิ่มแกนขนาด 1 ไว้ด้านหน้าของรูปร่างเสมือนจริงจนทั้งสองรูปร่างมีจำนวนแกนเท่ากัน ตัวดำเนินการ 1 มิติที่มีรูปร่าง (3,) ที่จับคู่กับตัวดำเนินการ 2 มิติที่มีรูปร่าง (2, 3) จะกลายเป็น (1, 3) เทียบกับ (2, 3)

  2. ตรวจสอบแต่ละแกน เมื่อเดินผ่านรูปร่างที่มีความยาวเท่ากันตอนนี้ทีละแกน แต่ละคู่ขนาดต้องตรงตามหนึ่งในสองเงื่อนไข: ขนาดเท่ากัน หรือหนึ่งในนั้นเป็น 1 แกนขนาด 1 จะถูกขยายเสมือนจริงให้เท่ากับขนาดอีกด้าน คู่ (1, 3) เทียบกับ (2, 3) เข้ากันได้เพราะแกนแรกมี 1 (ขยายเป็น 2) และแกนที่สองตรงกัน (3 == 3) ผลลัพธ์มีรูปร่าง (2, 3)

หากคู่แกนใดไม่ตรงตามเงื่อนไขใด รูปร่างจะไม่เข้ากันและตัวดำเนินการจะ raise 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 มิติข้ามเมทริกซ์ 2 มิติ กฎ 1 เพิ่มแกนขนาด 1 ด้านหน้าเพื่อให้ (3,) กลายเป็น (1, 3) จากนั้นกฎ 2 ขยายแถวนั้นลงแต่ละคอลัมน์ของ a

row = np.array([100, 200, 300], dtype=np.float)
a + row                # (2, 3) + (3,) -> (2, 3)

อาร์เรย์ 1 มิติสองตัวที่มีความยาวเท่ากัน บวกแบบ element-wise -- ไม่ต้องใช้ broadcasting:

np.arange(4) + np.arange(4)

เวกเตอร์คอลัมน์เทียบกับเวกเตอร์แถว สร้างรูปร่าง "outer" 2 มิติ: (4, 1) จับคู่กับ (3,) กลายเป็น (4, 1) เทียบกับ (1, 3) หลังการเพิ่ม rank และกฎ 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. สิ่งที่ broadcasting ไม่ จัดสรร

การขยายนั้นเสมือนจริง numpy เดินผ่านตัวดำเนินการทั้งสองพร้อมกัน โดยอ่านตัวที่เล็กกว่าซ้ำตามแกน broadcast แทนที่จะคัดลอก ข้อมูลของอาร์เรย์ที่สั้นกว่าไม่ถูกทำซ้ำในหน่วยความจำ

ขนาดของอาร์เรย์ ผลลัพธ์ คือสิ่งที่สำคัญสำหรับหน่วยความจำ a + row จัดสรรผลลัพธ์ตามรูปร่างของ a ไม่ใช่รูปร่างของ a บวกรูปร่างของ row เชน broadcasting ยาวยังสามารถสร้างค่ากลางขนาดใหญ่ได้

6.10.4. เมื่อ broadcasting ผิดพลาด

ความล้มเหลวแบบคลาสสิกคือรูปร่างสองแบบที่ไม่มีแกนขนาด 1 ให้ขยายและขนาดไม่ตรงกัน -- เช่น (3, 4) เทียบกับ (4, 3) กฎ 2 ไม่สามารถจับคู่ 3 กับ 4 ได้ ดังนั้น numpy จึง raise ValueError

ปัญหาที่ลึกซึ้งกว่าคือ broadcast ที่สำเร็จ แต่ไม่ใช่ตามที่แอปพลิเคชันตั้งใจ (5,) เทียบกับ (5, 1) คือกรณีต้นแบบ: การเพิ่ม rank เปลี่ยน (5,) เป็น (1, 5) ซึ่ง broadcast กับ (5, 1) ได้เมทริกซ์ (5, 5) -- การรวมกันแบบ outer ของเวกเตอร์ทั้งสอง ไม่ใช่ผลลัพธ์แบบ element-wise ความยาว 5 ที่แอปพลิเคชันอาจต้องการ เมื่อไม่แน่ใจ ให้พิมพ์ shape ของทั้งสองด้านและเดินผ่านกฎก่อนที่จะใช้ reshape() หรือ transpose()