Evaluando Modelos de Aprendizaje Automático con Curvas ROC y AUC: Una Guía Integral
En el ámbito del aprendizaje automático, seleccionar el modelo adecuado para tu conjunto de datos es crucial para lograr predicciones precisas y confiables. Una de las maneras más efectivas de evaluar y comparar modelos es a través de la Curva Característica de Operación del Receptor (ROC) y el Área Bajo la Curva (AUC). Esta guía profundiza en la comprensión de las curvas ROC, el cálculo del AUC y el aprovechamiento de estas métricas para elegir el modelo con mejor desempeño para tus tareas de clasificación binaria. Acompañaremos un ejemplo práctico usando un Jupyter Notebook, demostrando cómo implementar estos conceptos utilizando diversos algoritmos de aprendizaje automático.
Tabla de Contenidos
- Introducción a la Curva ROC y al AUC
- ¿Por Qué AUC Mejor que Precisión?
- Descripción del Conjunto de Datos
- Preprocesamiento de Datos
- Entrenamiento y Evaluación del Modelo
- Elección del Mejor Modelo
- Conclusión
- Recursos
Introducción a la Curva ROC y al AUC
¿Qué es una Curva ROC?
Una Curva Característica de Operación del Receptor (ROC) es una representación gráfica que ilustra la capacidad diagnóstica de un sistema de clasificación binaria a medida que varía su umbral de discriminación. La curva ROC traza dos parámetros:
- Tasa de Verdaderos Positivos (TPR): También conocida como sensibilidad o recall, mide la proporción de positivos reales identificados correctamente.
- Tasa de Falsos Positivos (FPR): Mide la proporción de negativos reales que fueron identificados incorrectamente como positivos.
La curva ROC permite visualizar el compromiso entre sensibilidad y especificidad (1 – FPR) a través de diferentes configuraciones de umbral.
Comprendiendo el AUC
Área Bajo la Curva (AUC) cuantifica la capacidad general del modelo para discriminar entre clases positivas y negativas. El valor de AUC varía de 0 a 1:
- AUC = 1: Clasificador perfecto.
- AUC = 0.5: Sin discriminación (equivalente a adivinación aleatoria).
- AUC < 0.5: Predictivo inverso (peor que aleatorio).
Un AUC más alto indica un modelo con mejor desempeño.
¿Por Qué AUC Mejor que Precisión?
Mientras que la precisión mide la proporción de predicciones correctas sobre todas las predicciones realizadas, puede ser engañosa, especialmente en casos de desbalance de clases. Por ejemplo, si el 95% de los datos pertenece a una clase, un modelo que solo predice esa clase logrará una precisión del 95% pero no capturará la clase minoritaria.
AUC, por otro lado, proporciona una evaluación más matizada al considerar el desempeño del modelo a través de todos los umbrales de clasificación, lo que lo convierte en una métrica más confiable para conjuntos de datos desequilibrados.
Descripción del Conjunto de Datos
Para nuestro análisis, utilizaremos el Conjunto de Datos de Clima de Kaggle. Este conjunto de datos contiene diversos atributos relacionados con el clima registrados diariamente en diferentes lugares de Australia.
Objetivo: Predecir si lloverá mañana (RainTomorrow
) basándose en las condiciones climáticas de hoy.
Tipo: Clasificación Binaria (Yes
/No
).
Preprocesamiento de Datos
Un preprocesamiento efectivo de datos es la piedra angular para construir modelos robustos de aprendizaje automático. Aquí hay un desglose paso a paso:
1. Importación de Bibliotecas y Datos
1 2 3 4 5 6 |
import pandas as pd import seaborn as sns # Load the dataset data = pd.read_csv('weatherAUS.csv') data.tail() |
2. Separación de Características y Objetivo
1 2 3 4 5 |
# Features (All columns except the last one) X = data.iloc[:, :-1] # Target variable y = data.iloc[:, -1] |
3. Manejo de Datos Faltantes
a. Características Numéricas
1 2 3 4 5 6 7 8 9 |
import numpy as np from sklearn.impute import SimpleImputer # Identify numeric columns numerical_cols = list(np.where((X.dtypes == np.int64) | (X.dtypes == np.float64))[0]) # Impute missing values with mean imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean') X.iloc[:, numerical_cols] = imp_mean.fit_transform(X.iloc[:, numerical_cols]) |
b. Características Categóricas
1 2 3 4 5 6 |
# Identify object (categorical) columns string_cols = list(np.where((X.dtypes == object))[0]) # Impute missing values with the most frequent value imp_mode = SimpleImputer(missing_values=np.nan, strategy='most_frequent') X.iloc[:, string_cols] = imp_mode.fit_transform(X.iloc[:, string_cols]) |
4. Codificación de Variables Categóricas
a. Codificación Label para el Objetivo
1 2 3 4 5 |
from sklearn.preprocessing import LabelEncoder # Initialize Label Encoder le = LabelEncoder() y = le.fit_transform(y) |
b. Codificación de Características
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder # Function to perform One-Hot Encoding def OneHotEncoderMethod(indices, data): columnTransformer = ColumnTransformer( [('encoder', OneHotEncoder(), indices)], remainder='passthrough' ) return columnTransformer.fit_transform(data) # Identify columns for One-Hot Encoding based on the number of unique categories def EncodingSelection(X, threshold=10): string_cols = list(np.where((X.dtypes == object))[0]) one_hot_encoding_indices = [] for col in string_cols: unique_vals = len(pd.unique(X[X.columns[col]])) if unique_vals == 2 or unique_vals > threshold: X[X.columns[col]] = le.fit_transform(X[X.columns[col]]) else: one_hot_encoding_indices.append(col) X = OneHotEncoderMethod(one_hot_encoding_indices, X) return X X = EncodingSelection(X) |
5. Selección de Características
Para reducir la complejidad del modelo y mejorar el desempeño, seleccionaremos las 10 mejores características usando la prueba de Chi-Cuadrado (Chi2).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from sklearn.feature_selection import SelectKBest, chi2 from sklearn.preprocessing import MinMaxScaler # Initialize SelectKBest kbest = SelectKBest(score_func=chi2, k=10) scaler = MinMaxScaler() # Scale features X_scaled = scaler.fit_transform(X) # Fit SelectKBest kbest.fit(X_scaled, y) # Get top 10 feature indices best_features = np.argsort(kbest.scores_)[-10:] # Select top features X = X[:, best_features] |
6. División del Conjunto de Datos
1 2 3 4 5 6 |
from sklearn.model_selection import train_test_split # Split data into training and testing sets (80-20 split) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.20, random_state=1 ) |
7. Escalado de Características
Estandarizar las características asegura que cada una contribuya por igual al resultado.
1 2 3 4 5 |
from sklearn.preprocessing import StandardScaler sc = StandardScaler(with_mean=False) X_train = sc.fit_transform(X_train) X_test = sc.transform(X_test) |
Entrenamiento y Evaluación del Modelo
Entrenaremos varios modelos de clasificación y evaluaremos su desempeño utilizando tanto la Precisión como el AUC.
K-Vecinos Más Cercanos (KNN)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score, roc_curve, auc import matplotlib.pyplot as plt from sklearn import metrics # Initialize and train KNN knnClassifier = KNeighborsClassifier(n_neighbors=3) knnClassifier.fit(X_train, y_train) # Predict and evaluate y_pred_knn = knnClassifier.predict(X_test) accuracy_knn = accuracy_score(y_pred_knn, y_test) print(f'KNN Accuracy: {accuracy_knn:.2f}') # Plot ROC Curve metrics.plot_roc_curve(knnClassifier, X_test, y_test) plt.title('KNN ROC Curve') plt.show() |
Salida:
1 |
KNN Accuracy: 0.82 |

