W3docs

Curva AUC - ROC

Aprende a calcular y graficar curvas AUC-ROC en Python con sklearn. Comprende TPR, FPR, umbrales y cuándo usar AUC en lugar de precisión.

La curva AUC-ROC (Área Bajo la Curva Característica Operativa del Receptor) es una de las métricas más importantes para evaluar modelos de clasificación binaria. Captura la capacidad de un modelo para separar ejemplos positivos y negativos a través de todos los posibles umbrales de decisión, brindando una imagen mucho más completa que una única puntuación de precisión.

Este capítulo cubre:

  • Qué es la curva ROC y cómo se construye
  • Cómo interpretar TPR, FPR y los términos de la matriz de confusión que los sustentan
  • Cómo calcular AUC-ROC con scikit-learn de Python
  • Cómo graficar la curva ROC con matplotlib
  • Cuándo AUC-ROC es la métrica adecuada y cuándo no lo es

Términos clave: TP, FP, TN, FN

Antes de adentrarnos en la curva en sí, es importante entender los cuatro resultados posibles que puede producir un clasificador binario. Para cualquier predicción, la etiqueta real es positiva (P) o negativa (N), y la etiqueta predicha también es positiva o negativa:

Predicho PositivoPredicho Negativo
Real PositivoVerdadero Positivo (TP)Falso Negativo (FN)
Real NegativoFalso Positivo (FP)Verdadero Negativo (TN)

Estas cuatro celdas son los bloques fundamentales de todas las métricas de evaluación tratadas en este capítulo. Para una introducción más detallada, consulta el capítulo de la Matriz de Confusión.

¿Qué es la curva ROC?

Un clasificador suele generar una puntuación de probabilidad (un número entre 0 y 1) en lugar de una etiqueta definitiva. Se elige un umbral — por ejemplo, 0.5 — y todo lo que esté por encima del umbral se predice como positivo.

Cambiar el umbral desplaza el equilibrio entre capturar verdaderos positivos y marcar incorrectamente verdaderos negativos:

  • Un umbral muy bajo captura más positivos (alta recuperación), pero también genera más falsas alarmas.
  • Un umbral muy alto es más selectivo (alta precisión), pero pierde positivos reales.

La curva ROC representa esta disyuntiva para todos los umbrales posibles a la vez:

  • Eje X — Tasa de Falsos Positivos (FPR): qué fracción de los negativos reales se marca erróneamente como positiva.
  • Eje Y — Tasa de Verdaderos Positivos (TPR), también llamada Recall o Sensibilidad: qué fracción de los positivos reales se identifica correctamente.

TPR = TP / (TP + FN) y FPR = FP / (FP + TN)

Cada punto de la curva ROC corresponde a un valor de umbral. En conjunto, trazan un camino desde (0, 0) — el umbral más selectivo, que no predice nada como positivo — hasta (1, 1) — el umbral menos selectivo, que predice todo como positivo.

¿Qué es AUC?

El AUC (Área Bajo la Curva) resume toda la curva ROC en un único número entre 0 y 1:

AUCSignificado
1.0Clasificador perfecto — separa cada positivo de cada negativo
0.5Sin habilidad — equivalente a adivinar al azar
< 0.5Peor que el azar (las probabilidades predichas están invertidas)
0.7 – 0.8Aceptable para muchos problemas prácticos
0.8 – 0.9Bueno
> 0.9Excelente

Intuitivamente, AUC es la probabilidad de que el modelo clasifique un ejemplo positivo elegido al azar más alto que un ejemplo negativo elegido al azar.

Cálculo de AUC-ROC con scikit-learn

El módulo sklearn.metrics proporciona roc_curve() para obtener los valores de TPR/FPR por umbral y roc_auc_score() para obtener el valor único de AUC.

Ejemplo mínimo funcional

from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve

# 1. Create a toy binary-classification dataset
X, y = make_classification(n_samples=500, n_features=10, random_state=42)

