Skip to content

NumPy: Regole ed esempi di trasmissione

Python

Nelle operazioni tra gli array NumPy (ndarray), ogni forma viene automaticamente convertita in modo che sia la stessa tramite la trasmissione.

In questo articolo vengono descritti i seguenti contenuti.

  • Regole di trasmissione in NumPy
  • Esempi di trasmissione in NumPy
    • Esempi di array 2D
    • Esempi di array 3D
  • Casi che non possono essere trasmessi
  • Funzioni per ottenere l’array trasmesso
    • Trasmetti un array a una forma specificata.:np.broadcast_to()
    • Trasmetti più matrice:np.broadcast_arrays()

La documentazione ufficiale che spiega la trasmissione è di seguito.

Usa reshape() o np.newaxis se vuoi rimodellare ndarray in qualsiasi forma tu voglia.

Regole di trasmissione in NumPy

Ci sono le seguenti due regole per la trasmissione in NumPy.

  1. Fai in modo che i due array hanno lo stesso numero di dimensioni.
    • Se i numeri delle dimensioni delle due matrici sono diversi, aggiungi nuove dimensioni con dimensione 1 all’inizio della matrice con la dimensione più piccola.
  2. Rendi ciascuna dimensione delle due matrici della stessa dimensione.
    • Se le dimensioni di ciascuna delle due matrici non corrispondono, le dimensioni con dimensione 1 vengono estese alla dimensione dell’altra matrice.
    • Se è presente una dimensione la cui dimensione non è 1 in nessuno dei due array, non può essere trasmesso e viene generato un errore.

Si noti che il numero di dimensioni di ndarray può essere ottenuto con l’attributo ndim e la forma con l’attributo shape.

Esempi di trasmissione in NumPy

Esempi di array 2D

Matrice 2D e matrice 1D

I seguenti array 2D e 1D sono usati come esempi. Per facilitare la comprensione del risultato della trasmissione, uno di loro usa zeros() per impostare tutti gli elementi su 0.

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,)

La forma dell’array 1D è (3,) invece di (3) perché le tuple con un elemento hanno una virgola alla fine.

Il risultato dell’aggiunta di questi due ndarray è il seguente.

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

Trasformiamo l’array con un numero di dimensioni minore (array 1D b) secondo le regole sopra l’altro.

Innanzitutto, secondo la regola 1, l’array viene trasformato dalla forma (3,) a (1, 3) aggiungendo una nuova dimensione di dimensione 1 alla testa. Viene utilizzato il metodo reshape().

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

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

Successivamente, la dimensione di ciascuna dimensione viene allungata secondo la regola 2. L’array viene allungato da (1, 3) a (3, 3). La parte allungata è una copia della parte originale. np.tile() viene utilizzato.

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

Nota che reshape() e np.tile() sono usati qui per motivi di spiegazione, ma se vuoi ottenere l’array trasmesso, ci sono funzioni np.broadcast_to() e np.broadcast_arrays() per quello scopo. Vedi sotto.

Matrice 2D e matrice 2D

Il risultato dell’addizione con l’array 2D di (3, 1) è il seguente.

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]]

In questo caso, poiché il numero di dimensioni è già lo stesso, l’array viene allungato da (3, 1) a (3, 3) secondo la regola 2.

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

Negli esempi precedenti, viene convertito solo uno degli array, ma ci sono casi in cui entrambi vengono convertiti tramite trasmissione.

Quanto segue è il risultato dell’aggiunta di matrici le cui forme sono (1, 3) e (3, 1).

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]]

Sia (1, 3) che (3, 1) sono allungati a (3, 3).

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]]

Lo stesso vale se uno di questi è un array 1D.

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]]

L’array 1D viene convertito come (4,) -> (1, 4) -> (3, 4) e l’array 2D come (3, 1) -> (3, 4).

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]]

Si noti che la dimensione viene allungata solo la dimensione originale è 1. In caso contrario, non può essere trasmesso e viene generato un errore, come deriva di seguito.

Esempi di array 3D

La regola 1 si applica anche se la differenza nel numero di dimensioni è dovuta o più.

Usando gli array 3D e 1D come esempi, i risultati dell’addizione sono i seguenti:

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]]]

La forma viene modificata come (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]]]

Casi che non possono essere trasmessi

Come accennato in precedenza, la dimensione viene allungata solo se la dimensione originale è 1. Se le dimensioni delle dimensioni sono diverse e le dimensioni di entrambi gli array non sono 1, non può essere trasmesso e viene generato un errore.

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) 

Lo stesso vale per il caso seguente.

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,) 

In questo esempio, se alla fine viene aggiunta una nuova dimensione, l’array può essere trasmesso.

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]]]

È facile capire se può essere trasmesso o meno dalla forma allineata a destra.

NG
(2, 3, 4)
(      3)

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

Se le dimensioni sono diverse allineate a destra e confrontate verticalmente, una di esse deve essere 1 per essere trasmesso.

Ad esempio, nel caso delle immagini, un’immagine a colori è un array 3D la cui forma è (altezza, larghezza, 3) (3 significa rosso, verde e blu), mentre un’immagine in scala di grigi è un array 2D la cui forma è (altezza , larghezza).

Nel caso di calcolo dell’altezza di ciascun colore in un’immagine a colori e del valore di un’immagine in scala di grigi, è impossibile trasmettere anche se l’immagine e la larghezza sono le stesse.

È necessario aggiungere una dimensione alla fine dell’immagine in scala di grigi con np.newaxis, np.expand_dims() e così via.

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

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

Funzioni per ottenere l’array trasmesso

Trasmetti un array a una forma specificata.:np.broadcast_to()

Usa np.broadcast_to() per trasmettere ndarray con la forma specificata.

Il primo argomento è il ndarray originale e il secondo è una tupla o una lista che indica la forma. Il ndarray viene trasmesso.

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'>

Si verifica un errore quando si specifica una forma che non può essere trasmesso.

# 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)

Trasmetti più matrice:np.broadcast_arrays()

Usa np.broadcast_arrays() per trasmettere più ndarray.

Specificare più array separati da virgole. Viene restituito un elenco di ndarray.

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'>

Si verifica un errore quando si specifica una combinazione di array che non possono essere trasmessi.

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