NumPy (Numerical Python) es la biblioteca fundamental para la computación científica en Python. Desarrollada originalmente por Travis Oliphant en 2005, se ha convertido en la columna vertebral de prácticamente todo el ecosistema moderno de ciencia de datos de Python.

Si trabajas con análisis de datos, machine learning, ingeniería o cualquier área que requiera cálculos numéricos eficientes, NumPy es indispensable. En esta guía completa, dominarás desde los conceptos básicos hasta técnicas avanzadas de manipulación de arrays.

📦 ¿Qué es NumPy y Por Qué Usarlo?

NumPy es una biblioteca que proporciona soporte para arrays multidimensionales de alto rendimiento, junto con funciones matemáticas de alto nivel para trabajar con esos arrays. La principal ventaja de NumPy sobre las listas tradicionales de Python es la velocidad: las operaciones con arrays NumPy son decenas a cientos de veces más rápidas que las equivalentes con listas nativas.

Esta eficiencia ocurre porque NumPy utiliza memoria continua e implementa operaciones vectorizadas que aprovechan la estructura de datos para realizar cálculos en paralelo. Además, NumPy es la base de otras bibliotecas esenciales como Pandas, Matplotlib, Scikit-learn y TensorFlow.

🚀 Instalación y Configuración

Instalación vía pip

# Instalación estándar
pip install numpy

# Verificar versión
import numpy as np
print(np.__version__)

Instalación vía conda

# Para usuarios de Anaconda/Miniconda
conda install numpy

🔧 Arrays NumPy: El Corazón de la Biblioteca

Creando Arrays Básicos

import numpy as np

# Array a partir de una lista
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)

# Array de ceros
zeros = np.zeros(5)
print(zeros)

# Array de unos
unos = np.ones((3, 3))  # matriz 3x3
print(unos)

# Array con valores aleatorios
aleatorios = np.random.rand(5)
print(aleatorios)

# Array con secuencia
secuencia = np.arange(0, 10, 2)  # de 0 a 10, paso 2
print(secuencia)

# Array con espacio lineal
espaciado = np.linspace(0, 1, 5)  # 5 valores entre 0 y 1
print(espaciado)

Propiedades de los Arrays

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

print(f"Dimensiones: {arr.ndim}")       # 2
print(f"Forma: {arr.shape}")             # (2, 3)
print(f"Total elementos: {arr.size}")     # 6
print(f"Tipo de datos: {arr.dtype}")     # int64
print(f"Memoria (bytes): {arr.itemsize * arr.size}")

📊 Operaciones Matemáticas con NumPy

Operaciones Aritméticas

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

# Operaciones básicas - aplicadas elemento a elemento
print(arr + 10)    # [11, 12, 13, 14, 15]
print(arr - 5)     # [-4, -3, -2, -1, 0]
print(arr * 2)     # [2, 4, 6, 8, 10]
print(arr / 2)     # [0.5, 1.0, 1.5, 2.0, 2.5]
print(arr ** 2)    # [1, 4, 9, 16, 25]

# Operaciones entre arrays
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(a + b)       # [5, 7, 9]
print(a * b)       # [4, 10, 18]

Funciones Matemáticas

import numpy as np

arr = np.array([0, np.pi/2, np.pi, 3*np.pi/2])

# Funciones trigonométricas
print(np.sin(arr))   # [0. 1. 0. -1.]
print(np.cos(arr))   # [1. 0. -1. 0.]
print(np.tan(arr))   # [0. inf 0. inf]

# Otras funciones importantes
arr2 = np.array([1, 4, 9, 16])
print(np.sqrt(arr2))   # [1. 2. 3. 4.]
print(np.log(arr2))    # [0. 1. 2. 2.77...]
print(np.abs([-1, -2, 3]))  # [1, 2, 3]

