6.10. Broadcasting

Când un operator binar primește două tablouri ale căror forme nu se potrivesc exact, numpy nu ridică o eroare – ci face broadcasting. Broadcasting-ul este un set restrâns de reguli care decid dacă două forme sunt compatibile și, dacă sunt, cum este întinsă virtual cea mai mică pentru a se potrivi cu cea mai mare.

6.10.1. Regulile

Când cei doi operanzi au formele A și B, numpy le parcurge în doi pași.

  1. Potrivirea rangurilor. Dacă un operand are mai puține axe decât celălalt, numpy completează virtual începutul formei sale cu axe de dimensiune 1 până când ambele forme au același număr de axe. Un operand 1-D de formă (3,) asociat cu un operand 2-D de formă (2, 3) devine (1, 3) față de (2, 3).

  2. Verificarea fiecărei axe. Parcurgând acum formele de lungime egală axă cu axă, fiecare pereche de dimensiuni trebuie să satisfacă una dintre două condiții: dimensiunile sunt egale sau una dintre ele este 1. O axă de dimensiune 1 este întinsă virtual până la dimensiunea celeilalte părți pentru operație. Perechea (1, 3) față de (2, 3) este compatibilă deoarece prima axă are un 1 (se întinde la 2), iar a doua axă se potrivește (3 == 3); rezultatul are forma (2, 3).

Dacă vreo pereche de axe nu satisface niciuna dintre condiții, formele sunt incompatibile, iar operatorul ridică ValueError.

6.10.2. Exemple

Un scalar față de orice tablou. Scalarul se comportă ca o formă (1,) și se întinde la orice:

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

Un vector 1-D peste o matrice 2-D. Regula 1 adaugă în față o axă de dimensiune 1 pentru a transforma (3,) în (1, 3); regula 2 întinde apoi acel rând în jos pe fiecare coloană a lui a

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

Două tablouri 1-D de lungime egală se adună element cu element – fără broadcasting:

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

Un vector coloană față de un vector linie produce o formă „exterioară” 2-D: (4, 1) asociat cu (3,) devine (4, 1) față de (1, 3) după adăugarea de rang, iar regula 2 întinde fiecare operand de-a lungul axei sale de dimensiune 1:

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

Aceleași reguli de formă se aplică oricărei ufunc cu două argumente, inclusiv arctan2()

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

6.10.3. Ce nu alocă broadcasting-ul

Întinderea este virtuală. numpy parcurge ambii operanzi împreună, recitind-ul pe cel mai mic de-a lungul axei sale de broadcast în loc să îl copieze. Datele tabloului mai scurt nu sunt niciodată replicate în memorie.

Dimensiunea tabloului de ieșire este cea care contează pentru memorie. a + row alocă o ieșire de forma lui a, nu de forma lui a plus forma lui row. Lanțurile lungi de broadcasting pot produce totuși rezultate intermediare mari.

6.10.4. Când broadcasting-ul eșuează

Eșecul clasic îl reprezintă două forme în care niciuna nu are o axă de dimensiune 1 de întins, iar dimensiunile nu coincid – (3, 4) față de (4, 3), de exemplu. Regula 2 nu poate potrivi un 3 cu un 4, așa că numpy ridică ValueError.

O problemă mai subtilă este un broadcast care reușește, dar nu în modul intenționat de aplicație. (5,) față de (5, 1) este cazul canonic: adăugarea de rang transformă (5,) în (1, 5), care face broadcasting față de (5, 1) pentru a produce o matrice (5, 5) – combinația exterioară a celor doi vectori, nu rezultatul element cu element de lungime 5 pe care aplicația îl dorea probabil. Când ai dubii, afișează shape pe ambele părți și parcurge pas cu pas regulile înainte de a recurge la reshape() sau transpose().