NumPy 2.4 para Ciencia de Datos en Python: Arrays, Vectorización y Broadcasting (2026)

Guía completa de NumPy 2.4 para ciencia de datos en Python. Arrays, vectorización, broadcasting y las novedades de enero 2026 con ejemplos prácticos listos para usar.

NumPy 2.4 en Python: Arrays y Vectorización

NumPy es, sin exagerar, la piedra angular del ecosistema de ciencia de datos en Python. Sin él no existirían Pandas, Scikit-learn ni TensorFlow tal como los conocemos hoy. La versión 2.4, lanzada en diciembre de 2025, trae mejoras de rendimiento notables: operaciones con escalares hasta 6 veces más rápidas y una gestión de cadenas de texto completamente renovada. En esta guía aprenderás a crear y manipular arrays, eliminar bucles lentos con vectorización, y aplicar broadcasting para operaciones entre arrays de distintas formas.

Honestamente, si llevas tiempo con Python y todavía no dominas NumPy a fondo, este es el mejor momento para hacerlo.

¿Por Qué NumPy es Indispensable para la Ciencia de Datos?

Python por defecto es lento para operaciones numéricas masivas. Un bucle que suma 10 millones de números puede tardar varios segundos. NumPy resuelve esto almacenando datos en bloques de memoria contiguos con un único tipo de dato y delegando la ejecución al código C compilado. El resultado son operaciones 10 a 100 veces más rápidas que los equivalentes en Python puro.

Además, NumPy establece el protocolo de array estándar que utilizan el resto de librerías del ecosistema. Cuando entrenas un modelo con Scikit-learn o creas un DataFrame en Pandas, ambas convierten internamente los datos a arrays NumPy para realizar las operaciones matemáticas. Es, en definitiva, el lenguaje común que hablan todas las herramientas de datos en Python.

Instalación y Configuración de NumPy 2.4

NumPy 2.4.1 es la versión estable actual (enero 2026). Instala o actualiza con pip o Conda — es rápido y sin complicaciones:

pip install "numpy>=2.4"
# O en un entorno Conda:
conda install numpy

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

NumPy 2.x requiere Python 3.10 o superior. Si usas Python 3.8 o 3.9, actualiza el intérprete antes de instalar NumPy 2.4 para evitar errores de compatibilidad. (Sí, sé que actualizar el intérprete da pereza, pero vale la pena.)

Arrays de NumPy: La Estructura Fundamental

El objeto central de NumPy es el ndarray (n-dimensional array). A diferencia de las listas de Python, todos los elementos de un ndarray comparten el mismo tipo de dato (dtype), lo que permite operaciones vectorizadas a velocidad de C. Es una diferencia pequeña en el papel, pero enorme en la práctica.

Crear Arrays desde Cero

import numpy as np

# Desde una lista Python
arr1d = np.array([1, 2, 3, 4, 5])
arr2d = np.array([[1, 2, 3], [4, 5, 6]])

# Arrays especiales
zeros = np.zeros((3, 4))               # Matriz 3x4 de ceros
ones  = np.ones((2, 3), dtype=float)   # Matriz 2x3 de unos
iden  = np.eye(4)                      # Matriz identidad 4x4

# Rangos y secuencias
rango     = np.arange(0, 10, 2)        # [0, 2, 4, 6, 8]
espaciado = np.linspace(0, 1, 5)       # [0.0, 0.25, 0.5, 0.75, 1.0]

# Arrays aleatorios con semilla para reproducibilidad
rng      = np.random.default_rng(42)
aleatorio = rng.standard_normal((3, 3))

Atributos Esenciales del ndarray

arr = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])

print(arr.shape)   # (2, 3) — 2 filas, 3 columnas
print(arr.ndim)    # 2     — número de dimensiones
print(arr.dtype)   # float64 — tipo de dato
print(arr.size)    # 6     — total de elementos
print(arr.nbytes)  # 48    — bytes en memoria (6 x 8 bytes)

El dtype determina el uso de memoria y la precisión numérica. Para ciencia de datos, float64 es el estándar, pero usar float32 puede reducir la memoria a la mitad sin pérdida significativa en muchos modelos de machine learning. Merece la pena tenerlo en cuenta cuando trabajas con datasets grandes.

Indexado y Slicing de Arrays

NumPy ofrece un sistema de indexado más potente que las listas de Python, con soporte para indexado multidimensional, booleano y con arrays de índices. Una vez que le coges el truco, no querrás volver a las listas.

Indexado Básico y Slicing

arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])

# Elemento específico [fila, columna]
print(arr[1, 2])       # 60

# Fila completa
print(arr[0])          # [10, 20, 30]

