Python para Data Science: NumPy, Pandas y Scikit-Learn en 2026

Guia practica de NumPy, Pandas y Scikit-Learn para construir pipelines de machine learning en Python. Incluye ejemplos reales de limpieza de datos, ingenieria de variables y clasificacion con GradientBoosting.

Python para Data Science: NumPy, Pandas y Scikit-Learn en 2026 — codigo y visualizaciones de machine learning

El ecosistema cientifico de Python se consolido en 2026 como la columna vertebral de los proyectos de datos en organizaciones de todos los tamanos. NumPy, Pandas y Scikit-Learn forman una triada que cubre desde la manipulacion bruta de arrays hasta el despliegue de modelos de clasificacion con validacion cruzada. Entender como estas tres bibliotecas se articulan entre si no solo acelera el desarrollo, sino que tambien es una exigencia habitual en las preguntas de entrevista en data science.

Versiones de referencia para este articulo

Los ejemplos de codigo fueron validados con Python 3.12, NumPy 2.1, Pandas 2.2 y Scikit-Learn 1.5. Si se trabaja en un entorno virtual, se recomienda fijar estas versiones en requirements.txt para garantizar reproducibilidad.

NumPy: la base numerica del stack cientifico

NumPy proporciona el tipo ndarray, un arreglo n-dimensional de tipo homogeneo almacenado en memoria contigua. Esta decision de diseno permite que las operaciones se ejecuten en codigo C compilado en lugar de en el interprete de Python, lo cual resulta en una diferencia de velocidad de uno a dos ordenes de magnitud respecto a las listas nativas.

La ventaja practica mas visible es la aritmetica vectorizada: en lugar de escribir un bucle for para multiplicar dos colecciones de numeros, NumPy aplica la operacion elemento a elemento en una sola instruccion. El siguiente ejemplo ilustra este patron con datos de ventas:

python
# numpy_basics.py
import numpy as np

# Create arrays from different sources
prices = np.array([29.99, 49.99, 19.99, 99.99, 39.99])
quantities = np.arange(1, 6)  # [1, 2, 3, 4, 5]

# Vectorized arithmetic — no loops needed
revenue = prices * quantities
print(revenue)  # [29.99, 99.98, 59.97, 399.96, 199.95]

# Statistical aggregations
print(f"Total revenue: ${revenue.sum():.2f}")   # $789.85
print(f"Mean price: ${prices.mean():.2f}")       # $47.99
print(f"Std deviation: ${prices.std():.2f}")     # $27.64

# Boolean indexing — filter without loops
premium_mask = prices > 40
premium_items = prices[premium_mask]  # [49.99, 99.99]

El boolean indexing mostrado al final del fragmento es una tecnica fundamental: genera una mascara booleana a partir de una condicion y la usa directamente como indice del array. No hay necesidad de filter() ni de comprensiones de lista; el codigo es mas legible y significativamente mas rapido con datasets de gran tamano.

Operaciones multidimensionales y broadcasting

La verdadera potencia de NumPy emerge cuando se trabaja con datos tabulares representados como matrices bidimensionales. El parametro axis controla la direccion de agregacion: axis=0 opera sobre las filas (resultado por columna) y axis=1 opera sobre las columnas (resultado por fila).

python
# numpy_reshape.py
import numpy as np

# Monthly sales data: 4 products x 3 months
sales = np.array([
    [120, 150, 130],  # Product A
    [200, 180, 220],  # Product B
    [90, 110, 95],    # Product C
    [300, 280, 310],  # Product D
])

# Column-wise mean (average per month)
monthly_avg = sales.mean(axis=0)  # [177.5, 180.0, 188.75]

# Row-wise sum (total per product)
product_totals = sales.sum(axis=1)  # [400, 600, 295, 890]

# Normalize each product relative to its own max
normalized = sales / sales.max(axis=1, keepdims=True)
# keepdims=True preserves the shape for broadcasting
print(normalized[0])  # [0.8, 1.0, 0.867] — Product A relative to its peak

# Reshape for Scikit-Learn (requires 2D input)
flat_sales = sales.flatten()  # 1D array of 12 values
reshaped = flat_sales.reshape(-1, 1)  # 12x1 column vector

El uso de keepdims=True es un detalle que marca la diferencia en entrevistas tecnicas: sin el, sales.max(axis=1) devuelve un array unidimensional de forma (4,), y la division con la matriz original falla por incompatibilidad de dimensiones. Con keepdims=True el resultado tiene forma (4, 1), lo que activa el broadcasting automatico de NumPy.