# Estadísticas
notas = np.array([7.5, 8.0, 6.5, 9.0, 7.0])
print(f"Media: {np.mean(notas):.2f}")      # 7.6
print(f"Mediana: {np.median(notas):.2f}")  # 7.5
print(f"Desviación estándar: {np.std(notas):.2f}")  # 0.98
print(f"Varianza: {np.var(notas):.2f}")   # 0.96
print(f"Máximo: {np.max(notas)}")         # 9.0
print(f"Mínimo: {np.min(notas)}")          # 6.5

🎯 Indexación y Slicing

Acceso a Elementos

import numpy as np

arr = np.array([[1, 2, 3, 4],
                [5, 6, 7, 8],
                [9, 10, 11, 12]])

# Acceso por índice
print(arr[0, 0])      # 1
print(arr[1, 2])      # 7
print(arr[-1, -1])    # 12

# Indexación negativa
print(arr[-1])        # [9, 10, 11, 12]
print(arr[-1, -2])   # 11

Slicing (Rebanado)

import numpy as np

arr = np.arange(1, 21).reshape(4, 5)

# Filas y columnas específicas
print(arr[0, :])      # primera fila
print(arr[:, 2])      # tercera columna

# Intervalos
print(arr[1:3, 1:4])  # filas 1-2, columnas 1-3

# Con paso
print(arr[::2])       # todas las filas, paso 2
print(arr[::2, ::2])  # filas y columnas con paso 2

# Máscara booleana
arr2 = np.array([10, 20, 30, 40, 50])
mascara = arr2 > 25
print(arr2[mascara])  # [30, 40, 50]

# Indexación fancy
indices = [0, 2, 4]
print(arr2[indices])  # [10, 30, 50]

🔄 Manipulación de Arrays

reshape() y flatten()

import numpy as np

arr = np.arange(1, 13)
print(arr.shape)  # (12,)

# Redimensionar a matriz 3x4
matriz = arr.reshape(3, 4)
print(matriz)
# [[1  2  3  4]
#  [5  6  7  8]
#  [9 10 11 12]]

# flatten() - aplanar a 1D
print(matriz.flatten())

# ravel() - similar a flatten pero devuelve vista
print(matriz.ravel())

transpose() y swapaxes()

import numpy as np

matriz = np.array([[1, 2, 3],
                   [4, 5, 6]])

print(matriz.T)
# [[1 4]
#  [2 5]
#  [3 6]]

# swapaxes() - intercambiar ejes
arr3d = np.arange(1, 13).reshape(2, 3, 2)
print(arr3d.swapaxes(1, 2).shape)  # (2, 2, 3)

Concatenación y Split

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# Concatenar
print(np.concatenate([a, b]))        # [1 2 3 4 5 6]
print(np.hstack([a, b]))             # horizontal stack
print(np.vstack([a, b]))             # vertical stack

# Split
arr = np.array([1, 2, 3, 4, 5, 6])
print(np.split(arr, 3))  # [array([1,2]), array([3,4]), array([5,6])]

💡 Broadcasting

El broadcasting es una técnica poderosa de NumPy que permite realizar operaciones entre arrays de diferentes formas. NumPy automáticamente expande el array más pequeño para compatibilidad.

import numpy as np

# Ejemplo 1: arrays de diferentes formas
a = np.array([[1, 2, 3],
              [4, 5, 6]])
b = np.array([10, 20, 30])

# b es "broadcast" para tener la misma forma que a
print(a + b)
# [[11 22 33]
#  [14 25 36]]

# Ejemplo 2: escalar con array
arr = np.array([1, 2, 3, 4, 5])
print(arr * 10)  # [10 20 30 40 50]

# Ejemplo 3: matriz 2D con vector
matriz = np.ones((3, 3))
vector = np.array([1, 2, 3])
print(matriz + vector)
# [[2 3 4]
#  [2 3 4]
#  [2 3 4]]

🧮 Álgebra Lineal con NumPy

import numpy as np

# Producto punto
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b))      # 32

# Multiplicación de matrices
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.matmul(A, B))
# [[19 22]
#  [43 50]]

