Skip to content

NumPy: Broadcasting-Regeln und Beispiele

Python

Bei Operationen zwischen NumPy-Arrays (ndarray) wird jede Form automatisch durch Senden in dieselbe umgewandelt.

Dieser Artikel hat folgenden Inhalt.

  • Übertragungsregeln in NumPy
  • Broadcasting-Beispiele in NumPy
    • Beispiele für 2D-Array
    • Beispiele für 3D-Array
  • Fälle, die nicht übertragen werden können
  • Funktionen zum Abrufen des gesendeten Arrays
    • Übertragen Sie ein Array an eine bestimmte Form.:np.broadcast_to()
    • Senden Sie mehrere Arrays:np.broadcast_arrays()

Die offizielle Dokumentation zur Erläuterung der Übertragung finden Sie unten.

Verwenden Sie reshape() oder np.newaxis, wenn Sie ndarray in einer beliebigen Form umformen möchten.

Übertragungsregeln in NumPy

Es gibt die folgenden zwei Regeln für das Senden in NumPy.

  1. Stellen Sie sicher, dass die beiden Arrays die gleiche Anzahl von Dimensionen haben.
    • Wenn die Anzahl der Dimensionen der beiden Arrays unterschiedlich ist, fügen Sie neue Dimensionen mit der Größe 1 zum Kopf des Arrays mit der kleineren Dimension hinzu.
  2. Machen Sie jede Dimension der beiden Arrays gleich groß.
    • Wenn die Größen der einzelnen Dimensionen der beiden Arrays nicht übereinstimmen, werden Dimensionen mit der Größe 1 auf die Größe des anderen Arrays gestreckt.
    • Wenn es in einem der beiden Arrays eine Dimension gibt, deren Größe nicht 1 ist, kann sie nicht gesendet werden, und es wird ein Fehler ausgelöst.

Beachten Sie, dass die Anzahl der Dimensionen von ndarray mit dem ndim-Attribut und die Form mit dem shape-Attribut abgerufen werden können.

Broadcasting-Beispiele in NumPy

Beispiele für 2D-Array

2D-Array und 1D-Array

Die following 2D- und 1D-Arrays werden als Beispiele verwendet. Um das Ergebnis des Broadcasts besser verständlich zu machen, used Einer von ihnen zeros(), um alle Elemente auf 0 zu setzen.

import numpy as np

a = np.zeros((3, 3), np.int)
print(a)
# [[0 0 0]
#  [0 0 0]
#  [0 0 0]]

print(a.shape)
# (3, 3)

b = np.arange(3)
print(b)
# [0 1 2]

print(b.shape)
# (3,)

Die Form des 1D-Arrays ist (3,) anstelle von (3), da Tupel mit einem Element ein Komma am Ende haben.

Das Ergebnis der Addition dieser beiden ndarray ist wie folgt.

print(a + b)
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

Lassen Sie uns das Array mit einer kleineren Anzahl von Dimensionen (1D-Array b) gemäß den oben beschriebenen Regeln transformieren.

Zuerst WIRD gemäß Regel 1 das Array von der Form (3,) in (1, 3) transformiert, indem eine neue Dimension der Größe 1 am Kopf hinzugefügt WIRD. Es WIRD die Methode reshape() verwendet.

b_1_3 = b.reshape(1, 3)
print(b_1_3)
# [[0 1 2]]

print(b_1_3.shape)
# (1, 3)

Als nächstes wird die Größe jeder Dimension gemäß Regel 2 gestreckt. Das Array wird von (1, 3) nach (3, 3) gestreckt. Der gestreckte Teil ist eine Kopie des Originalteils. np.tile() WIRD verwendet.

print(np.tile(b_1_3, (3, 1)))
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

Beachten SIE, dass reshape() und np.tile() hier zur Erklärung used Werden, aber WENN SIE das gesendete Array erhalten möchten, gibt es für diesen Zweck die Funktionen np.broadcast_to() und np.broadcast_arrays(). Siehe unten.

2D-Array und 2D-Array

Das Ergebnis der Addition mit dem 2D-Array von (3, 1) ist wie folgt.

b_3_1 = b.reshape(3, 1)
print(b_3_1)
# [[0]
#  [1]
#  [2]]

print(b_3_1.shape)
# (3, 1)

print(a + b_3_1)
# [[0 0 0]
#  [1 1 1]
#  [2 2 2]]

Da in diesem Fall die Anzahl der Dimensionen bereits gleich ist, wird das Array gemäß Regel 2 von (3, 1) nach (3, 3) gestreckt.