El metodo reshape(-1, 1) al final del fragmento anticipa un patron que se repetira constantemente al trabajar con Scikit-Learn: la mayoria de los estimadores esperan datos de entrada en formato bidimensional (n_samples, n_features), por lo que transformar arrays unidimensionales a columnas vectoriales es una operacion rutinaria.

Pandas: limpieza y transformacion de datos reales

Pandas introduce el DataFrame, una estructura tabular con indices etiquetados que funciona como una hoja de calculo programable. Donde NumPy asume datos homogeneos y numericos, Pandas maneja columnas de tipos mixtos, fechas, cadenas de texto y valores ausentes, lo cual lo convierte en la herramienta predilecta para la fase de preparacion de datos.

Un conjunto de datos real llega invariablemente con problemas: filas duplicadas, valores faltantes en columnas criticas, tipos de dato incorrectos y outliers que distorsionaran cualquier modelo posterior. La practica recomendada es encadenar transformaciones en un pipeline declarativo usando la API de metodos de Pandas:

python
# pandas_cleaning.py
import pandas as pd
import numpy as np

# Load and inspect raw data
df = pd.read_csv("candidates.csv")
print(df.shape)       # (1500, 8)
print(df.dtypes)      # Check column types
print(df.isna().sum()) # Count missing values per column

# Clean in a reproducible chain
df_clean = (
    df
    .dropna(subset=["salary", "experience_years"])      # Drop rows missing critical fields
    .assign(
        salary=lambda x: x["salary"].clip(lower=20000, upper=500000),  # Cap outliers
        experience_years=lambda x: x["experience_years"].astype(int),
        hired_date=lambda x: pd.to_datetime(x["hired_date"], errors="coerce"),
    )
    .drop_duplicates(subset=["email"])                  # Remove duplicate candidates
    .query("experience_years >= 0")                     # Filter invalid entries
    .reset_index(drop=True)
)

print(f"Cleaned: {len(df)} -> {len(df_clean)} rows")

Esta cadena de metodos tiene una ventaja sobre el enfoque imperativo de modificar el DataFrame paso a paso: es completamente reproducible, facil de auditar y genera un nuevo objeto en lugar de mutar el original. El uso de lambda dentro de .assign() permite referenciar el estado intermedio del DataFrame en ese punto de la cadena, lo que posibilita transformaciones que dependen de otras columnas.

clip() como alternativa a eliminar outliers

El metodo clip() recorta los valores fuera del rango especificado al minimo o maximo permitido, en lugar de eliminar las filas. Esto preserva el tamano del dataset a costa de introducir una distribucion truncada. La eleccion entre clip() y la eliminacion depende del contexto del negocio y del tipo de modelo que se vaya a entrenar.

Ingenieria de variables con groupby y transform

Una vez limpio el dataset, el siguiente paso es construir variables (features) que capturen patrones relevantes para el modelo. Las operaciones de agregacion por grupo son especialmente productivas: permiten crear metricas contextuales que un modelo de ML aprovecha mucho mejor que los valores crudos. Este proceso es central en las tareas de aprendizaje supervisado.

python
# pandas_groupby.py
import pandas as pd

# Aggregate candidate stats by department
dept_stats = (
    df_clean
    .groupby("department")
    .agg(
        avg_salary=("salary", "mean"),
        median_experience=("experience_years", "median"),
        headcount=("email", "count"),
        max_salary=("salary", "max"),
    )
    .sort_values("avg_salary", ascending=False)
)
print(dept_stats.head())

# Create features for ML: encode categorical + add aggregated stats
df_features = (
    df_clean
    .assign(
        # Ratio of individual salary to department average
        salary_ratio=lambda x: x["salary"] / x.groupby("department")["salary"].transform("mean"),
        # Time since hire in days
        tenure_days=lambda x: (pd.Timestamp.now() - x["hired_date"]).dt.days,
        # Binary encoding
        is_senior=lambda x: (x["experience_years"] >= 5).astype(int),
    )
)

La funcion .transform() es la clave en la linea del salary_ratio: a diferencia de .agg(), que reduce el grupo a una sola fila, .transform() devuelve un array del mismo tamano que el grupo original, lo que permite asignar el resultado directamente como una nueva columna en el DataFrame completo. Este patron evita la necesidad de un merge adicional y es uno de los conceptos mas frecuentes en las preguntas de entrevista sobre feature engineering.

