
Qué es broadcasting
Cuando se realiza una operación entre dos arrays de NumPy con la misma forma, la operación se aplica elemento a elemento. Pero en la práctica es habitual operar arrays con formas distintas: sumar un escalar a una matriz, restar un vector fila a cada fila de una matriz o combinar un vector columna con un vector fila.
Broadcasting es el mecanismo que NumPy utiliza para hacer compatibles arrays de formas diferentes en una operación aritmética. En lugar de copiar datos para igualar las dimensiones, NumPy "estira" virtualmente el array más pequeño para que coincida con el mayor, sin consumir memoria adicional.
import numpy as np
# Escalar + array: el escalar se "estira" a [5, 5, 5]
a = np.array([1, 2, 3])
resultado = a + 5
print(resultado) # [6 7 8]
Sin broadcasting, esta operación requeriría crear manualmente un array [5, 5, 5] para poder sumar elemento a elemento. NumPy lo hace de forma implícita y eficiente.
import numpy as np
# Matriz 2D + vector 1D
matriz = np.array([[10, 20, 30],
[40, 50, 60]])
descuento = np.array([1, 2, 3])
resultado = matriz - descuento
print(resultado)
# [[ 9 18 27]
# [39 48 57]]
En este caso, el vector descuento de forma (3,) se alinea con la última dimensión de la matriz (2, 3). NumPy replica virtualmente el vector en cada fila para completar la operación.
Broadcasting no crea copias de los datos. NumPy ajusta los strides internos del array para simular la replicación, lo que mantiene el consumo de memoria constante independientemente del tamaño del array mayor.
Las tres reglas de broadcasting
NumPy aplica tres reglas secuenciales para determinar si dos arrays son compatibles para broadcasting. Si alguna regla no se cumple, la operación lanza un ValueError.
Regla 1: alineación de dimensiones. Si los arrays tienen distinto número de dimensiones, se añade un 1 a la izquierda de la forma del array con menos dimensiones hasta igualar el número de ejes.
Regla 2: compatibilidad por eje. Dos dimensiones son compatibles si tienen el mismo tamaño o si una de ellas vale 1.
Regla 3: expansión. La dimensión con valor 1 se "estira" para igualar la otra dimensión. El array resultante tiene la forma máxima en cada eje.
flowchart TD
A["Array A shape: 4x3"] --- C{Comparar ejes}
B["Array B shape: 1x3"] --- C
C --> D["Eje 0: 4 vs 1<br>Compatible, se expande a 4"]
C --> E["Eje 1: 3 vs 3<br>Compatible, mismo tamaño"]
D --> F["Resultado shape: 4x3"]
E --> F
Un ejemplo paso a paso con las reglas:
import numpy as np
# A tiene forma (4, 3), B tiene forma (3,)
A = np.ones((4, 3))
B = np.array([10, 20, 30])
# Regla 1: B pasa de (3,) a (1, 3) para igualar dimensiones
# Regla 2: eje 0 -> 4 vs 1, compatible; eje 1 -> 3 vs 3, compatible
# Regla 3: B se expande en eje 0 de 1 a 4
resultado = A + B
print(resultado.shape) # (4, 3)
print(resultado)
# [[11. 21. 31.]
# [11. 21. 31.]
# [11. 21. 31.]
# [11. 21. 31.]]
Cuando las formas no son compatibles, NumPy indica el error de forma explícita:
import numpy as np
A = np.ones((3, 4))
B = np.ones((3,))
# Regla 1: B pasa de (3,) a (1, 3)
# Regla 2: eje 1 -> 4 vs 3, NO compatible (ninguno es 1)
# resultado = A + B # ValueError: operands could not be broadcast together
Tabla de compatibilidad
Algunos ejemplos de formas compatibles e incompatibles:
| Forma A | Forma B | Resultado | Compatible | |---------|---------|-----------|------------| | (5, 3) | (3,) | (5, 3) | Sí | | (5, 3) | (5, 1) | (5, 3) | Sí | | (1, 3) | (4, 1) | (4, 3) | Sí | | (3, 4) | (3,) | Error | No | | (2, 3) | (2,) | Error | No |
Patrones habituales de broadcasting
Escalar con array
El caso más simple. Un escalar tiene forma (), que se expande a cualquier dimensión.
import numpy as np
temperaturas = np.array([[15.2, 18.7, 22.1],
[14.8, 19.3, 21.5]])
# Convertir Celsius a Fahrenheit
fahrenheit = temperaturas * 9/5 + 32
print(fahrenheit)
# [[59.36 65.66 71.78]
# [58.64 66.74 70.7 ]]
Vector fila con matriz
Un vector de forma (n,) se alinea con la última dimensión de la matriz. Es el patrón que se usa para operar sobre cada columna de forma uniforme.
import numpy as np
# Ventas trimestrales de 3 productos en 4 trimestres
ventas = np.array([[100, 150, 200, 180],
[90, 120, 160, 140],
[110, 130, 190, 170]])
# Ponderacion por trimestre
pesos = np.array([0.2, 0.25, 0.3, 0.25])
ventas_ponderadas = ventas * pesos
print(ventas_ponderadas)
# [[ 20. 37.5 60. 45. ]
# [ 18. 30. 48. 35. ]
# [ 22. 32.5 57. 42.5]]
Vector columna con vector fila
Al combinar un vector columna (m, 1) con un vector fila (n,), se genera una matriz (m, n). Este patrón es útil para crear tablas de operaciones.
import numpy as np
filas = np.array([1, 2, 3, 4]).reshape(4, 1)
columnas = np.array([10, 20, 30])
# Tabla de multiplicacion
tabla = filas * columnas
print(tabla)
# [[ 10 20 30]
# [ 20 40 60]
# [ 30 60 90]
# [ 40 80 120]]
Aplicación práctica: normalización y centrado de datos
Broadcasting resulta especialmente útil en preprocesamiento de datos, donde es frecuente normalizar o centrar las columnas de un dataset. Estas operaciones requieren restar la media y dividir por la desviación típica de cada columna.
Centrado de datos
Centrar los datos consiste en restar la media de cada columna, de modo que cada variable tenga media cero.
import numpy as np
# Datos: 5 muestras, 3 variables
datos = np.array([[170, 65, 30],
[180, 80, 25],
[165, 55, 35],
[175, 70, 28],
[160, 50, 40]], dtype=float)
# Media por columna: shape (3,)
media = datos.mean(axis=0)
print(media) # [170. 64. 31.6]
# Centrado: broadcasting resta (3,) a cada fila de (5, 3)
datos_centrados = datos - media
print(datos_centrados)
# [[ 0. 1. -1.6]
# [ 10. 16. -6.6]
# [ -5. -9. 3.4]
# [ 5. 6. -3.6]
# [-10. -14. 8.4]]
Normalización estándar (Z-score)
La normalización estándar transforma los datos para que tengan media 0 y desviación típica 1. Se utiliza habitualmente antes de alimentar modelos de machine learning.
import numpy as np
datos = np.array([[170, 65, 30],
[180, 80, 25],
[165, 55, 35],
[175, 70, 28],
[160, 50, 40]], dtype=float)
media = datos.mean(axis=0) # shape (3,)
desviación = datos.std(axis=0) # shape (3,)
# Normalizacion: dos operaciones de broadcasting encadenadas
datos_normalizados = (datos - media) / desviación
print(datos_normalizados.mean(axis=0).round(2)) # [0. 0. 0.]
print(datos_normalizados.std(axis=0).round(2)) # [1. 1. 1.]
Normalización min-max
Otra técnica frecuente escala los valores al rango [0, 1] utilizando el mínimo y máximo de cada columna.
import numpy as np
datos = np.array([[170, 65, 30],
[180, 80, 25],
[165, 55, 35],
[175, 70, 28],
[160, 50, 40]], dtype=float)
mínimos = datos.min(axis=0) # shape (3,)
maximos = datos.max(axis=0) # shape (3,)
# Broadcasting: (5,3) - (3,) / ((3,) - (3,))
datos_escalados = (datos - mínimos) / (maximos - mínimos)
print(datos_escalados)
# [[0.5 0.5 0.33333333]
# [1. 1. 0. ]
# [0.25 0.16666667 0.66666667]
# [0.75 0.66666667 0.2 ]
# [0. 0. 1. ]]
En estas normalizaciones, broadcasting evita escribir bucles sobre las columnas. La operación se expresa en una sola línea y se ejecuta con la misma eficiencia que si se hubiera escrito en C. Este patrón es exactamente lo que hace internamente
StandardScaleroMinMaxScalerde scikit-learn.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, NumPy es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de NumPy
Explora más contenido relacionado con NumPy y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender el mecanismo de broadcasting en NumPy, aplicar las tres reglas de compatibilidad de formas y utilizar broadcasting en patrones habituales como normalización y centrado de datos.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje