6.10. Emitiranje (broadcasting)¶
Kada binarni operator dobije dva polja čiji se oblici ne podudaraju točno, numpy ne podiže iznimku – on emitira (broadcast). Emitiranje je malen skup pravila koja odlučuju jesu li dva oblika kompatibilna i, ako jesu, kako se manji virtualno rasteže da se podudara s većim.
6.10.1. Pravila¶
Kada dva operanda imaju oblike A i B, numpy ih obrađuje u dva koraka.
Uskladi rangove. Ako jedan operand ima manje osi od drugog,
numpyvirtualno nadopunjuje prednji dio njegova oblika osima veličine 1 dok oba oblika nemaju isti broj osi. 1-D operand oblika(3,)uparen s 2-D operandom oblika(2, 3)postaje(1, 3)nasuprot(2, 3).Provjeri svaku os. Prolazeći sada jednako duge oblike os po os, svaki par veličina mora zadovoljiti jedan od dva uvjeta: veličine su jednake ili je jedna od njih 1. Os veličine 1 virtualno se rasteže na veličinu druge strane za operaciju. Par
(1, 3)nasuprot(2, 3)kompatibilan je jer prva os ima 1 (rasteže se na 2), a druga se os podudara (3 == 3); rezultat ima oblik(2, 3).
Ako neki par osi ne zadovoljava niti jedan uvjet, oblici su nekompatibilni i operator podiže ValueError.
6.10.2. Primjeri¶
Skalar nasuprot bilo kojem polju. Skalar se ponaša kao oblik (1,) i rasteže se na bilo što:
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10 # (2, 3) + scalar -> (2, 3)
1-D vektor preko 2-D matrice. Pravilo 1 dodaje os veličine 1 na početak da bi (3,) postalo (1, 3); pravilo 2 zatim rasteže taj redak prema dolje niz svaki stupac polja a
row = np.array([100, 200, 300], dtype=np.float)
a + row # (2, 3) + (3,) -> (2, 3)
Dva 1-D polja jednake duljine zbrajaju se element po element – emitiranje nije potrebno:
np.arange(4) + np.arange(4)
Stupčani vektor nasuprot retčanom vektoru proizvodi 2-D „vanjski” oblik: (4, 1) upareno s (3,) postaje (4, 1) nasuprot (1, 3) nakon dodavanja ranga na početak, a pravilo 2 rasteže svaki operand duž njegove osi veličine 1:
x = np.array([1, 2, 3, 4]).reshape((4, 1)) # column
y = np.array([10, 20, 30]) # row
x + y # (4, 3) matrix
Ista pravila o obliku vrijede za svaki ufunc s dva argumenta, uključujući arctan2()
np.arctan2(y, 1.0)
np.arctan2(y, x)
6.10.3. Što emitiranje ne alocira¶
Rastezanje je virtualno. numpy prolazi oba operanda zajedno, ponovno čitajući manji duž njegove osi emitiranja umjesto da ga kopira. Podaci kraćeg polja nikada se ne repliciraju u memoriji.
Veličina izlaznog polja ono je što je važno za memoriju. a + row alocira izlaz oblika polja a, a ne oblika polja a plus oblika polja row. Dugi lanci emitiranja ipak mogu proizvesti velike međurezultate.
6.10.4. Kada emitiranje pođe po krivu¶
Klasični neuspjeh dva su oblika gdje niti jedan nema os veličine 1 za rastezanje, a veličine se ne slažu – primjerice (3, 4) nasuprot (4, 3). Pravilo 2 ne može uskladiti 3 s 4, pa numpy podiže ValueError.
Suptilniji je problem emitiranje koje uspije, ali ne na način koji je aplikacija namjeravala. (5,) nasuprot (5, 1) kanonski je slučaj: dodavanje ranga na početak pretvara (5,) u (1, 5), što se emitira nasuprot (5, 1) i proizvodi matricu (5, 5) – vanjsku kombinaciju dvaju vektora, a ne rezultat duljine 5 po elementu kakav je aplikacija vjerojatno htjela. Kada ste u nedoumici, ispišite shape na obje strane i prođite kroz pravila prije nego posegnete za reshape() ili transpose().