print(np.tile(b_3_1, (1, 3)))
# [[0 0 0]
#  [1 1 1]
#  [2 2 2]]

In den bisherigen Beispielen wird nur eines der Arrays konvertiert, aber es gibt Fälle, in denen beide durch Broadcasting konvertiert werden.

Das Folgende ist das Ergebnis des Hinzufügens von Arrays, deren Formen (1, 3) und (3, 1) sind.

print(b_1_3)
# [[0 1 2]]

print(b_1_3.shape)
# (1, 3)

print(b_3_1)
# [[0]
#  [1]
#  [2]]

print(b_3_1.shape)
# (3, 1)

print(b_1_3 + b_3_1)
# [[0 1 2]
#  [1 2 3]
#  [2 3 4]]

Beide (1, 3) als auch (3, 1) werden zu (3, 3) gestreckt.

print(np.tile(b_1_3, (3, 1)))
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

print(np.tile(b_3_1, (1, 3)))
# [[0 0 0]
#  [1 1 1]
#  [2 2 2]]

print(np.tile(b_1_3, (3, 1)) + np.tile(b_3_1, (1, 3)))
# [[0 1 2]
#  [1 2 3]
#  [2 3 4]]

Dasselbe gilt, wenn einer von ihnen ein 1D-Array ist.

c = np.arange(4)
print(c)
# [0 1 2 3]

print(c.shape)
# (4,)

print(b_3_1)
# [[0]
#  [1]
#  [2]]

print(b_3_1.shape)
# (3, 1)

print(c + b_3_1)
# [[0 1 2 3]
#  [1 2 3 4]
#  [2 3 4 5]]

1D-Array wird wie (4,) -> (1, 4) -> (3, 4) und 2D-Array wie (3, 1) -> (3, 4) konvertiert.

print(np.tile(c.reshape(1, 4), (3, 1)))
# [[0 1 2 3]
#  [0 1 2 3]
#  [0 1 2 3]]

print(np.tile(b_3_1, (1, 4)))
# [[0 0 0 0]
#  [1 1 1 1]
#  [2 2 2 2]]

print(np.tile(c.reshape(1, 4), (3, 1)) + np.tile(b_3_1, (1, 4)))
# [[0 1 2 3]
#  [1 2 3 4]
#  [2 3 4 5]]

Beachten Sie, dass die Dimension nur gestreckt wird, wenn die Originalgröße 1 ist. Andernfalls kann sie nicht gesendet werden und es wird ein Fehler ausgelöst, wie unten beschrieben.

Beispiele für 3D-Array

Regel 1 gilt auch dann, wenn der Unterschied in der Anzahl der Dimensionen zwei oder mehr beträgt.

Am Beispiel von 3D- und 1D-Arrays sehen die Additionsergebnisse wie folgt aus:

a = np.zeros((2, 3, 4), dtype=np.int)
print(a)
# [[[0 0 0 0]
#   [0 0 0 0]
#   [0 0 0 0]]
# 
#  [[0 0 0 0]
#   [0 0 0 0]
#   [0 0 0 0]]]

print(a.shape)
# (2, 3, 4)

b = np.arange(4)
print(b)
# [0 1 2 3]

print(b.shape)
# (4,)

print(a + b)
# [[[0 1 2 3]
#   [0 1 2 3]
#   [0 1 2 3]]
# 
#  [[0 1 2 3]
#   [0 1 2 3]
#   [0 1 2 3]]]

Die Form wird wie folgt geändert: (4, ) -> (1, 1, 4) -> (2, 3, 4).

b_1_1_4 = b.reshape(1, 1, 4)
print(b_1_1_4)
# [[[0 1 2 3]]]

print(np.tile(b_1_1_4, (2, 3, 1)))
# [[[0 1 2 3]
#   [0 1 2 3]
#   [0 1 2 3]]
# 
#  [[0 1 2 3]
#   [0 1 2 3]
#   [0 1 2 3]]]

Fälle, die nicht übertragen werden können

Wie oben erwähnt, wird die Dimension nur gestreckt, wenn die ursprüngliche Größe 1 ist. Wenn die Größen der Dimensionen unterschiedlich sind und die Größen beider Arrays nicht 1 sind, kann es nicht gesendet werden, und es wird ein Fehler ausgelöst.

a = np.zeros((4, 3), dtype=np.int)
print(a)
# [[0 0 0]
#  [0 0 0]
#  [0 0 0]
#  [0 0 0]]

print(a.shape)
# (4, 3)

