Broadcasting
Mathematische Operationen werden in numpy
so auf arrays angewendet, wie man das aus der Mathematik gewohnt ist. Wenn die Operanden unterschiedliche (formal inkompatible) Formen haben, so wird der kleinere der beiden Operanden nach einem festen Schema so ergänzt, dass die Operation möglich wird. Dieser Vorgang wird broadcasting genannt.
Der einfachste Fall ist die Addition eines Skalars zu einem array. Hier wird der Skalar auf alle Elemente des arrays addiert:
np.array([1, 2, 3]) + 1 # array([2, 3, 4])
Sofern die Dimensionen exakt übereinstimmen, so wird die Operation elementweise durchgeführt:
np.array([1, 2, 3]) + np.array([4, 5, 6]) # array([5, 7, 9])
Das Verfahren wird auch in höheren Dimensionen verwendet und erfolgt in folgenden Schritten:
- Die Dimensionen zweier arrays werden in umgekehrter Reihenfolge paarweise miteinander verglichen.
- Für jedes Paar müssen entweder beide Dimensionen gleich sein oder aber eine der beiden Dimensionen muss den Wert 1 haben. Sind beide Dimensionen gleich, wird die Operation elementweise durchgeführt. Ist eine der beiden Dimensionen 1, so wird das array in dieser Dimension mit Kopien des einzigen Wertes in dem array aufgefüllt und die Operation dann elementweise durchgeführt.
- Bei allen verbleibenden Dimensionen wird das array mit der kleineren Dimension mittels Dimensionen mit nur einem Element so ergänzt, dass die Anzahl der Dimensionen übereinstimmen.
Ein Beispiel:
np.identity(3) + np.array([1, 2, 3]) # array([[2., 2., 3.],
# [1., 3., 3.],
# [1., 2., 4.]])
Hier hat das array np.identity(3)
die Dimensionen (3, 3) und das array np.array([1, 2, 3])
die Dimensionen (3,). Die Dimensionen werden paarweise verglichen:
np.identity(3) -> 3, 3
np.array([1, 2, 3]) -> 3
In der letzten Dimension sind die beiden Einträge gleich, also kann elementweises Broadcasting anwendet werden. In der ersten Dimension gibt es keine Entsprechung, da das zweite array nur eine Dimension hat. Daher wird hier eine 1 angenommen:
np.identity(3) -> 3, 3
np.array([1, 2, 3]) -> 1, 3
Das entspricht logisch drei Kopien des zweiten arrays in der ersten Dimension:
np.identity(3) + np.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
Da die Kopie entlang der ersten axis erfolgt, handelt es sich um identische Zeilenvektoren. Wollte man stattdessen identische Spaltenvektoren haben, muss man numpy
anweisen, die Kopie entlang der zweiten axis zu machen, indem man eine künstliche axis im zweiten Operanden einfügt. Das gelingt z.B. mit reshape
:
np.identity(3) + np.array([1, 2, 3]).reshape(3, 1)
Das ist formal äquivalent zu:
np.identity(3) + np.array([[1], [2], [3]]) # array([[2., 1., 1.],
# [2., 3., 2.],
# [3., 3., 4.]])
Einen eleganten Weg mit np.newaxis
werden wir später kennenlernen.