# 2. Split into train / test sets
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# 3. Train a logistic regression model
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)

# 4. Get probability scores for the positive class (column 1)
y_proba = model.predict_proba(X_test)[:, 1]

# 5. Compute AUC
auc = roc_auc_score(y_test, y_proba)
print(f"AUC-ROC: {auc:.3f}")

# 6. Get the (fpr, tpr, thresholds) arrays for plotting
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
print(f"Number of threshold points: {len(thresholds)}")

La salida será similar a:

AUC-ROC: 0.944
Number of threshold points: 30

El valor exacto varía ligeramente según la versión de scikit-learn, pero debería estar en el rango de 0.90–0.97 para este conjunto de datos.

Graficando la curva ROC

import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve

X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)

y_proba = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_proba)
fpr, tpr, _ = roc_curve(y_test, y_proba)

plt.figure(figsize=(7, 5))
plt.plot(fpr, tpr, color="steelblue", lw=2, label=f"ROC curve (AUC = {auc:.2f})")
plt.plot([0, 1], [0, 1], color="gray", linestyle="--", label="Random baseline (AUC = 0.50)")
plt.xlabel("False Positive Rate (FPR)")
plt.ylabel("True Positive Rate (TPR)")
plt.title("ROC Curve")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_curve.png", dpi=120)
plt.show()

La línea diagonal discontinua representa a un clasificador aleatorio. Cuanto más se curve la curva ROC hacia la esquina superior izquierda, mejor es el modelo.

Comparación de múltiples modelos

Un flujo de trabajo común es entrenar varios modelos y comparar sus curvas ROC en la misma gráfica:

from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt

X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

models = {
    "Logistic Regression": LogisticRegression(random_state=42),
    "Decision Tree": DecisionTreeClassifier(max_depth=3, random_state=42),
}

plt.figure(figsize=(7, 5))
for name, clf in models.items():
    clf.fit(X_train, y_train)
    y_proba = clf.predict_proba(X_test)[:, 1]
    fpr, tpr, _ = roc_curve(y_test, y_proba)
    auc = roc_auc_score(y_test, y_proba)
    plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.2f})")

plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve Comparison")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_comparison.png", dpi=120)
plt.show()

El modelo con el área mayor bajo su curva suele ser la mejor opción para ese conjunto de datos. Para un flujo de selección de modelos riguroso, combina esto con validación cruzada.

Elección del punto de operación (umbral)

El AUC resume todos los umbrales, pero en algún momento necesitarás elegir un único umbral para producción. Dos estrategias comunes:

1. Estadístico J de Youden

El estadístico J de Youden maximiza TPR − FPR, encontrando el punto único de la curva ROC que se encuentra más lejos de la diagonal:

from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve
import numpy as np

X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]

fpr, tpr, thresholds = roc_curve(y_test, y_proba)
j_scores = tpr - fpr
best_idx = int(np.argmax(j_scores))
best_threshold = thresholds[best_idx]

print(f"Best threshold (Youden's J): {best_threshold:.3f}")
print(f"TPR at best threshold: {tpr[best_idx]:.3f}")
print(f"FPR at best threshold: {fpr[best_idx]:.3f}")

2. Umbral basado en el negocio

En la detección de fraudes puedes tolerar más falsos positivos para atrapar más fraudes (umbral más bajo). En el cribado médico es posible que quieras muy pocos falsos negativos (también umbral más bajo). Elegir el umbral es en última instancia una decisión de negocio informada por el coste de cada tipo de error.

AUC-ROC frente a otras métricas

MétricaMejor cuando...Precaución con...
AccuracyLas clases están balanceadasEngañosa en conjuntos de datos desequilibrados
Precision / RecallTe importa principalmente una claseRequiere un umbral
F1-ScoreBalance armónico de precisión y recallSigue dependiendo del umbral
AUC-ROCComparar modelos o ajustar umbralesNo significativa para multiclase sin extensión
AUC-PR (Precision-Recall AUC)Fuerte desequilibrio de clases (positivos raros)Menos intuitiva que ROC

