Sklearn train_test_split: Dividir datos para Machine Learning en Python
Updated on
Entrenar un modelo de machine learning con los mismos datos que usas para evaluarlo produce una precisión engañosamente alta. El modelo memoriza los datos de entrenamiento en lugar de aprender patrones generalizables -- un problema llamado sobreajuste (overfitting). Necesitas un conjunto de prueba separado que el modelo nunca vea durante el entrenamiento para obtener métricas de rendimiento honestas.
La función train_test_split() de Scikit-learn es la forma estándar de dividir conjuntos de datos en porciones de entrenamiento y prueba. Maneja arrays, DataFrames y matrices dispersas, con opciones para estratificación, reproducibilidad y ratios de división personalizados.
Uso básico
from sklearn.model_selection import train_test_split
import numpy as np
# Datos de ejemplo: 100 muestras, 5 características
X = np.random.randn(100, 5)
y = np.random.randint(0, 2, 100) # Etiquetas binarias
# División: 80% entrenamiento, 20% prueba
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"Set de entrenamiento: {X_train.shape[0]} muestras")
print(f"Set de prueba: {X_test.shape[0]} muestras")
# Set de entrenamiento: 80 muestras
# Set de prueba: 20 muestrasParámetros clave
| Parámetro | Valor predeterminado | Descripción |
|---|---|---|
test_size | 0.25 | Fracción (0.0-1.0) o número absoluto de muestras de prueba |
train_size | None | Fracción o número de muestras de entrenamiento (complemento de test_size) |
random_state | None | Semilla para divisiones reproducibles |
shuffle | True | Si se barajan los datos antes de dividir |
stratify | None | Array para división estratificada |
Con Pandas DataFrames
from sklearn.model_selection import train_test_split
import pandas as pd
df = pd.DataFrame({
'age': [25, 30, 35, 40, 45, 50, 55, 60, 28, 33],
'income': [40, 50, 60, 70, 80, 90, 100, 110, 45, 55],
'purchased': [0, 0, 1, 1, 1, 1, 1, 1, 0, 0]
})
X = df[['age', 'income']]
y = df['purchased']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
print(f"Train shape: {X_train.shape}") # (7, 2)
print(f"Test shape: {X_test.shape}") # (3, 2)
print(type(X_train)) # <class 'pandas.core.frame.DataFrame'>Los DataFrames siguen siendo DataFrames después de la división -- los nombres de columnas e índices se preservan.
random_state: Divisiones reproducibles
Sin random_state, obtienes una división diferente cada vez:
from sklearn.model_selection import train_test_split
import numpy as np
X = np.arange(10).reshape(5, 2)
y = np.array([0, 0, 1, 1, 1])
# Sin random_state: división diferente en cada ejecución
_, X_test1, _, _ = train_test_split(X, y, test_size=0.4)
_, X_test2, _, _ = train_test_split(X, y, test_size=0.4)
print(np.array_equal(X_test1, X_test2)) # Probablemente False
# Con random_state: misma división cada vez
_, X_test3, _, _ = train_test_split(X, y, test_size=0.4, random_state=42)
_, X_test4, _, _ = train_test_split(X, y, test_size=0.4, random_state=42)
print(np.array_equal(X_test3, X_test4)) # TrueSiempre establece random_state para reproducibilidad. Usa cualquier entero -- 42 es convencional, pero el número específico no importa.
División estratificada
Para conjuntos de datos desbalanceados, una división aleatoria podría poner la mayoría de las muestras minoritarias en un set. La estratificación asegura que ambos sets tengan las mismas proporciones de clase:
from sklearn.model_selection import train_test_split
import numpy as np
from collections import Counter
# Conjunto de datos desbalanceado: 90% clase 0, 10% clase 1
np.random.seed(42)
X = np.random.randn(200, 4)
y = np.array([0] * 180 + [1] * 20)
# Sin estratificación
_, _, y_train_bad, y_test_bad = train_test_split(
X, y, test_size=0.2, random_state=42
)
print("Sin estratificación:")
print(f" Train: {Counter(y_train_bad)}")
print(f" Test: {Counter(y_test_bad)}")
# Con estratificación
_, _, y_train_good, y_test_good = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print("\nCon stratify=y:")
print(f" Train: {Counter(y_train_good)}")
print(f" Test: {Counter(y_test_good)}")
# Train: Counter({0: 144, 1: 16}) -- 10% clase 1
# Test: Counter({0: 36, 1: 4}) -- 10% clase 1Cuándo usar estratificación
| Escenario | ¿Usar stratify? |
|---|---|
| Clases balanceadas (50/50) | Opcional |
| Clases desbalanceadas (90/10) | Sí |
| Clasificación multiclase | Sí |
| Regresión (objetivo continuo) | No (no soportado) |
| Conjuntos pequeños (< 100 muestras) | Sí (previene clases vacías) |
División Train/Validación/Test
Para el ajuste de hiperparámetros, necesitas tres conjuntos: entrenamiento, validación y prueba. Aplica train_test_split dos veces:
from sklearn.model_selection import train_test_split
import numpy as np
X = np.random.randn(1000, 10)
y = np.random.randint(0, 3, 1000)
# Primera división: 80% train+val, 20% test
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Segunda división: 75% train, 25% val (del 80% = 60/20/20 en total)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.25, random_state=42, stratify=y_temp
)
print(f"Entrenamiento: {X_train.shape[0]} muestras (60%)")
print(f"Validación: {X_val.shape[0]} muestras (20%)")
print(f"Prueba: {X_test.shape[0]} muestras (20%)")Ejemplo completo de pipeline ML
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
import numpy as np
# Generar datos de ejemplo
np.random.seed(42)
X = np.random.randn(500, 8)
y = (X[:, 0] + X[:, 1] > 0).astype(int)
# Dividir datos
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Escalar características (¡ajustar solo con datos de entrenamiento!)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # Usar estadísticas del entrenamiento
# Entrenar modelo
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
# Evaluar
y_pred = model.predict(X_test_scaled)
print(f"Precisión: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))Regla crítica: Ajusta el scaler (y cualquier preprocesamiento) solo con el set de entrenamiento. Aplica la misma transformación al set de prueba. Ajustar con el conjunto completo causa fuga de datos (data leakage).
Ratios de división comunes
| División | Entrenamiento | Prueba | Cuándo usar |
|---|---|---|---|
| 80/20 | 80% | 20% | Opción predeterminada, la mayoría de conjuntos |
| 70/30 | 70% | 30% | Conjuntos pequeños, se necesita mayor set de prueba |
| 90/10 | 90% | 10% | Conjuntos grandes (10k+ muestras) |
| 60/20/20 | 60% | 20% val + 20% test | Al ajustar hiperparámetros |
Explorar resultados del modelo
Después de dividir y entrenar tu modelo, PyGWalker (opens in a new tab) te permite explorar interactivamente predicciones vs valores reales, distribuciones de características entre sets de train/test y patrones de error en Jupyter:
import pandas as pd
import pygwalker as pyg
results = pd.DataFrame({
'actual': y_test,
'predicted': y_pred,
'correct': y_test == y_pred
})
walker = pyg.walk(results)FAQ
¿Qué hace train_test_split en sklearn?
train_test_split() divide aleatoriamente arrays o DataFrames en dos subconjuntos: uno para entrenamiento y otro para prueba. Asegura que la evaluación del modelo use datos que no ha visto durante el entrenamiento, proporcionando estimaciones de rendimiento honestas.
¿Cuál es el mejor ratio de división train/test?
80/20 es el valor predeterminado estándar. Usa 70/30 para conjuntos más pequeños donde necesitas un set de prueba confiable, y 90/10 para conjuntos grandes (10k+ muestras) donde el 10% sigue siendo sustancial. Para ajuste de hiperparámetros, usa 60/20/20 (train/val/test).
¿Qué hace random_state en train_test_split?
random_state establece la semilla aleatoria para el barajado que ocurre antes de la división. Usar el mismo valor de random_state produce la misma división cada vez, haciendo tus resultados reproducibles. Cualquier entero funciona.
¿Cuándo debo usar stratify en train_test_split?
Usa stratify=y cuando tu variable objetivo esté desbalanceada (ej., 95% negativo, 5% positivo) o cuando tengas un conjunto de datos pequeño. La estratificación asegura que tanto el set de entrenamiento como el de prueba tengan la misma proporción de cada clase.
¿Cómo divido datos en sets de entrenamiento, validación y prueba?
Llama a train_test_split dos veces. Primero divide en train+val y test (ej., 80/20). Luego divide train+val en train y val (ej., 75/25 del 80%, dando 60/20/20 en total). Alternativamente, usa sklearn.model_selection.KFold para validación cruzada.
Conclusión
train_test_split() es la base de todo flujo de trabajo de machine learning en Python. Úsalo siempre antes de entrenar -- nunca evalúes con datos de entrenamiento. Establece random_state para reproducibilidad, usa stratify=y para clases desbalanceadas, y recuerda ajustar los pasos de preprocesamiento solo con el set de entrenamiento. Para selección de modelos, divide en tres partes (train/validación/test) o usa validación cruzada para estimaciones más robustas.