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.
Potrivirea rangurilor. Dacă un operand are mai puține axe decât celălalt,
numpycompletează 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).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().