6.10. Broadcasting¶
Wanneer een binaire operator twee arrays krijgt waarvan de vormen niet exact overeenkomen, werpt numpy geen fout op – het past broadcasting toe. Broadcasting is een kleine set regels die bepalen of twee vormen compatibel zijn en, zo ja, hoe de kleinere virtueel wordt uitgerekt om bij de grotere te passen.
6.10.1. De regels¶
Wanneer de twee operanden de vormen A en B hebben, werkt numpy deze in twee stappen af.
Stem de rangen op elkaar af. Als een operand minder assen heeft dan de andere, vult
numpyde voorkant van zijn vorm virtueel op met assen van grootte 1 totdat beide vormen evenveel assen hebben. Een 1D-operand van vorm(3,)gekoppeld aan een 2D-operand van vorm(2, 3)wordt(1, 3)tegenover(2, 3).Controleer elke as. Lopend door de nu even lange vormen, as voor as, moet elk paar groottes aan een van twee voorwaarden voldoen: de groottes zijn gelijk, of een ervan is 1. Een as van grootte 1 wordt virtueel uitgerekt tot de grootte van de andere kant voor de bewerking. Het paar
(1, 3)tegenover(2, 3)is compatibel omdat de eerste as een 1 heeft (rekt uit tot 2) en de tweede as overeenkomt (3 == 3); het resultaat heeft vorm(2, 3).
Als een aspaar aan geen van beide voorwaarden voldoet, zijn de vormen incompatibel en werpt de operator ValueError op.
6.10.2. Voorbeelden¶
Scalair tegenover een willekeurige array. De scalair gedraagt zich als vorm (1,) en rekt uit tot van alles:
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10 # (2, 3) + scalar -> (2, 3)
1D-vector over een 2D-matrix. Regel 1 zet een as van grootte 1 voorop om (3,) tot (1, 3) te maken; regel 2 rekt die rij vervolgens langs elke kolom van a uit:
row = np.array([100, 200, 300], dtype=np.float)
a + row # (2, 3) + (3,) -> (2, 3)
Twee 1D-arrays van gelijke lengte tellen elementsgewijs op – geen broadcasting nodig:
np.arange(4) + np.arange(4)
Een kolomvector tegenover een rijvector produceert een 2D-“buiten”-vorm: (4, 1) gekoppeld aan (3,) wordt (4, 1) tegenover (1, 3) na het vooraan toevoegen van de rang, en regel 2 rekt elke operand uit langs zijn as van grootte 1:
x = np.array([1, 2, 3, 4]).reshape((4, 1)) # column
y = np.array([10, 20, 30]) # row
x + y # (4, 3) matrix
Dezelfde vormregels gelden voor elke ufunc met twee argumenten, arctan2() inbegrepen:
np.arctan2(y, 1.0)
np.arctan2(y, x)
6.10.3. Wat broadcasting niet reserveert¶
Het uitrekken is virtueel. numpy doorloopt beide operanden samen en herleest de kleinere langs zijn broadcast-as in plaats van die te kopiëren. De gegevens van de kortere array worden nooit in het geheugen gedupliceerd.
De grootte van de uitvoer-array is wat telt voor het geheugen. a + row reserveert een uitvoer met de vorm van a, niet de vorm van a plus de vorm van row. Lange broadcasting-ketens kunnen alsnog grote tussenresultaten produceren.
6.10.4. Wanneer broadcasting misgaat¶
Het klassieke falen zijn twee vormen waarbij geen van beide een as van grootte 1 heeft om uit te rekken en de groottes niet overeenkomen – (3, 4) tegenover (4, 3) bijvoorbeeld. Regel 2 kan een 3 niet matchen tegen een 4, dus werpt numpy ValueError op.
Een subtieler probleem is een broadcast die slaagt, maar niet op de manier die de applicatie bedoelde. (5,) tegenover (5, 1) is het canonieke geval: het vooraan toevoegen van de rang verandert de (5,) in (1, 5), die broadcast tegen (5, 1) om een (5, 5)-matrix te produceren – de buitencombinatie van de twee vectoren, niet het elementsgewijze resultaat van lengte 5 dat de applicatie waarschijnlijk wilde. Print bij twijfel shape aan beide kanten en loop de regels stap voor stap door voordat je naar reshape() of transpose() grijpt.