# Determinante
print(np.linalg.det(A))  # -2.0

# Inversa
print(np.linalg.inv(A))
# [[-2.   1. ]
#  [ 1.5 -0.5]]

# Autovalores
w, v = np.linalg.eig(A)
print(f"Autovalores: {w}")
print(f"Autovectores: {v}")

🎓 Proyecto Práctico: Análisis de Ventas

import numpy as np

# Simular datos de ventas de una tienda
np.random.seed(42)

dias = 30
productos = ["Electrónicos", "Ropa", "Alimentos", "Hogar"]

# Generar ventas aleatorias para cada producto
ventas = np.random.randint(1000, 5000, size=(dias, len(productos)))
ventas = ventas.astype(float)

# Agregar variación de precio por producto
precios = np.array([500.00, 80.00, 25.00, 150.00])

# Calcular receita diária
receita_diaria = ventas * precios

print("=" * 50)
print("📊 INFORME DE VENTAS - TIENDA MIX")
print("=" * 50)

# Receita total por producto
receita_por_producto = receita_diaria.sum(axis=0)
print("\n💰 Receita por Producto:")
for i, prod in enumerate(productos):
    print(f"  {prod}: $ {receita_por_producto[i]:,.2f}")

# Producto más vendido
producto_mas_vendido = productos[np.argmax(receita_por_producto)]
print(f"\n🏆 Producto más vendido: {producto_mas_vendido}")

# Media diária de ventas
ventas_media_dia = ventas.mean(axis=1)
print(f"\n📈 Media de ventas por día: {ventas_media_dia.mean():,.0f}")

# Día con maior receita
dia_maior_receita = np.argmax(receita_diaria.sum(axis=1)) + 1
receita_dia_max = receita_diaria.sum(axis=1).max()
print(f"\n📅 Melhor día: Día {dia_maior_receita} ($ {receita_dia_max:,.2f})")

# Desviación estándar de ventas (volatilidad)
volatilidad = ventas.std(axis=0)
print(f"\n📉 Volatilidad por producto:")
for i, prod in enumerate(productos):
    print(f"  {prod}: {volatilidad[i]:,.0f}")

🔗 Integración con Otras Bibliotecas

NumPy es la base del ecosistema de ciencia de datos de Python. Mira cómo se integra con las principales bibliotecas:

NumPy + Pandas

import numpy as np
import pandas as pd

# Crear DataFrame a partir de arrays NumPy
datos = np.random.randn(100, 3)
df = pd.DataFrame(datos, columns=['A', 'B', 'C'])
print(df.describe())

NumPy + Matplotlib

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('Seno con NumPy')
plt.show()

NumPy + SciPy

from scipy import stats
import numpy as np

datos = np.array([23, 25, 28, 30, 32, 35, 38, 40])
media = np.mean(datos)
desvio = np.std(datos)
resultado = stats.normaltest(datos)
print(f"Media: {media:.2f}")
print(f"Test normalidad: {resultado}")

⚡ Consejos de Rendimiento

  • ✅ Usa arrays NumPy en lugar de listas Python para operaciones numéricas
  • ✅ Prefiere operaciones vectorizadas en lugar de bucles for
  • ✅ Utiliza np.where() para condicionales en arrays
  • ✅ Usa @njit (Numba) para funciones que no pueden ser vectorizadas
  • ✅ Evita usar append() en bucles - preasigna la memoria
  • ✅ Usa view en lugar de copy cuando sea posible
  • ✅ Configura el dtype correcto para ahorrar memoria
  • ❌ Evita convertir arrays NumPy a listas Python

🚀 Próximos Pasos

Ahora que dominas NumPy, el siguiente paso es explorar bibliotecas que lo utilizan como base:

¡NumPy es solo el comienzo de tu viaje en ciencia de datos con Python! Sigue practicando y explorando las infinitas posibilidades que esta poderosa biblioteca ofrece.