¿Listo para aprobar tus entrevistas de Data Science & ML?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Scikit-Learn: pipelines reproducibles de principio a fin

Scikit-Learn es la biblioteca de machine learning de referencia en Python. Su diseno gira en torno a la API fit/transform/predict, que unifica la interfaz de preprocesadores, transformadores y estimadores. La clase Pipeline encadena estos componentes de forma que el modelo entrenado incluye todas las etapas de transformacion, eliminando la posibilidad de que el preprocesamiento del conjunto de entrenamiento se aplique de forma diferente al conjunto de prueba.

El siguiente ejemplo construye un pipeline completo para predecir si un empleado sera promovido, partiendo del DataFrame de features generado con Pandas:

python
# sklearn_pipeline.py
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
import pandas as pd

# Define column groups
numeric_features = ["salary", "experience_years", "salary_ratio", "tenure_days"]
categorical_features = ["department", "role_level"]
target = "promoted"

# Split before any preprocessing
X = df_features[numeric_features + categorical_features]
y = df_features[target]
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# Build the preprocessing + model pipeline
preprocessor = ColumnTransformer(
    transformers=[
        ("num", StandardScaler(), numeric_features),           # Scale numeric columns
        ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features),  # Encode categories
    ]
)

pipeline = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", GradientBoostingClassifier(
        n_estimators=200,
        learning_rate=0.1,
        max_depth=4,
        random_state=42,
    )),
])

# Train and evaluate
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred))

El uso de stratify=y en train_test_split garantiza que la proporcion de clases en entrenamiento y prueba sea la misma que en el dataset original, lo cual es especialmente importante cuando las clases estan desbalanceadas. ColumnTransformer permite aplicar transformaciones distintas a columnas numericas y categoricas en un solo paso, manteniendo la coherencia entre entrenamiento y produccion.

Nunca preprocesar antes de dividir el dataset

Aplicar StandardScaler o cualquier otra transformacion al dataset completo antes de dividirlo en entrenamiento y prueba introduce data leakage: el escalador aprende estadisticas del conjunto de prueba, lo que hace que las metricas de evaluacion sean optimistas y el modelo se comporte peor en produccion. El Pipeline de Scikit-Learn previene este error al ajustar los transformadores unicamente sobre los datos de entrenamiento durante el .fit().

Validacion cruzada y busqueda de hiperparametros

Entrenar un modelo una sola vez con una particion aleatoria no es suficiente para estimar su rendimiento real. La validacion cruzada k-fold evalua el modelo en k subconjuntos distintos del conjunto de entrenamiento, proporcionando una estimacion mas robusta de la generalizacion. Combinada con GridSearchCV, permite explorar el espacio de hiperparametros de forma sistematica:

python
# sklearn_tuning.py
from sklearn.model_selection import cross_val_score, GridSearchCV
import numpy as np

# 5-fold cross-validation on the full pipeline
scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="f1")
print(f"F1 scores: {scores}")
print(f"Mean F1: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")

# Grid search over hyperparameters
param_grid = {
    "classifier__n_estimators": [100, 200, 300],
    "classifier__max_depth": [3, 4, 5],
    "classifier__learning_rate": [0.05, 0.1, 0.2],
}

grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring="f1",
    n_jobs=-1,      # Use all CPU cores
    verbose=1,
)

grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best F1: {grid_search.best_score_:.3f}")

# Evaluate the best model on held-out test set
best_model = grid_search.best_estimator_
print(classification_report(y_test, best_model.predict(X_test)))

La notacion classifier__n_estimators en param_grid es la forma en que Scikit-Learn referencia los parametros de un paso especifico dentro de un Pipeline: el nombre del paso seguido de doble guion bajo y el nombre del parametro. El argumento n_jobs=-1 indica que se deben usar todos los nucleos de CPU disponibles para ejecutar los experimentos en paralelo, lo cual reduce considerablemente el tiempo de busqueda en grids grandes. Comprender estas mecanicas es esencial para responder las preguntas de entrevista sobre machine learning con precision.

Persistencia del modelo con joblib

Un modelo entrenado no tiene valor practico si no puede ser serializado, transportado y cargado en un proceso distinto. Scikit-Learn recomienda joblib en lugar de pickle para este proposito porque joblib maneja eficientemente los arrays de NumPy, que constituyen la mayor parte del estado interno de los modelos:

python
# sklearn_export.py
import joblib
from pathlib import Path

