Difusión de NumPy para Computación Eficiente

NumPyBeginner
Practicar Ahora

Introducción

La difusión (broadcasting) es un concepto fundamental en NumPy que permite operaciones aritméticas elemento a elemento en arrays de diferentes formas. Esta potente característica elimina la necesidad de bucles explícitos, lo que resulta en un código más conciso y computacionalmente eficiente. En este laboratorio, aprenderá las reglas de la difusión y las aplicará a ejemplos prácticos escribiendo y ejecutando scripts de Python.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 97%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Difusión de un Escalar a un Array

La forma más sencilla de difusión ocurre cuando se realiza una operación entre un array y un solo número (un escalar). NumPy "estira" o "difunde" automáticamente el escalar para que coincida con la forma del array.

Primero, localice el archivo broadcasting.py en el explorador de archivos del lado izquierdo de su pantalla. Haga doble clic en él para abrirlo en el editor.

Ahora, reemplace el contenido de broadcasting.py con el siguiente código. Este código crea un array NumPy 1D y lo multiplica por un valor escalar.

import numpy as np

## Create a 1D array
a = np.array([1.0, 2.0, 3.0])

## Define a scalar
b = 2.0

## Multiply the array by the scalar
## The scalar 'b' is broadcast to the shape of 'a'
result = a * b

print("Array 'a':", a)
print("Scalar 'b':", b)
print("Result of a * b:", result)

Para ver el resultado, necesita ejecutar el script. Abra una terminal haciendo clic en el icono + en el panel de terminal en la parte inferior de la pantalla y seleccionando Terminal. Luego, ejecute el siguiente comando:

python broadcasting.py

Debería ver la siguiente salida, donde cada elemento del array a ha sido multiplicado por 2.0.

Array 'a': [1. 2. 3.]
Scalar 'b': 2.0
Result of a * b: [2. 4. 6.]

Difusión de Dos Arrays con Formas Compatibles

La difusión también funciona entre dos arrays si sus formas son compatibles. La regla general para la compatibilidad es: al comparar las dimensiones de dos arrays de derecha a izquierda, cada par de dimensiones debe ser igual o una de ellas debe ser 1.

Veamos un ejemplo de suma de un array 1D a un array 2D. El array 1D se difundirá a través de cada fila del array 2D.

Actualice su archivo broadcasting.py con el siguiente código:

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create a 1D array (shape: 3,)
b = np.array([10.0, 20.0, 30.0])

## Add the two arrays
## 'b' is broadcast to shape (2, 3) to match 'a'
## It becomes [[10. 20. 30.], [10. 20. 30.]] internally
result = a + b

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))
print("Result of a + b:\n", result)

Ejecute el script de nuevo en la terminal:

python broadcasting.py

La salida muestra que el array 1D b se sumó a cada fila del array 2D a.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (3,)): [10. 20. 30.]
Result of a + b:
 [[11. 22. 33.]
 [14. 25. 36.]]

Comprender Formas Incompatibles

La difusión fallará si las formas de los arrays no son compatibles según las reglas. Esto resulta en un ValueError. Comprender cuándo ocurre esto es crucial para la depuración.

Intentemos una operación con formas incompatibles. Aquí, intentaremos sumar un array de forma (2, 3) con un array de forma (2,). NumPy compara las dimensiones finales (3 y 2), descubre que no son iguales y que ninguna de ellas es 1. Esto provocará un error.

Modifique su archivo broadcasting.py para que contenga el siguiente código:

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create an incompatible 1D array (shape: 2,)
b = np.array([1.0, 2.0])

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))

## This will raise a ValueError
try:
    result = a + b
except ValueError as e:
    print("\nError:", e)

Ejecute el script desde la terminal:

python broadcasting.py

Como se esperaba, el programa captura un ValueError e imprime un mensaje de error explicando que las formas no están alineadas. Este es el comportamiento correcto y esperado para formas incompatibles.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (2,)): [1. 2.]

Error: operands could not be broadcast together with shapes (2,3) (2,)

Ejemplo Práctico - Cuantización Vectorial

Apliquemos la difusión a un problema práctico. La Cuantización Vectorial (VQ) es una técnica utilizada en la compresión y clasificación de datos. Un paso clave es encontrar el vector de "código" más cercano de un "codebook" (libro de códigos) a un vector de "observación" dado. La difusión hace que este cálculo sea eficiente.

Imaginemos que tenemos una observación (por ejemplo, el peso y la altura de un atleta) y un libro de códigos de diferentes tipos de atletas. Queremos encontrar a qué tipo se parece más nuestra observación.

Reemplace el código en broadcasting.py con el siguiente:

import numpy as np

## An observation vector (e.g., weight, height)
observation = np.array([111.0, 188.0])

## A codebook of vectors
codes = np.array([[102.0, 203.0],
                  [132.0, 193.0],
                  [45.0, 155.0],
                  [57.0, 173.0]])

## Use broadcasting to subtract the observation from all codes at once
## observation (2,) is broadcast to (4, 2)
diff = codes - observation

## Calculate the squared Euclidean distance
dist_sq = np.sum(diff**2, axis=-1)

## Find the index of the minimum distance
closest_index = np.argmin(dist_sq)

## Get the closest code from the codebook
closest_code = codes[closest_index]

print("Observation:", observation)
print("Codebook:\n", codes)
print("Distances squared:", dist_sq)
print("Closest code index:", closest_index)
print("Closest code:", closest_code)

Ejecute el script en su terminal:

python broadcasting.py

La salida mostrará las distancias al cuadrado desde la observación a cada código e identificará el vector de código coincidente más cercano. Todo este cálculo se realizó sin bucles explícitos de Python, gracias a la difusión.

Observation: [111. 188.]
Codebook:
 [[102. 203.]
 [132. 193.]
 [ 45. 155.]
 [ 57. 173.]]
Distances squared: [ 306.  466. 5445. 3141.]
Closest code index: 0
Closest code: [102. 203.]

Resumen

En este laboratorio, ha aprendido los fundamentos de la difusión de NumPy. Comenzó con el caso simple de difundir un escalar a un array, pasó a la difusión entre dos arrays compatibles y aprendió a reconocer formas incompatibles que generan errores. Finalmente, aplicó la difusión a un problema práctico de cuantización vectorial, demostrando su poder para escribir código limpio, eficiente y sin bucles para cálculos numéricos. Dominar la difusión es un paso clave para escribir código NumPy efectivo y profesional.