Para datos severamente desequilibrados — por ejemplo, 1 % de positivos — la curva AUC-PR (precisión frente a recall) suele ser más informativa que la AUC-ROC, porque la FPR puede parecer pequeña incluso cuando muchos negativos están marcados incorrectamente.

Errores comunes

Pasar etiquetas definitivas en lugar de probabilidades. roc_auc_score necesita puntuaciones de probabilidad, no etiquetas 0/1. Usa model.predict_proba(X_test)[:, 1], no model.predict(X_test).

Olvidar especificar la clase positiva. Por defecto, roc_curve y roc_auc_score tratan la etiqueta entera mayor como positiva. Pasa pos_label=1 explícitamente cuando tus etiquetas no sean enteros 0/1.

Sobreajuste del AUC en el conjunto de entrenamiento. Evalúa siempre en un conjunto de prueba separado o usa validación cruzada para obtener una estimación fiable del AUC. El capítulo de Grid Search muestra cómo pasar scoring='roc_auc' directamente a GridSearchCV.

AUC ≈ 0.5 no siempre significa que el modelo es malo. Puede significar que los ejemplos positivos y negativos se solapan genuinamente en el espacio de características, o que tus características no son informativas para la tarea.

Ejemplo práctico: predicción de enfermedades

El siguiente ejemplo completo utiliza el conjunto de datos de cáncer de mama incluido en scikit-learn:

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt

# Load dataset (569 samples, 30 features, binary labels: malignant=0 / benign=1)
data = load_breast_cancer()
X, y = data.data, data.target

# Scale features — important for logistic regression
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

X_train, X_test, y_train, y_test = train_test_split(
    X_scaled, y, test_size=0.2, random_state=42, stratify=y
)

classifiers = {
    "Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
    "Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
}

plt.figure(figsize=(7, 5))
for name, clf in classifiers.items():
    clf.fit(X_train, y_train)
    y_proba = clf.predict_proba(X_test)[:, 1]
    auc = roc_auc_score(y_test, y_proba)
    fpr, tpr, _ = roc_curve(y_test, y_proba)
    plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.3f})")

plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve — Breast Cancer Dataset")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("breast_cancer_roc.png", dpi=120)
plt.show()

Ambos clasificadores deberían producir puntuaciones AUC superiores a 0.98 en este conjunto de datos, confirmando que las características del cáncer de mama son muy predictivas.

Temas relacionados

  • Matriz de Confusión — los bloques fundamentales TP/FP/TN/FN usados a lo largo de este capítulo
  • Regresión Logística — el modelo más común emparejado con la evaluación AUC-ROC
  • Validación Cruzada — cómo obtener estimaciones fiables de AUC que generalicen más allá de una única división entrenamiento/prueba
  • Grid Search — cómo ajustar hiperparámetros con scoring='roc_auc'
  • Árbol de Decisión — otro clasificador binario cuyas salidas de probabilidad pueden evaluarse con AUC-ROC

Resumen

ConceptoPunto clave
Curva ROCRepresenta TPR frente a FPR en cada umbral de decisión
AUCÁrea bajo la curva ROC; 1.0 = perfecto, 0.5 = aleatorio
roc_auc_scorePasa puntuaciones de probabilidad, no etiquetas definitivas
roc_curveDevuelve arrays (fpr, tpr, thresholds) para graficar
Selección de umbralUsa el estadístico J de Youden o el conocimiento del dominio para elegir un umbral de producción
Cuándo preferir AUC-PRConjuntos de datos severamente desequilibrados con positivos raros

La AUC-ROC te proporciona un único número independiente del umbral que resume la capacidad discriminativa de tu modelo en todos los puntos de operación. Úsala para comparar modelos, ajustar hiperparámetros y comunicar la calidad del clasificador — luego elige el umbral específico que se ajuste a la tolerancia de tu aplicación a los falsos positivos frente a los falsos negativos.

Was this page helpful?