# Save the complete pipeline (preprocessor + model)
model_dir = Path("models")
model_dir.mkdir(exist_ok=True)
joblib.dump(best_model, model_dir / "promotion_model_v1.joblib")

# Load and predict in a different process
loaded_model = joblib.load(model_dir / "promotion_model_v1.joblib")
new_data = pd.DataFrame({
    "salary": [75000],
    "experience_years": [4],
    "salary_ratio": [1.05],
    "tenure_days": [730],
    "department": ["Engineering"],
    "role_level": ["Mid"],
})

prediction = loaded_model.predict(new_data)
probability = loaded_model.predict_proba(new_data)[:, 1]
print(f"Promoted: {bool(prediction[0])}, Confidence: {probability[0]:.2%}")

Guardar el Pipeline completo en lugar del estimador aislado es una practica critica: el archivo .joblib incluye el ColumnTransformer con todas las estadisticas aprendidas durante el entrenamiento (medias, desviaciones estandar, categorias del encoder). Al cargar el modelo en produccion, loaded_model.predict(new_data) aplica exactamente el mismo preprocesamiento que se utilizo durante el entrenamiento, sin necesidad de codigo adicional.

Consideraciones para entornos de produccion en 2026

El workflow descrito en este articulo cubre el ciclo completo de un proyecto de data science de tamano medio. Sin embargo, algunos patrones adicionales son relevantes cuando el modelo pasa de un notebook de exploracion a un servicio activo:

  • Versionado de datos: herramientas como DVC permiten rastrear los datasets junto con el codigo, de modo que cualquier version del modelo puede reproducirse con exactitud.
  • Monitoreo de drift: la distribucion de los datos de entrada en produccion puede desviarse de la del conjunto de entrenamiento con el tiempo. Bibliotecas como evidently automatizan la deteccion de este fenomeno.
  • Alternativas al GridSearchCV: para espacios de hiperparametros grandes, Optuna o HalvingGridSearchCV (incluido en Scikit-Learn desde la version 1.0) ofrecen busquedas mas eficientes mediante estrategias de poda adaptativa.
  • Feature stores: en equipos con multiples proyectos de ML, centralizar las transformaciones de variables en un feature store (Feast, Tecton) evita la duplicacion de logica y garantiza consistencia entre entrenamiento y servicio.
  • Tipos estaticos: desde Pandas 2.0, el uso de pd.ArrowDtype y la integracion con PyArrow reduce el consumo de memoria y mejora la interoperabilidad con herramientas como Polars o Apache Arrow.
Scikit-Learn 1.5: mejoras en la API de pipelines

Scikit-Learn 1.5 introdujo mejoras en la representacion HTML de los pipelines (visible en Jupyter con set_config(display='diagram')), soporte mejorado para tipos de dato de Pandas en los transformadores y mayor estabilidad en la API de FeatureUnion. Vale la pena revisar el changelog antes de migrar proyectos existentes.

Conclusion

NumPy, Pandas y Scikit-Learn comparten una filosofia de diseno coherente que facilita su integracion: todos operan sobre arrays de NumPy como estructura de datos comun, exponen interfaces consistentes y separan claramente las responsabilidades entre la manipulacion de datos y el modelado estadistico. Los puntos clave de este articulo son:

  • NumPy elimina los bucles explicitos con aritmetica vectorizada y broadcasting, siendo la base sobre la que se construyen las otras dos bibliotecas.
  • Pandas abstrae las operaciones de limpieza y transformacion en una API declarativa basada en cadenas de metodos; transform() es la funcion mas importante para crear variables contextuales sin perder filas.
  • Scikit-Learn estandariza el ciclo de vida del modelo con la API fit/transform/predict; el Pipeline con ColumnTransformer es el patron correcto para garantizar que el preprocesamiento sea identico en entrenamiento y produccion.
  • La validacion cruzada y GridSearchCV son herramientas inseparables del entrenamiento riguroso; evaluar sobre el conjunto de prueba solo una vez, al final, es la unica forma de obtener metricas honestas.
  • joblib.dump() sobre el pipeline completo es el mecanismo de persistencia recomendado para proyectos en produccion.

Dominar esta triada y ser capaz de explicar las decisiones de diseno detras de cada patron es lo que diferencia a un practicante solido de data science en el mercado laboral de 2026.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Etiquetas

#data-science
#python
#numpy
#pandas
#scikit-learn
#machine-learning
#tutorial

Compartir

Artículos relacionados