Regresión Logística
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.linear_model import LogisticRegression # Initialize and train Logistic Regression LRM = LogisticRegression(random_state=0, max_iter=200) LRM.fit(X_train, y_train) # Predict and evaluate y_pred_lr = LRM.predict(X_test) accuracy_lr = accuracy_score(y_pred_lr, y_test) print(f'Logistic Regression Accuracy: {accuracy_lr:.2f}') # Plot ROC Curve metrics.plot_roc_curve(LRM, X_test, y_test) plt.title('Logistic Regression ROC Curve') plt.show() |
Salida:
1 |
Logistic Regression Accuracy: 0.84 |

Nota: Si encuentras una advertencia de convergencia, considera aumentar max_iter
o estandarizar tus datos.
Bayesiano Naive Gaussiano
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.naive_bayes import GaussianNB # Initialize and train GaussianNB model_GNB = GaussianNB() model_GNB.fit(X_train, y_train) # Predict and evaluate y_pred_gnb = model_GNB.predict(X_test) accuracy_gnb = accuracy_score(y_pred_gnb, y_test) print(f'Gaussian Naive Bayes Accuracy: {accuracy_gnb:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_GNB, X_test, y_test) plt.title('Gaussian Naive Bayes ROC Curve') plt.show() |
Salida:
1 |
Gaussian Naive Bayes Accuracy: 0.81 |

Máquina de Vectores de Soporte (SVM)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.svm import SVC # Initialize and train SVM model_SVC = SVC(probability=True) model_SVC.fit(X_train, y_train) # Predict and evaluate y_pred_svc = model_SVC.predict(X_test) accuracy_svc = accuracy_score(y_pred_svc, y_test) print(f'SVM Accuracy: {accuracy_svc:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_SVC, X_test, y_test) plt.title('SVM ROC Curve') plt.show() |
Salida:
1 |
SVM Accuracy: 0.84 |