# Columna completa
print(arr[:, 1])       # [20, 50, 80]

# Submatriz: filas 0-1, columnas 1-2
print(arr[0:2, 1:3])   # [[20, 30], [50, 60]]

# Reshape: mismos datos, nueva forma
plano = arr.reshape(1, 9)   # [[10, 20, 30, 40, 50, 60, 70, 80, 90]]

Indexado Booleano

El indexado booleano es una herramienta esencial para filtrar y transformar datos. Es de esas cosas que parecen magia la primera vez que las ves:

datos = np.array([15, 32, 7, 48, 21, 9, 55, 3])

# Filtrar valores mayores a 20
grandes = datos[datos > 20]
print(grandes)  # [32, 48, 21, 55]

# Reemplazar valores negativos con 0 (útil en preprocesamiento)
arr = np.array([-3.0, 1.5, -0.8, 4.2, -1.1])
arr[arr < 0] = 0.0
print(arr)  # [0.  1.5 0.  4.2 0. ]

Vectorización: Elimina los Bucles Python

La vectorización es el principio más importante para escribir código NumPy eficiente. Consiste en reemplazar bucles Python explícitos con operaciones que actúan sobre el array completo a la vez, delegando la ejecución a rutinas C compiladas con instrucciones SIMD. Dicho de otro modo: menos código Python, más velocidad real.

Por Qué los Bucles Son Lentos

Python interpreta cada instrucción individualmente. En un bucle de un millón de iteraciones, el intérprete realiza un millón de llamadas de función. NumPy, en cambio, ejecuta la misma operación en una sola llamada a código C altamente optimizado.

import numpy as np
import time

datos = np.random.randn(1_000_000)

# Versión lenta con bucle Python
inicio = time.perf_counter()
resultado = np.empty(len(datos))
for i in range(len(datos)):
    resultado[i] = datos[i] ** 2 if datos[i] > 0 else datos[i] ** 3
tiempo_bucle = time.perf_counter() - inicio

# Versión vectorizada con np.where
inicio = time.perf_counter()
resultado_v = np.where(datos > 0, datos ** 2, datos ** 3)
tiempo_vec = time.perf_counter() - inicio

print(f"Bucle Python:   {tiempo_bucle:.3f}s")
print(f"Vectorizado:    {tiempo_vec:.4f}s")
print(f"Aceleracion:    {tiempo_bucle / tiempo_vec:.0f}x mas rapido")
# Aceleracion tipica: 50-100x

Las cifras son consistentes: entre 50x y 100x de aceleración. No es un caso especialmente favorable — es lo habitual.

Funciones Universales (ufuncs)

NumPy proporciona decenas de funciones universales que operan elemento a elemento sobre arrays sin necesidad de bucles:

arr = np.array([1.0, 4.0, 9.0, 16.0, 25.0])

# Operaciones matematicas elementales
print(np.sqrt(arr))           # [1. 2. 3. 4. 5.]
print(np.log(arr))            # logaritmo natural
print(np.exp(np.array([1, 2, 3])))  # [e, e^2, e^3]

# Seleccion condicional vectorizada
x = np.array([3.7, -1.2, 0.5, -4.8, 2.1])
print(np.clip(x, 0.0, 3.0))  # [3.  0.  0.5 0.  2.1]
print(np.sign(x))             # [ 1. -1.  1. -1.  1.]

# np.where con condicion mas compleja
etiquetas = np.where(x > 0, "positivo", "negativo")
print(etiquetas)

Broadcasting: Operaciones con Arrays de Diferente Forma

El broadcasting es el mecanismo que permite a NumPy realizar operaciones entre arrays de formas distintas sin copiar datos. El array más pequeño se "expande" virtualmente para que sea compatible con el más grande. Al principio resulta un poco confuso, pero una vez que lo interioriza se convierte en una herramienta de uso diario.

Las 3 Reglas del Broadcasting

NumPy aplica estas reglas al comparar dimensiones de derecha a izquierda:

  1. Si los arrays tienen distinto número de dimensiones, el de menor rango se rellena con 1s a la izquierda: (4,) se convierte en (1, 4).
  2. Una dimensión de tamaño 1 se "estira" para coincidir con la dimensión correspondiente del otro array.
  3. Si en alguna dimensión los tamaños no coinciden y ninguno es 1, se lanza un ValueError.
import numpy as np

# Broadcasting escalar: (4,) + escalar = (4,)
arr = np.array([1, 2, 3, 4])
print(arr + 10)   # [11 12 13 14]

# Broadcasting 1D sobre 2D
# matriz (3,3) + vector (3,) -> vector tratado como (1,3) -> (3,3)
matriz = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
vector = np.array([10, 20, 30])
print(matriz + vector)
# [[11 22 33]
#  [14 25 36]
#  [17 28 39]]

