
NumPy - Operaciones Avanzadas con Arrays
Domina las operaciones avanzadas con arrays de NumPy: reshaping, concatenación, indexación booleana, broadcasting y más.
¡Bienvenido al segundo tutorial de NumPy! En esta entrega aprenderás a manipular arrays de manera avanzada, dominando técnicas esenciales para el procesamiento eficiente de datos numéricos.
Objetivo: Aprender operaciones avanzadas con arrays de NumPy, incluyendo reshaping, concatenación, indexación sofisticada y broadcasting.
Índice
- Reshaping de Arrays
- Concatenación y Apilamiento
- División de Arrays
- Transposición y Manipulación de Ejes
- Indexación Avanzada
- Indexación Booleana
- Broadcasting
- Copia vs Vista
- Proyecto Práctico - Procesamiento de Imágenes
- Próximos Pasos
- Conclusión
Reshaping de Arrays
Cambiar la forma de un array sin modificar sus datos:
import numpy as np
# Array original
arr = np.arange(12)
print("Array original (1D):", arr)
print("Forma:", arr.shape)
# Reshape a 2D
arr_2d = arr.reshape(3, 4)
print("\nReshape a 3x4:")
print(arr_2d)
print("Forma:", arr_2d.shape)
# Reshape a 3D
arr_3d = arr.reshape(2, 2, 3)
print("\nReshape a 2x2x3:")
print(arr_3d)
print("Forma:", arr_3d.shape)
Usos comunes de reshape
# Aplanar un array multidimensional
matriz = np.array([[1, 2, 3], [4, 5, 6]])
print("Matriz original:")
print(matriz)
aplanado = matriz.reshape(-1) # -1 significa "inferir dimensión"
print("\nArray aplanado:", aplanado)
# Cambiar a vector columna
vector_columna = arr.reshape(-1, 1)
print("\nVector columna:")
print(vector_columna)
print("Forma:", vector_columna.shape)
Redimensionamiento automático
# Usando -1 para inferir dimensiones
arr = np.arange(24)
# NumPy infiere la dimensión faltante
arr_4x6 = arr.reshape(4, -1)
print("4 filas, columnas inferidas:", arr_4x6.shape)
arr_2x3x4 = arr.reshape(2, 3, -1)
print("2x3x?, ? inferido:", arr_2x3x4.shape)
Concatenación y Apilamiento
Combinar múltiples arrays:
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print("Array A:")
print(a)
print("\nArray B:")
print(b)
Concatenación
# Concatenar horizontalmente (axis=1)
horizontal = np.concatenate((a, b), axis=1)
print("\nConcatenación horizontal:")
print(horizontal)
# Concatenar verticalmente (axis=0)
vertical = np.concatenate((a, b), axis=0)
print("\nConcatenación vertical:")
print(vertical)
Apilamiento
# Apilar verticalmente
vstack = np.vstack((a, b))
print("\nVertical stack:")
print(vstack)
# Apilar horizontalmente
hstack = np.hstack((a, b))
print("\nHorizontal stack:")
print(hstack)
# Apilar en profundidad
dstack = np.dstack((a, b))
print("\nDepth stack (3D):")
print(dstack)
print("Forma:", dstack.shape)
División de Arrays
Separar un array en múltiples partes:
arr = np.arange(12)
print("Array original:", arr)
# Dividir en partes iguales
partes = np.split(arr, 4)
print("\nDividido en 4 partes:")
for i, parte in enumerate(partes):
print(f"Parte {i}: {parte}")
División en posiciones específicas
# Dividir en posiciones específicas
arr = np.arange(10)
print("Array:", arr)
# Dividir en [2, 5, 8]
partes = np.split(arr, [2, 5, 8])
print("\nDivisiones en posiciones [2, 5, 8]:")
for i, parte in enumerate(partes):
print(f"Parte {i}: {parte}")
División de arrays 2D
matriz = np.arange(16).reshape(4, 4)
print("Matriz 4x4:")
print(matriz)
# Dividir verticalmente
vertical_split = np.vsplit(matriz, 2)
print("\nDivisión vertical:")
for i, parte in enumerate(vertical_split):
print(f"Parte {i}:")
print(parte)
print()
# Dividir horizontalmente
horizontal_split = np.hsplit(matriz, 2)
print("División horizontal:")
for i, parte in enumerate(horizontal_split):
print(f"Parte {i}:")
print(parte)
print()
Transposición y Manipulación de Ejes
Cambiar la orientación de los arrays:
matriz = np.array([[1, 2, 3], [4, 5, 6]])
print("Matriz original:")
print(matriz)
print("Forma:", matriz.shape)
# Transposición
transpuesta = matriz.T
print("\nTranspuesta:")
print(transpuesta)
print("Forma:", transpuesta.shape)
# Transposición con transpose()
transpuesta2 = np.transpose(matriz)
print("\nTranspuesta con transpose():")
print(transpuesta2)
Arrays 3D y transposición
arr_3d = np.arange(24).reshape(2, 3, 4)
print("Array 3D (2, 3, 4):")
print(arr_3d)
# Transposición cambiando el orden de ejes
trans_3d = np.transpose(arr_3d, (1, 0, 2))
print("\nTransposición (1, 0, 2):")
print(trans_3d)
print("Nueva forma:", trans_3d.shape)
Aplanamiento de arrays
arr_3d = np.arange(24).reshape(2, 3, 4)
# Aplanar completamente
aplanado = arr_3d.flatten()
print("Array aplanado:", aplanado)
# Aplanar en orden C (por filas)
ravel_c = arr_3d.ravel()
print("Ravel (orden C):", ravel_c)
# Aplanar en orden F (por columnas)
ravel_f = np.ravel(arr_3d, order='F')
print("Ravel (orden F):", ravel_f)
Indexación Avanzada
Técnicas sofisticadas para acceder a elementos:
Indexación con arrays de índices
arr = np.array([10, 20, 30, 40, 50])
indices = np.array([0, 2, 4])
print("Array:", arr)
print("Índices:", indices)
print("Elementos seleccionados:", arr[indices])
Indexación en múltiples dimensiones
matriz = np.arange(12).reshape(3, 4)
print("Matriz:")
print(matriz)
# Seleccionar elementos específicos
filas = np.array([0, 1, 2])
columnas = np.array([1, 2, 3])
print("Elementos en posiciones (0,1), (1,2), (2,3):")
print(matriz[filas, columnas])
Indexación Booleana
Usar condiciones para seleccionar elementos:
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print("Array:", arr)
# Crear máscara booleana
mascara = arr > 5
print("Máscara (elementos > 5):", mascara)
print("Elementos seleccionados:", arr[mascara])
# Operaciones con máscaras
pares = arr[arr % 2 == 0]
print("Números pares:", pares)
multiplos_3 = arr[(arr % 3 == 0) & (arr > 3)]
print("Múltiplos de 3 mayores que 3:", multiplos_3)
Modificación condicional
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print("Array original:", arr)
# Modificar elementos que cumplen condición
arr[arr < 5] = 0
print("Después de poner 0 a elementos < 5:", arr)
# Usar np.where para asignaciones condicionales
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
resultado = np.where(arr % 2 == 0, arr * 2, arr / 2)
print("Pares * 2, impares / 2:", resultado)
Broadcasting
Operaciones entre arrays de diferentes formas:
# Broadcasting básico
a = np.array([1, 2, 3])
b = np.array([[10], [20], [30]])
print("Array A (1D):", a)
print("Array B (2D):")
print(b)
# Broadcasting automático
resultado = a + b
print("\nResultado del broadcasting:")
print(resultado)
Reglas de Broadcasting
# Ejemplo 1: Escalares
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("Array:")
print(arr)
escalar = 10
resultado = arr + escalar
print(f"\nArray + {escalar}:")
print(resultado)
# Ejemplo 2: Arrays con dimensiones compatibles
a = np.array([[1, 2, 3]])
b = np.array([[10], [20]])
print("\nArray A (1, 3):")
print(a)
print("Array B (2, 1):")
print(b)
resultado = a + b
print("\nBroadcasting A + B:")
print(resultado)
Casos donde el broadcasting falla
# Broadcasting incompatible
a = np.array([1, 2, 3]) # (3,)
b = np.array([10, 20]) # (2,)
try:
resultado = a + b
except ValueError as e:
print("Error de broadcasting:", e)
# Solución: reshape
b_reshaped = b.reshape(2, 1) # (2, 1)
print("Array B reshaped:", b_reshaped.shape)
Copia vs Vista
Entender la diferencia entre copias y vistas de arrays:
arr_original = np.array([1, 2, 3, 4, 5])
print("Array original:", arr_original)
# Vista (no copia los datos)
vista = arr_original[1:4]
print("Vista:", vista)
# Modificar la vista afecta al original
vista[0] = 99
print("Después de modificar vista:")
print("Vista:", vista)
print("Original:", arr_original)
Creación explícita de copias
arr_original = np.array([1, 2, 3, 4, 5])
# Copia completa
copia = arr_original.copy()
print("Copia:", copia)
# Modificar copia no afecta original
copia[0] = 999
print("Después de modificar copia:")
print("Copia:", copia)
print("Original:", copia)
Métodos que devuelven vistas vs copias
arr = np.arange(12).reshape(3, 4)
print("Array original:")
print(arr)
# Operaciones que devuelven vistas
vista1 = arr[1:3, :] # Vista
vista2 = arr[:, 1:3] # Vista
vista3 = arr.T # Vista
vista4 = arr.ravel() # Vista (por defecto)
# Operaciones que devuelven copias
copia1 = arr.copy() # Copia
copia2 = arr.flatten() # Copia
copia3 = arr.reshape(6, 2) # Vista (normalmente)
print("Vista reshape:", copia3.base is arr) # True si es vista
Proyecto Práctico - Procesamiento de Imágenes
Vamos a crear un programa simple de procesamiento de imágenes usando arrays de NumPy:
import numpy as np
def crear_imagen_sintetica(ancho, alto):
"""Crea una imagen sintética con patrones"""
# Crear coordenadas
x = np.linspace(0, 10, ancho)
y = np.linspace(0, 10, alto)
X, Y = np.meshgrid(x, y)
# Crear patrones
patron1 = np.sin(X) * np.cos(Y)
patron2 = np.exp(-(X-5)**2 - (Y-5)**2)
# Combinar patrones
imagen = (patron1 + patron2) * 127 + 128
return np.clip(imagen, 0, 255).astype(np.uint8)
def aplicar_filtro(imagen, filtro):
"""Aplica un filtro simple a la imagen"""
if filtro == 'blur':
# Filtro de desenfoque simple
kernel = np.ones((3, 3)) / 9
elif filtro == 'sharpen':
# Filtro de nitidez
kernel = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
elif filtro == 'edge':
# Detector de bordes
kernel = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]])
else:
return imagen
# Aplicar convolución simple
resultado = np.zeros_like(imagen, dtype=np.float32)
for i in range(1, imagen.shape[0] - 1):
for j in range(1, imagen.shape[1] - 1):
region = imagen[i-1:i+2, j-1:j+2]
resultado[i, j] = np.sum(region * kernel)
return np.clip(resultado, 0, 255).astype(np.uint8)
def analizar_imagen(imagen):
"""Analiza estadísticas de la imagen"""
print("=== ANÁLISIS DE IMAGEN ===")
print(f"Dimensiones: {imagen.shape}")
print(f"Tipo de datos: {imagen.dtype}")
print(f"Rango de valores: {imagen.min()} - {imagen.max()}")
print(f"Valor promedio: {imagen.mean():.2f}")
print(f"Desviación estándar: {imagen.std():.2f}")
# Histograma simple
hist, bins = np.histogram(imagen.flatten(), bins=10, range=(0, 255))
print("\nHistograma (10 bins):")
for i, (count, bin_edge) in enumerate(zip(hist, bins[:-1])):
print(".1f")
def main():
print("=== PROCESAMIENTO DE IMÁGENES CON NUMPY ===\n")
# Crear imagen sintética
imagen = crear_imagen_sintetica(20, 20)
print("Imagen original creada")
analizar_imagen(imagen)
# Aplicar filtros
filtros = ['blur', 'sharpen', 'edge']
for filtro in filtros:
imagen_filtrada = aplicar_filtro(imagen, filtro)
print(f"\n--- Filtro: {filtro.upper()} ---")
analizar_imagen(imagen_filtrada)
# Operaciones de manipulación
print(f"\nManipulaciones para {filtro}:")
print(f" Invertida: rango {255 - imagen_filtrada.max()} - {255 - imagen_filtrada.min()}")
print(f" Brillo +50: rango {np.clip(imagen_filtrada + 50, 0, 255).min()} - {np.clip(imagen_filtrada + 50, 0, 255).max()}")
print(f" Contraste: std {np.clip(imagen_filtrada * 1.5, 0, 255).std():.2f}")
if __name__ == "__main__":
main()
Próximos Pasos
¡Excelente progreso! Has dominado las operaciones avanzadas con arrays. Ahora puedes:
- Funciones Matemáticas y Estadísticas: Operaciones numéricas avanzadas
- NumPy para Ciencia de Datos: Integración con análisis de datos
- Optimización de Rendimiento: Técnicas para código más eficiente
- Integración con otras bibliotecas: Pandas, Matplotlib, SciPy
Tutoriales Recomendados
Conclusión
Las operaciones avanzadas con arrays son fundamentales para el procesamiento eficiente de datos en NumPy. Has aprendido a:
- Cambiar la forma de los arrays con reshape
- Combinar arrays mediante concatenación y apilamiento
- Dividir arrays en partes manejables
- Usar indexación sofisticada para acceder a datos específicos
- Aplicar broadcasting para operaciones entre arrays de diferentes formas
- Entender la diferencia entre copias y vistas
Practica estos conceptos aplicándolos a tus propios proyectos y verás cómo mejoran significativamente el rendimiento y la expresividad de tu código.
¡Sigue adelante en tu camino hacia el dominio de NumPy!
¡Continúa explorando las poderosas capacidades de NumPy!
💡 Tip Importante
📝 Mejores Prácticas para Operaciones con Arrays
- Usa broadcasting cuando sea posible para evitar bucles explícitos
- Entiende la diferencia entre vistas y copias para optimizar memoria
- Utiliza indexación booleana para selecciones condicionales eficientes
- Aprovecha reshape para cambiar dimensiones sin copiar datos
- Documenta operaciones complejas con comentarios claros
📚 Recursos Recomendados:
¡Tu dominio de NumPy continúa creciendo. Sigue practicando y experimentando!
No hay comentarios aún
Sé el primero en comentar este tutorial.