b = np.arange(6).reshape(2, 3)
print(b)
# [[0 1 2]
#  [3 4 5]]

print(b.shape)
# (2, 3)

# print(a + b)
# ValueError: operands could not be broadcast together with shapes (4,3) (2,3) 

Dasselbe gilt für den folgenden Fall.

a = np.zeros((2, 3, 4), dtype=np.int)
print(a)
# [[[0 0 0 0]
#   [0 0 0 0]
#   [0 0 0 0]]
# 
#  [[0 0 0 0]
#   [0 0 0 0]
#   [0 0 0 0]]]

print(a.shape)
# (2, 3, 4)

b = np.arange(3)
print(b)
# [0 1 2]

print(b.shape)
# (3,)

# print(a + b)
# ValueError: operands could not be broadcast together with shapes (2,3,4) (3,) 

Wenn in diesem Beispiel am Ende eine neue Dimension hinzugefügt wird, kann das Array übertragen werden.

b_3_1 = b.reshape(3, 1)
print(b_3_1)
# [[0]
#  [1]
#  [2]]

print(b_3_1.shape)
# (3, 1)

print(a + b_3_1)
# [[[0 0 0 0]
#   [1 1 1 1]
#   [2 2 2 2]]
# 
#  [[0 0 0 0]
#   [1 1 1 1]
#   [2 2 2 2]]]

Es ist leicht zu verstehen, ob es durch die rechtsbündige Form gesendet werden kann oder nicht.

NG
(2, 3, 4)
(      3)

OK
(2, 3, 4)
(   3, 1) -> (1, 3, 1) -> (2, 3, 4)

Wenn die Größen rechtsbündig und vertikal unterschiedlich sind, muss eine davon 1 sein, um übertragen zu werden.

Im Fall von Bildern ist ein Farbbild beispielsweise ein 3D-Array mit der Form (Höhe, Breite, 3) (3 bedeutet Rot, Grün und Blau), während ein Graustufenbild ein 2D-Array mit der Form (Höhe , Breite).

Im Fall der Berechnung des Werts jeder Farbe in einem Farbbild und des Werts eines Graustufenbilds ist es unmöglich zu senden, selbst wenn Höhe und Breite gleich sind.

Sie müssen am Ende des Graustufenbilds mit np.newaxis, np.expand_dims() usw. eine Dimension hinzufügen.

NG
(h, w, 3)
(   h, w)

OK
(h, w, 3)
(h, w, 1) -> (h, w, 3)

Funktionen zum Abrufen des gesendeten Arrays

Übertragen Sie ein Array an eine bestimmte Form.:np.broadcast_to()

Verwenden Sie np.broadcast_to(), um ndarray mit der angegebenen Form zu senden.

Das erste Argument ist das ursprüngliche Ndarray, und das zweite ist ein Tupel oder eine Liste, die die Form angibt. Das gesendete ndarray wird zurückgesendet.

a = np.arange(3)
print(a)
# [0 1 2]

print(a.shape)
# (3,)

print(np.broadcast_to(a, (3, 3)))
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

print(type(np.broadcast_to(a, (3, 3))))
# <class 'numpy.ndarray'>

Beim Angeben Einer Form, die nicht gesendet werden kann, tritt ein Fehler auf.

# print(np.broadcast_to(a, (2, 2)))
# ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (3,) and requested shape (2,2)

Senden Sie mehrere Arrays:np.broadcast_arrays()

Verwenden Sie np.broadcast_arrays(), um mehrere ndarray zu übertragen.

Geben Sie mehrere durch Kommas getrennte Arrays an. Eine Liste von ndarray wird zurückgegeben.

a = np.arange(3)
print(a)
# [0 1 2]

print(a.shape)
# (3,)

b = np.arange(3).reshape(3, 1)
print(b)
# [[0]
#  [1]
#  [2]]

print(b.shape)
# (3, 1)

arrays = np.broadcast_arrays(a, b)

print(type(arrays))
# <class 'list'>

print(len(arrays))
# 2

print(arrays[0])
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

print(arrays[1])
# [[0 0 0]
#  [1 1 1]
#  [2 2 2]]

print(type(arrays[0]))
# <class 'numpy.ndarray'>

Beim Angeben einer Kombination von Arrays, die nicht gesendet werden können, tritt ein Fehler auf.

c = np.zeros((2, 2))
print(c)
# [[0. 0.]
#  [0. 0.]]

print(c.shape)
# (2, 2)

# arrays = np.broadcast_arrays(a, c)
# ValueError: shape mismatch: objects cannot be broadcast to a single shape