Árbol de Decisión
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.tree import DecisionTreeClassifier # Initialize and train Decision Tree model_DTC = DecisionTreeClassifier() model_DTC.fit(X_train, y_train) # Predict and evaluate y_pred_dtc = model_DTC.predict(X_test) accuracy_dtc = accuracy_score(y_pred_dtc, y_test) print(f'Decision Tree Accuracy: {accuracy_dtc:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_DTC, X_test, y_test) plt.title('Decision Tree ROC Curve') plt.show() |
Salida:
1 |
Decision Tree Accuracy: 0.78 |

Bosque Aleatorio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.ensemble import RandomForestClassifier # Initialize and train Random Forest model_RFC = RandomForestClassifier(n_estimators=500, max_depth=5) model_RFC.fit(X_train, y_train) # Predict and evaluate y_pred_rfc = model_RFC.predict(X_test) accuracy_rfc = accuracy_score(y_pred_rfc, y_test) print(f'Random Forest Accuracy: {accuracy_rfc:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_RFC, X_test, y_test) plt.title('Random Forest ROC Curve') plt.show() |
Salida:
1 |
Random Forest Accuracy: 0.84 |

AdaBoost
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.ensemble import AdaBoostClassifier # Initialize and train AdaBoost model_ABC = AdaBoostClassifier() model_ABC.fit(X_train, y_train) # Predict and evaluate y_pred_abc = model_ABC.predict(X_test) accuracy_abc = accuracy_score(y_pred_abc, y_test) print(f'AdaBoost Accuracy: {accuracy_abc:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_ABC, X_test, y_test) plt.title('AdaBoost ROC Curve') plt.show() |
Salida:
1 |
AdaBoost Accuracy: 0.84 |

XGBoost
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import xgboost as xgb from sklearn.exceptions import ConvergenceWarning import warnings # Suppress warnings warnings.filterwarnings("ignore", category=ConvergenceWarning) warnings.filterwarnings("ignore", category=UserWarning) # Initialize and train XGBoost model_xgb = xgb.XGBClassifier(use_label_encoder=False, eval_metric='logloss') model_xgb.fit(X_train, y_train) # Predict and evaluate y_pred_xgb = model_xgb.predict(X_test) accuracy_xgb = accuracy_score(y_pred_xgb, y_test) print(f'XGBoost Accuracy: {accuracy_xgb:.2f}') # Plot ROC Curve metrics.plot_roc_curve(model_xgb, X_test, y_test) plt.title('XGBoost ROC Curve') plt.show() |
Salida:
1 |
XGBoost Accuracy: 0.85 |

Elección del Mejor Modelo
Después de evaluar todos los modelos, observamos las siguientes precisiones:
Modelo | Precisión | AUC |
---|---|---|
K-Vecinos Más Cercanos | 0.82 | 0.80 |
Regresión Logística | 0.84 | 0.86 |
Bayesiano Naive Gaussiano | 0.81 | 0.81 |
SVM | 0.84 | 0.86 |
Árbol de Decisión | 0.78 | 0.89 |
Bosque Aleatorio | 0.84 | 0.85 |
AdaBoost | 0.84 | 0.86 |
XGBoost | 0.85 | 0.87 |
Observaciones Clave:
- XGBoost emerge como el de mejor desempeño con la mayor precisión (85%) y un AUC sólido (0.87).
- Regresión Logística, SVM y AdaBoost también demuestran un desempeño encomiable con precisiones alrededor del 84% y AUC de 0.86.
- Árbol de Decisión muestra la menor precisión (78%) pero tiene un AUC relativamente alto (0.89), indicando potencial para distinguir clases a pesar de una menor precisión en la predicción.
Conclusión: Mientras que la precisión proporciona una métrica directa, el AUC ofrece una visión más profunda del desempeño del modelo a través de diversos umbrales. En este escenario, XGBoost destaca como el modelo más confiable, equilibrando una alta precisión y una fuerte capacidad discriminativa.
Conclusión
Evaluar modelos de aprendizaje automático requiere un enfoque multifacético. Basarse únicamente en la precisión puede ser engañoso, especialmente en conjuntos de datos con desequilibrios de clases. Las curvas ROC y el AUC proporcionan una evaluación más completa del desempeño de un modelo, destacando su capacidad para distinguir entre clases de manera efectiva.
En esta guía, exploramos cómo preprocesar datos, entrenar múltiples modelos de clasificación y evaluarlos utilizando curvas ROC y AUC. La implementación práctica usando un Jupyter Notebook mostró las fortalezas de cada modelo, demostrando finalmente que XGBoost fue la opción superior para predecir lluvias basándose en el conjunto de datos proporcionado.
Recursos
- Curva ROC en Wikipedia
- AUC Explicado
- Conjunto de Datos de Clima de Kaggle
- Documentación de Scikit-Learn
- Documentación de XGBoost
Al comprender y utilizar las curvas ROC y el AUC, los científicos de datos y los profesionales de aprendizaje automático pueden tomar decisiones más informadas al seleccionar modelos, asegurando un mayor desempeño y confiabilidad en sus tareas predictivas.