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().