# Broadcasting columna: np.newaxis crea una dimension de tamanio 1
correcciones = np.array([1.0, -0.5, 2.0])   # forma (3,)
col = correcciones[:, np.newaxis]             # forma (3, 1)
print(matriz + col)
# [[ 2.  3.  4. ]
#  [ 3.5 4.5 5.5]
#  [ 9. 10. 11. ]]

Caso Práctico: Normalización Z-Score

Normalizar datasets es un paso clave antes de entrenar modelos de ML. El broadcasting hace que este preprocesamiento sea elegante y eficiente — y sin una sola línea de bucle:

rng = np.random.default_rng(42)
X = rng.standard_normal((1000, 5))   # 1000 muestras, 5 caracteristicas

# Media y desviacion estandar por columna, shape (5,)
media = X.mean(axis=0)
std   = X.std(axis=0)

# Broadcasting: (5,) se expande a (1000, 5) automaticamente
X_normalizado = (X - media) / std

print("Media post-norm:", X_normalizado.mean(axis=0).round(8))
# Valores muy proximos a cero
print("Std  post-norm:", X_normalizado.std(axis=0).round(6))
# Valores muy proximos a uno

Caso Práctico: Matriz de Distancias Euclidianas

Con broadcasting se puede calcular todas las distancias entre N puntos en una sola expresión, sin ningún bucle:

puntos = np.array([[0, 0], [3, 4], [1, 1], [6, 8]])  # 4 puntos 2D

# puntos[:, np.newaxis] -> (4, 1, 2)
# puntos[np.newaxis, :] -> (1, 4, 2)
# diferencia resultante -> (4, 4, 2)
diferencias = puntos[:, np.newaxis] - puntos[np.newaxis, :]
distancias  = np.sqrt((diferencias ** 2).sum(axis=2))

print(distancias.round(2))
# [[ 0.    5.    1.41 10.  ]
#  [ 5.    0.    3.61  5.  ]
#  [ 1.41  3.61  0.    8.6 ]
#  [10.    5.    8.6   0.  ]]

Funciones Estadísticas y de Álgebra Lineal

Estadísticas Descriptivas

datos = np.array([[4, 7, 2, 8], [1, 9, 3, 6], [5, 2, 8, 4]])

print(datos.mean())               # media de todos los elementos
print(datos.mean(axis=0))         # media por columna, shape (4,)
print(datos.mean(axis=1))         # media por fila, shape (3,)
print(datos.std(ddof=1))          # desviacion estandar muestral
print(np.percentile(datos, [25, 50, 75]))  # cuartiles
print(np.median(datos))           # mediana
print(np.corrcoef(datos.T))       # matriz de correlacion entre columnas

Álgebra Lineal con numpy.linalg

A = np.array([[2.0, 1.0], [5.0, 3.0]])
b = np.array([4.0, 7.0])

# Multiplicacion de matrices (operador @)
C = A @ A.T

# Resolver sistema Ax = b
x = np.linalg.solve(A, b)
print("Solucion:", x)

# Descomposicion en valores propios
valores, vectores = np.linalg.eig(A)

# Descomposicion SVD (usada en PCA y recomendadores)
U, s, Vt = np.linalg.svd(A)
print("Valores singulares:", s)

NumPy en el Ecosistema de Ciencia de Datos

NumPy no trabaja en aislamiento: es la infraestructura sobre la que se construyen todas las librerías de datos. Cuando realizas ingeniería de características con Pandas y Scikit-learn, ambas librerías utilizan arrays NumPy internamente para todas sus operaciones numéricas.

import numpy as np
import pandas as pd

# Crear DataFrame desde array NumPy (sin copiar datos)
arr = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
df  = pd.DataFrame(arr, columns=["A", "B"])

# De Pandas a NumPy
arr_desde_df = df.to_numpy()
print(type(arr_desde_df))   # 

# Las funciones NumPy operan directamente sobre columnas Pandas
df["distancia"] = np.sqrt(df["A"] ** 2 + df["B"] ** 2)

Cuando construyes pipelines de machine learning con Scikit-learn, todos los estimadores reciben y devuelven arrays NumPy. Dominar NumPy te permite inspeccionar coeficientes, manipular predicciones y depurar modelos con precisión quirúrgica.

from sklearn.preprocessing import StandardScaler
import numpy as np

X = np.random.randn(100, 4)

# Scikit-learn trabaja internamente con NumPy
scaler = StandardScaler()
X_esc  = scaler.fit_transform(X)

