6.10. Broadcasting

Cuando un operador binario recibe dos arrays cuyas formas no coinciden exactamente, numpy no lanza un error, sino que aplica broadcasting. El broadcasting es un pequeño conjunto de reglas que deciden si dos formas son compatibles y, si lo son, cómo se estira virtualmente la más pequeña para igualar a la más grande.

6.10.1. Las reglas

Cuando los dos operandos tienen formas A y B, numpy las procesa en dos pasos.

  1. Igualar los rangos. Si un operando tiene menos ejes que el otro, numpy rellena virtualmente el frente de su forma con ejes de tamaño 1 hasta que ambas formas tengan el mismo número de ejes. Un operando 1-D de forma (3,) emparejado con un operando 2-D de forma (2, 3) se convierte en (1, 3) frente a (2, 3).

  2. Comprobar cada eje. Recorriendo las formas ahora de igual longitud eje por eje, cada par de tamaños debe satisfacer una de dos condiciones: los tamaños son iguales, o uno de ellos es 1. Un eje de tamaño 1 se estira virtualmente al tamaño del otro lado para la operación. El par (1, 3) frente a (2, 3) es compatible porque el primer eje tiene un 1 (se estira a 2) y el segundo eje coincide (3 == 3); el resultado tiene forma (2, 3).

Si algún par de ejes no satisface ninguna de las condiciones, las formas son incompatibles y el operador lanza ValueError.

6.10.2. Ejemplos

Escalar frente a cualquier array. El escalar actúa como una forma (1,) y se estira a cualquier cosa:

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10                 # (2, 3) + scalar -> (2, 3)

Vector 1-D sobre una matriz 2-D. La regla 1 antepone un eje de tamaño 1 para convertir (3,) en (1, 3); la regla 2 estira luego esa fila por cada columna de a:

row = np.array([100, 200, 300], dtype=np.float)
a + row                # (2, 3) + (3,) -> (2, 3)

Dos arrays 1-D de igual longitud se suman elemento a elemento – no se necesita broadcasting:

np.arange(4) + np.arange(4)

Un vector columna frente a un vector fila produce una forma «exterior» 2-D: (4, 1) emparejado con (3,) se convierte en (4, 1) frente a (1, 3) tras el relleno de rango, y la regla 2 estira cada operando a lo largo de su eje de tamaño 1:

x = np.array([1, 2, 3, 4]).reshape((4, 1))     # column
y = np.array([10, 20, 30])                      # row
x + y                                           # (4, 3) matrix

Las mismas reglas de forma se aplican a cualquier ufunc de dos argumentos, incluida arctan2():

np.arctan2(y, 1.0)
np.arctan2(y, x)

6.10.3. Qué no reserva el broadcasting

El estiramiento es virtual. numpy recorre ambos operandos a la vez, releyendo el más pequeño a lo largo de su eje de broadcasting en lugar de copiarlo. Los datos del array más corto nunca se replican en memoria.

Lo que importa para la memoria es el tamaño del array de salida. a + row reserva una salida con la forma de a, no la forma de a más la forma de row. Las cadenas largas de broadcasting pueden, aun así, producir resultados intermedios grandes.

6.10.4. Cuando el broadcasting sale mal

El fallo clásico son dos formas en las que ninguna tiene un eje de tamaño 1 que estirar y los tamaños no coinciden – (3, 4) frente a (4, 3), por ejemplo. La regla 2 no puede emparejar un 3 con un 4, así que numpy lanza ValueError.

Un problema más sutil es un broadcasting que tiene éxito, pero no de la forma que la aplicación pretendía. (5,) frente a (5, 1) es el caso canónico: el relleno de rango convierte el (5,) en (1, 5), que hace broadcasting frente a (5, 1) para producir una matriz (5, 5) – la combinación exterior de los dos vectores, no el resultado elemento a elemento de longitud 5 que la aplicación probablemente quería. En caso de duda, imprime shape en ambos lados y repasa las reglas paso a paso antes de recurrir a reshape() o transpose().