6.10. Phát sóng (Broadcasting)

Khi một toán tử nhị phân nhận hai mảng có hình dạng không khớp chính xác, numpy không ném lỗi -- nó phát sóng. Phát sóng là một tập hợp nhỏ các quy tắc xác định xem hai hình dạng có tương thích không và, nếu có, cách hình dạng nhỏ hơn được kéo dài ảo để khớp với hình dạng lớn hơn.

6.10.1. Các quy tắc

Khi hai toán hạng có hình dạng AB, numpy xử lý chúng theo hai bước.

  1. Cân bằng số chiều. Nếu một toán hạng có ít trục hơn toán hạng kia, numpy ảo thêm các trục kích thước 1 vào đầu hình dạng của nó cho đến khi cả hai hình dạng có cùng số trục. Một toán hạng 1 chiều có hình dạng (3,) ghép với toán hạng 2 chiều có hình dạng (2, 3) trở thành (1, 3) đối chiếu với (2, 3).

  2. Kiểm tra từng trục. Duyệt các hình dạng có cùng độ dài theo từng trục, mỗi cặp kích thước phải thỏa mãn một trong hai điều kiện: kích thước bằng nhau, hoặc một trong số chúng là 1. Trục kích thước 1 được kéo dài ảo theo kích thước của phía kia cho phép toán. Cặp (1, 3) đối chiếu với (2, 3) tương thích vì trục đầu tiên có giá trị 1 (được kéo dài thành 2) và trục thứ hai khớp (3 == 3); kết quả có hình dạng (2, 3).

Nếu bất kỳ cặp trục nào không thỏa mãn điều kiện nào, các hình dạng không tương thích và toán tử ném ValueError.

6.10.2. Ví dụ

Vô hướng đối với bất kỳ mảng nào. Vô hướng hoạt động như hình dạng (1,) và được kéo dài thành bất kỳ thứ gì:

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10                 # (2, 3) + scalar -> (2, 3)

Vectơ 1 chiều qua ma trận 2 chiều. Quy tắc 1 thêm trục kích thước 1 để biến (3,) thành (1, 3); quy tắc 2 sau đó kéo dài hàng đó xuống từng cột của a

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

Hai mảng 1 chiều có cùng độ dài cộng theo từng phần tử -- không cần phát sóng:

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

Vectơ cột đối với vectơ hàng tạo ra hình dạng 2 chiều "outer": (4, 1) ghép với (3,) trở thành (4, 1) đối chiếu với (1, 3) sau khi thêm số chiều, và quy tắc 2 kéo dài từng toán hạng theo trục kích thước 1 của nó:

x = np.array([1, 2, 3, 4]).reshape((4, 1))     # column
y = np.array([10, 20, 30])                      # row
x + y                                           # (4, 3) matrix

Cùng quy tắc hình dạng áp dụng cho bất kỳ ufunc hai đối số nào, kể cả arctan2()

np.arctan2(y, 1.0)
np.arctan2(y, x)

6.10.3. Những gì phát sóng không cấp phát

Sự kéo dài là ảo. numpy duyệt cả hai toán hạng cùng nhau, đọc lại toán hạng nhỏ hơn theo trục phát sóng thay vì sao chép nó. Dữ liệu của mảng ngắn hơn không bao giờ được nhân bản trong bộ nhớ.

Kích thước của mảng đầu ra là điều quan trọng đối với bộ nhớ. a + row cấp phát đầu ra có hình dạng của a, không phải hình dạng của a cộng với hình dạng của row. Các chuỗi phát sóng dài vẫn có thể tạo ra các kết quả trung gian lớn.

6.10.4. Khi phát sóng xảy ra lỗi

Lỗi điển hình là hai hình dạng mà cả hai đều không có trục kích thước 1 để kéo dài và kích thước không khớp -- (3, 4) đối chiếu với (4, 3), chẳng hạn. Quy tắc 2 không thể khớp 3 với 4, vì vậy numpy ném ValueError.

Một vấn đề tinh tế hơn là một phép phát sóng thành công, nhưng không theo cách ứng dụng có ý định. (5,) đối chiếu với (5, 1) là trường hợp điển hình: việc thêm số chiều biến (5,) thành (1, 5), được phát sóng đối chiếu với (5, 1) để tạo ra ma trận (5, 5) -- tổ hợp outer của hai vectơ, không phải kết quả theo từng phần tử có độ dài 5 mà ứng dụng có thể muốn. Khi nghi ngờ, hãy in shape của cả hai phía và đi qua các quy tắc trước khi dùng reshape() hoặc transpose().