print(type(X_esc))       # numpy.ndarray
print(scaler.mean_)      # numpy.ndarray con medias por columna
print(scaler.scale_)     # numpy.ndarray con desviaciones tipicas

Novedades de NumPy 2.4 para 2026

NumPy 2.4.1, lanzado el 10 de enero de 2026, incorpora mejoras que impactan directamente en flujos de trabajo de ciencia de datos. Algunas son notables:

  • Escalares 6x más rápidos: las ufuncs de entrada única sobre escalares son aproximadamente 6 veces más rápidas que en versiones anteriores, lo que beneficia cálculos iterativos ligeros.
  • StringDType con hash: np.unique usa ahora un algoritmo basado en hash para deduplicación de strings, obteniendo una mejora de ~15x sobre el método anterior (498s vs 33s para mil millones de strings).
  • Python sin GIL (free-threaded): soporte estable de la ABI para Python 3.13t, permitiendo paralelización real con múltiples hilos sin el Global Interpreter Lock.
  • Nuevo casting same_value: mayor control sobre conversiones de tipo para evitar pérdidas silenciosas de precisión numérica.
  • Anotaciones de forma mejoradas: los type-checkers pueden inferir el número de dimensiones del array de retorno en operaciones comunes.
# StringDType para arrays de texto de longitud variable
import numpy as np

texto = np.array(
    ["pandas", "numpy", "scikit-learn", "numpy", "pandas"],
    dtype=np.dtypes.StringDType()
)
unicos = np.unique(texto)   # ~15x mas rapido en arrays grandes
print(unicos)  # ['numpy' 'pandas' 'scikit-learn']

# Verificar soporte free-threaded
import sys
print("Free-threaded:" , "free-threaded" in sys.version.lower())

Errores Comunes y Cómo Evitarlos

Error de Broadcasting por Formas Incompatibles

# Esto falla:
# a = np.array([[1,2,3],[4,5,6]])  # (2, 3)
# b = np.array([1, 2])             # (2,)
# a + b  ->  ValueError: could not broadcast (2,3) with (2,)

# Solucion: convertir b a vector columna
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([1, 2])
print(a + b[:, np.newaxis])   # b -> (2,1), se expande a (2,3)
# [[2 3 4]
#  [6 7 8]]

Vistas vs Copias

Este es uno de esos errores que cuesta caro la primera vez que te lo encuentras en producción:

arr = np.arange(12).reshape(3, 4)

# Las vistas NO copian datos — modificar la vista modifica el original
vista = arr[1:, :]
vista[:] = 0              # arr tambien cambia

# Para una copia independiente:
copia = arr[1:, :].copy()
copia[:] = 99             # arr NO se modifica

Preguntas Frecuentes

¿Cuál es la diferencia entre una lista de Python y un array de NumPy?

Las listas de Python pueden contener cualquier tipo de objeto y son dinámicas, pero lentas para operaciones numéricas. Los arrays NumPy almacenan elementos del mismo tipo en memoria contigua, lo que permite operaciones vectorizadas a velocidad de C, siendo 10 a 100 veces más rápidos en cálculos matemáticos masivos.

¿Qué es el broadcasting en NumPy y cuándo debo usarlo?

Broadcasting es el mecanismo que permite operar arrays de formas distintas sin copiar datos. Úsalo siempre que necesites aplicar una operación a lo largo de un eje: normalizar columnas de una matriz, sumar un vector a cada fila, o calcular distancias entre múltiples puntos simultáneamente.

¿Vale la pena actualizar a NumPy 2.4 desde NumPy 1.x?

Sí, pero con precaución. NumPy 2.x introduce cambios que rompen la compatibilidad con la ABI de NumPy 1.x, por lo que algunos paquetes compilados pueden necesitar actualización. Las mejoras de rendimiento (hasta 15x en ciertas operaciones) y el nuevo StringDType justifican la migración para proyectos nuevos. Para proyectos existentes, revisa primero la guía oficial de migración.

¿Cuándo debería usar Polars o Dask en lugar de NumPy?

NumPy es ideal para arrays numéricos homogéneos que caben en RAM. Si trabajas con datos tabulares de tipos mixtos, usa Pandas o Polars. Si los datos no caben en la memoria de una sola máquina, considera Dask (que usa NumPy internamente) o cuDF para GPU.

¿Cómo sé si mi código NumPy está aprovechando la vectorización al máximo?

Usa %timeit en Jupyter para comparar versiones con bucles versus vectorizadas. Si ves bucles for sobre elementos individuales del array, hay potencial de optimización. Reemplázalos con np.where(), ufuncs como np.add() o np.multiply(), e indexado booleano. Una aceleración de 50–100x es habitual.

Sobre el Autor Editorial Team

Our team of expert writers and editors.