Evaluación de Clasificadores: Métricas Esenciales y su Interpretación
Autor/a
Diego Villalba
Fecha de publicación
19 de mayo de 2026
Introducción
La evaluación de modelos de clasificación constituye uno de los problemas más estratégicamente complejos en la Minería de Datos. No es suficiente construir un modelo que “funcione”: es necesario cuantificar de manera precisa y contextualmente apropiada qué tan bien funciona, en qué escenarios falla y qué tipo de errores comete, ya que como veremos mas adelante la calidad de un clasificador esta completamente determiada por el objetivo del modelo y el contexto de aplicación (Han, Kamber, y Pei 2011).
Esta distinción tiene consecuencias directas en entornos reales: un modelo de diagnóstico médico que confunde “exactitud estadística” con “utilidad clínica” puede dejar pacientes enfermos sin tratamiento; un sistema de detección de fraude que optimiza la métrica equivocada puede bloquear transacciones legítimas y dañar la relación con el cliente.
De esta manera la literatura especializada ha producido un conjunto robusto de métricas y herramientas de evaluación que van más allá del intuitivo porcentaje de aciertos. Con lo cual comprender estas métricas es competencia central del científico de datos (Provost y Fawcett 2013).
Este capítulo desarrolla sistemáticamente las métricas más importantes para la evaluación de clasificadores binarios y multiclase, integrándolas con el proceso KDD (Knowledge Discovery in Databases) y con la lógica de la toma de decisiones en contextos de alto impacto. Cada sección combina rigor matemático con ejemplos de código ejecutable en Python, siguiendo el principio de que la comprensión profunda de una métrica requiere tanto su derivación formal como su implementación y visualización sobre datos reales.
Errores
En este capitulo definiremos un error como la situación en la que el modelo de clasificación asigna una etiqueta incorrecta a una instancia.
Mientras que definiremos el ratio de error como la proporción de instancias clasificadas incorrectamente sobre el total de instancias evaluadas.
Definición. El ratio de error de un clasificador con \(k\) errores en un conjunto de prueba de \(n\) instancias se define como:
\[
\epsilon = \frac{k}{n}
\tag{1}\]
En primera instnacia esta definición puede parecer suficiente para los propositos de cualquier clasificador, ya que en la mayoria de los casos el objetivo e minimizar el numero de errores que comete el modelo. Sin embargo como veremos a lo largo del capitulo, no todos los errores son iguales, ya que dependiendo del contexto una equivocación puede tener diferentes consecuencias que exploraremos a continuación.
Tipo de errores y su costo
La matriz de confusión: la base de toda evaluación de clasificadores
En terminos generales decimos que una una clase es positiva (1) cuando es la clase de interés, y negativa (0) cuando es la clase complementaria. Por ejemplo, en un sistema de detección de fraude, la clase positiva sería “fraude” y la negativa “transacción legítima”. En un sistema de diagnóstico médico, la clase positiva sería “enfermo” y la negativa “sano”.
De esta manera podemos clasificar los errores de un clasificador binario por medio de la matriz de confusión, que es la representación tabular que sintetiza el desempeño de un clasificador binario (Han, Kamber, y Pei 2011). Formalmente se define como:
Definición. Para un clasificador con clases positiva (1) y negativa (0), la matriz de confusión se define como:
donde la fila representa la clase real y la columna la clase predicha. Los cuatro elementos son:
TP (Verdadero Positivo): El modelo predijo positivo y el caso real es positivo. Clasificación correcta de la clase de interés.
TN (Verdadero Negativo): El modelo predijo negativo y el caso real es negativo. Clasificación correcta de la clase mayoritaria.
FP (Falso Positivo): El modelo predijo positivo pero el caso real es negativo. También llamado Error Tipo I.
FN (Falso Negativo): El modelo predijo negativo pero el caso real es positivo. También llamado Error Tipo II.
Visualmente podemos visualizar los tipos de errores en la siguiente figura:
Representación visual de una matriz de confusión y métricas de clasificación binaria. El diagrama ilustra la relación entre la realidad (columnas Positivo/Negativo) y las predicciones del modelo (círculo central). Se identifican los Verdaderos Positivos (TP) y Falsos Positivos (FP) dentro del área de predicción, mientras que los Falsos Negativos (FN) y Verdaderos Negativos (TN) se ubican en el área de exclusión.
Como veremos mas adelante, la matriz de confusión es la base de toda evaluación de clasificadores, ya que a partir de ella se derivan todas las métricas de desempeño. Además, la interpretación de cada elemento de la matriz tiene implicaciones estratégicas directas en la toma de decisiones basada en el modelo.
Generación de los Datasets Sintéticos
A lo largo de este capítulo se utilizan dos datasets sintéticos que replican las características estadísticas de problemas reales.
El Dataset Médico simula la detección de una condición grave con prevalencia del ~8 %, reflejando el desbalance típico en estudios de screening.
El Dataset Financiero simula transacciones con una tasa de fraude del ~3 %, consistente con benchmarks de la industria (Pedregosa et al., 2011).
Ambos datasets se generan con sklearn.datasets.make_classification, que permite controlar el número de características informativas, el nivel de ruido en las etiquetas y el desbalance de clases.
Distribución de clases — test médico : {np.int64(0): np.int64(275), np.int64(1): np.int64(25)}
Distribución de clases — test financiero: {np.int64(0): np.int64(1449), np.int64(1): np.int64(51)}
Para evaluar a lo largo del capitulo diferentes modelos empleamos tres modelos de clasificación binaria: una regresión logística y un random forest para el dataset médico, y una regresión logística con ajuste de clase para el dataset financiero.
Así como primer ejemplo mostramos la matriz de confusión del modelo de regresión logística sobre el dataset médico:
Mostrar código
# ── Función reutilizable: matriz de confusión interactiva ───────────────────def plot_confusion_matrix(cm,titulo=" ", etiquetas=None):""" Visualiza una matriz de confusión 2×2 con Plotly. Muestra conteos absolutos y porcentajes por fila (tasa de acierto/error por clase real). Parámetros ---------- cm : np.ndarray — salida de sklearn confusion_matrix titulo : str — título del gráfico etiquetas : list[str] — nombres de las clases [neg, pos] """if etiquetas isNone: etiquetas = ["Negativo (0)", "Positivo (1)"] cm_pct = cm.astype(float) / cm.sum(axis=1, keepdims=True) *100 texto = [ [f"{cm[i,j]}<br>({cm_pct[i,j]:.1f} %)"for j inrange(2)]for i inrange(2) ]# Invertir filas para que TP quede arriba-derecha (convención visual habitual) z_plot = cm[::-1, :] texto_plot = texto[::-1] y_labels = etiquetas[::-1] fig = ff.create_annotated_heatmap( z=z_plot, x=etiquetas, y=y_labels, annotation_text=texto_plot, colorscale=[[0, "#f8f9fa"], [0.5, "#4a90d9"], [1, "#e74c3c"]], showscale=True, ) fig.update_layout( title=titulo, xaxis_title="Predicción del modelo", yaxis_title="Valor real", height=430, width=530, font=dict(size=13), )# Etiquetas de cuadrante etq = {(0, 0): ("TN", "gray"), (0, 1): ("FP", "#e67e22"), (1, 0): ("FN", "#e74c3c"), (1, 1): ("TP", "#27ae60")}for (ri, ci), (label, color) in etq.items(): ri_plot =1- ri fig.add_annotation( x=etiquetas[ci], y=y_labels[ri_plot], text=f"<b>{label}</b>", showarrow=False, font=dict(size=11, color=color), yshift=-22, )return fig# ── Aplicar al clasificador médico ──────────────────────────────────────────cm_lr = confusion_matrix(y_te_m, y_pred_lr)TP =int(cm_lr[1, 1]); TN =int(cm_lr[0, 0])FP =int(cm_lr[0, 1]); FN =int(cm_lr[1, 0])print("Matriz de confusión — Regresión Logística (médico):")print(cm_lr)print(f"\nTP={TP}, TN={TN}, FP={FP}, FN={FN}")fig_cm = plot_confusion_matrix( cm_lr,# titulo="Matriz de Confusión — Clasificador Médico (Reg. Logística)", etiquetas=["Sano", "Enfermo"],)fig_cm.show()
A partir de esta matriz de confusión se derivan todas las métricas de desempeño que analizaremos a lo largo del capítulo. Además, la interpretación de cada elemento de la matriz tiene implicaciones estratégicas directas en la toma de decisiones basada en el modelo.
El Costo Asimétrico del Error: Contextos Reales
La característica más importante de los errores en clasificación no es su frecuencia absoluta, sino su costo diferencial. En la mayoría de los problemas reales de minería de datos, FP y FN no tienen el mismo costo, y optimizar hacia uno implica sacrificar el otro (James et al., 2021).
Medicina: El Costo Devastador del Falso Negativo
En un sistema de detección temprana de cáncer de pulmón, un Falso Negativo significa que el sistema predice “benigno” para un nódulo que en realidad es canceroso. El paciente no recibe tratamiento. Esta asimetría implica que en medicina diagnóstica se prefieren clasificadores con alto Recall (baja tasa de FN), incluso a expensas de menor Precision. Un Falso Positivo significa que el sistema predice “maligno” para un nódulo benigno: el paciente es referido para biopsia innecesaria —procedimiento invasivo y costoso—, pero comparado con el costo del FN, el FP es el error tolerable.
Finanzas: El Costo Operativo del Falso Positivo
En un sistema de detección de fraude, un Falso Positivo bloquea una transacción legítima: el cliente no puede completar su compra, con el riesgo de perder la relación comercial. Un Falso Negativo deja pasar un fraude real, pero la pérdida suele ser cubierta por seguro o chargeback. Esta asimetría inversa implica que en detección de fraude se prefieren clasificadores con alta Precision, aceptando que algunos fraudes pasen sin detectarse (Provost & Fawcett, 2013).
Como mencionamos anteriormente, siq ueremos evaluar de manera adecuada un clasificador es necesario considerar no solo el número de errores, sino también su tipo y costo. Para esto se han desarrollado un conjunto de métricas que se derivan de la matriz de confusión y que permiten cuantificar diferentes aspectos del desempeño del modelo. Estas métricas no son mutuamente excluyentes, sino complementarias, y su interpretación debe hacerse en conjunto para obtener una visión completa del modelo. A continuación se presentan las métricas más importantes para la evaluación de clasificadores binarios, con su definición formal, cálculo detallado e interpretación contextualizada.
La limitación más crítica de la accuracy emerge cuando el dataset presenta desbalance de clases, situación extremadamente común en aplicaciones reales de minería de datos (Saito & Rehmsmeier, 2015). En nuestro dataset financiero con 97 % de transacciones legítimas, un clasificador trivial alcanza ~97 % de accuracy sin detectar ningún fraude:
Mostrar código
# ── Paradoja de la exactitud — dataset financiero ──────────────────────────y_trivial_fin = np.zeros_like(y_te_f)acc_trivial_fin = accuracy_score(y_te_f, y_trivial_fin)acc_modelo_fin = accuracy_score(y_te_f, y_pred_fin)recall_trivial = recall_score(y_te_f, y_trivial_fin, zero_division=0)recall_modelo = recall_score(y_te_f, y_pred_fin)print("Dataset financiero (97 % legítimas, 3 % fraude)")print("─"*52)print(f"Accuracy — modelo trivial : {acc_trivial_fin:.4f} ({acc_trivial_fin*100:.1f} %)")print(f"Accuracy — modelo real : {acc_modelo_fin:.4f} ({acc_modelo_fin*100:.1f} %)")print()print(f"Recall — modelo trivial : {recall_trivial:.4f} → 0 fraudes detectados")print(f"Recall — modelo real : {recall_modelo:.4f} → detecta fraudes reales")print()print("Conclusión: mayor accuracy ≠ mejor modelo en datos desbalanceados.")print("(Saito & Rehmsmeier, 2015)")
Dataset financiero (97 % legítimas, 3 % fraude)
────────────────────────────────────────────────────
Accuracy — modelo trivial : 0.9660 (96.6 %)
Accuracy — modelo real : 0.7820 (78.2 %)
Recall — modelo trivial : 0.0000 → 0 fraudes detectados
Recall — modelo real : 0.7255 → detecta fraudes reales
Conclusión: mayor accuracy ≠ mejor modelo en datos desbalanceados.
(Saito & Rehmsmeier, 2015)
Mostrar código
# ── Visualización: Accuracy vs. Recall ─────────────────────────────────────modelos = ["Modelo Real\n(LR + class_weight)", "Modelo Trivial\n(siempre negativo)"]accuracies = [acc_modelo_fin, acc_trivial_fin]recalls = [recall_modelo, recall_trivial]fig = go.Figure(data=[ go.Bar(name="Accuracy", x=modelos, y=accuracies, marker_color=["#4a90d9", "#e74c3c"], text=[f"{v:.2%}"for v in accuracies], textposition="auto"), go.Bar(name="Recall (clase fraude)", x=modelos, y=recalls, marker_color=["#27ae60", "#c0392b"], text=[f"{v:.2%}"for v in recalls], textposition="auto"),])fig.update_layout( barmode="group", title=("Paradoja de la Exactitud en el Dataset Financiero<br>""<sup>Saito & Rehmsmeier (2015): un modelo con mayor accuracy detecta 0 fraudes</sup>" ), yaxis=dict(title="Valor", range=[0, 1.1], tickformat="%"), height=420,# legend=dict(orientation="h", yanchor="bottom", y=1.02),)fig.show()
Precision: Confiabilidad de las Predicciones Positivas
Definición Formal
\[
\text{Precision} = \frac{TP}{TP + FP}
\]
La Precision mide qué proporción de las instancias predichas como positivas son genuinamente positivas (James et al., 2021).
Cálculo Detallado
Mostrar código
# ── Precision: cálculo manual y con sklearn ─────────────────────────────────precision_manual = TP / (TP + FP) if (TP + FP) >0else0precision_sk = precision_score(y_te_m, y_pred_lr, zero_division=0)print(f"Cálculo manual — Precision = {TP} / ({TP}+{FP}) = {precision_manual:.4f}")print(f"sklearn — Precision = {precision_sk:.4f}")print()print(f"Interpretación: de cada 10 alarmas de 'enfermo',")print(f" aproximadamente {precision_sk*10:.1f} corresponden a enfermos reales.")
Cálculo manual — Precision = 6 / (6+2) = 0.7500
sklearn — Precision = 0.7500
Interpretación: de cada 10 alarmas de 'enfermo',
aproximadamente 7.5 corresponden a enfermos reales.
Limitación: Alta Precision ≠ Buen Clasificador
Para entenderlo mejor, imagina que la Precisión es la “puntería” o calidad de lo que el sistema detecta.
Si un sistema es extremadamente tímido y solo lanza una alerta cuando tiene una certeza absoluta, su Precisión será perfecta (1.0): cada vez que hable, tendrá razón. Sin embargo, el costo de esa perfección es el silencio; el sistema ignorará muchísimos casos reales por miedo a equivocarse. En la práctica, un sistema que casi nunca se atreve a alertar no es útil, aunque nunca mienta.
Por el contrario, si el sistema es demasiado “sensible” y alerta por cualquier cosa, su precisión cae. Fawcett (2006) identifica que cuando la precisión es muy baja, ocurre la fatiga de alarmas: el usuario recibe tantos avisos falsos que deja de confiar en el sistema y termina ignorándolo por completo.
Ejemplo: El filtro de correos “Spam”
Alta Precisión (Certeza absoluta): Configuras tu correo para que solo marque como Spam los mensajes que digan explícitamente “ESTO ES UNA ESTAFA”. Tendrás una precisión de 1.0 (nunca se equivocará con un correo importante), pero tu bandeja de entrada principal se llenará de basura que el filtro no se atrevió a marcar.
Baja Precisión (Fatiga de alarmas): Configuras el filtro para que marque como Spam cualquier correo que tenga un signo de exclamación o una palabra en mayúsculas. El filtro atrapará todo el spam, pero también enviará a la basura facturas importantes y mensajes de tus amigos. Al final, tendrás que revisar la carpeta de Spam todos los días, anulando la utilidad del filtro.
Mostrar código
# ── Modelo "conservador": alta precision, bajo recall ──────────────────────umbral_alto =0.90y_conserv = (y_prob_lr >= umbral_alto).astype(int)p_c = precision_score(y_te_m, y_conserv, zero_division=0)r_c = recall_score(y_te_m, y_conserv, zero_division=0)f1_c = f1_score(y_te_m, y_conserv, zero_division=0)print(f"Umbral = {umbral_alto} (modelo muy conservador)")print(f" Precision : {p_c:.4f} (casi nunca se equivoca cuando alerta)")print(f" Recall : {r_c:.4f} (pero deja de detectar muchos enfermos)")print(f" F1-Score : {f1_c:.4f}")print(f" Alertas emitidas: {y_conserv.sum()} de {y_te_m.sum()} enfermos reales")
Umbral = 0.9 (modelo muy conservador)
Precision : 1.0000 (casi nunca se equivoca cuando alerta)
Recall : 0.0400 (pero deja de detectar muchos enfermos)
F1-Score : 0.0769
Alertas emitidas: 1 de 25 enfermos reales
Recall (Sensibilidad): Cobertura de los Positivos Reales
El Recall mide qué proporción de los casos verdaderamente positivos fue correctamente identificada por el clasificador (Fawcett, 2006).
Cálculo y Relación con la Tasa de Falsos Negativos
Mostrar código
# ── Recall: cálculo y relación con FNR ──────────────────────────────────────recall_manual = TP / (TP + FN) if (TP + FN) >0else0recall_sk = recall_score(y_te_m, y_pred_lr)fnr = FN / (TP + FN) # Tasa de Falsos Negativos = 1 − Recallprint(f"Cálculo manual — Recall = {TP} / ({TP}+{FN}) = {recall_manual:.4f}")print(f"sklearn — Recall = {recall_sk:.4f}")print(f"FNR (1 − Recall) = {fnr:.4f}")print()print(f"De {y_te_m.sum()} enfermos reales en el conjunto de prueba,")print(f" el modelo detecta {TP} y deja sin detectar {FN}.")
Cálculo manual — Recall = 6 / (6+19) = 0.2400
sklearn — Recall = 0.2400
FNR (1 − Recall) = 0.7600
De 25 enfermos reales en el conjunto de prueba,
el modelo detecta 6 y deja sin detectar 19.
Limitación: Alto Recall ≠ Buen Clasificador
Si la Precisión se enfocaba en la “calidad” de las alertas (no mentir), el Recall se enfoca en la “cantidad” o cobertura: qué tan buena es la red del sistema para atrapar todos los casos reales.
Un sistema con Recall = 1.0 es como una red de pesca sin agujeros: atrapa absolutamente todos los peces. Sin embargo, el problema es que, para lograrlo, esa red suele ser tan pesada o grande que termina arrastrando también basura, piedras y algas. En términos operativos, buscar un Recall perfecto suele obligar al sistema a ser menos selectivo, lo que termina dañando la Precisión.
Ejemplo: El detector de incendios
Recall Alto (Sensibilidad total): Imagina un detector de humo tan sensible que se activa incluso con el vapor de una ducha caliente o el humo de un cigarrillo a diez metros. Su Recall es de 1.0 porque es imposible que ocurra un incendio real sin que la alarma suene. El problema es que vivirás evacuando el edificio por errores (baja precisión).
Recall Bajo (Filtro exigente): Ahora imagina que el detector solo se activa cuando las llamas tocan directamente el sensor. Su precisión será alta (si suena, es que hay fuego de verdad), pero su Recall será muy bajo: para cuando el sistema avise, el incendio ya habrá consumido media habitación. El sistema fue “preciso” pero llegó demasiado tarde porque no tuvo la capacidad de detectar el problema a tiempo.
Trade-off Precision–Recall: Tabla por Umbral
Como mencionaba Fawcett (2006), ambos extremos son fallas: un sistema con Recall bajo es “ciego” ante la realidad, mientras que uno con Recall alto pero baja precisión genera la ya mencionada fatiga de alarmas. En la práctica, el científico de datos debe encontrar un equilibrio entre ambos, lo que se conoce como el trade-off Precision–Recall. Este equilibrio se controla principalmente a través del umbral de decisión: el punto de corte sobre la probabilidad predicha que determina cuándo el modelo clasifica una instancia como positiva.
La siguiente tabla es la evidencia empírica del trade-off fundamental entre Recall y Precision que toda decisión de umbral debe considerar (Provost & Fawcett, 2013):
Mostrar código
# ── Trade-off P-R según umbral — dataset médico ─────────────────────────────rows = []for u in [0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80]: y_u = (y_prob_lr >= u).astype(int) tp_u =int(((y_u==1) & (y_te_m==1)).sum()) fp_u =int(((y_u==1) & (y_te_m==0)).sum()) fn_u =int(((y_u==0) & (y_te_m==1)).sum()) tn_u =int(((y_u==0) & (y_te_m==0)).sum()) rows.append({"Umbral θ": u,"TP": tp_u, "FP": fp_u, "FN": fn_u, "TN": tn_u,"Recall": round(tp_u/(tp_u+fn_u), 3) if (tp_u+fn_u)>0else0,"Precision": round(tp_u/(tp_u+fp_u), 3) if (tp_u+fp_u)>0else0,"F1": round(f1_score(y_te_m, y_u, zero_division=0), 3), })df_tradeoff = pd.DataFrame(rows)print("Trade-off Precision–Recall según el umbral de decisión")print("Dataset Médico — Regresión Logística")print("(Provost & Fawcett, 2013)\n")print(df_tradeoff.to_string(index=False))
La Especificidad mide qué proporción de los casos verdaderamente negativos fue correctamente identificada como negativa por el clasificador (Han et al., 2011).
Cálculo y Relación con FPR
Mostrar código
# ── Specificity: cálculo y relación con la Tasa de Falsos Positivos ─────────specificity = TN / (TN + FP) if (TN + FP) >0else0fpr_val = FP / (TN + FP) # 1 − Specificity = FPRprint(f"Specificity = {TN} / ({TN}+{FP}) = {specificity:.4f}")print(f"FPR (1 − Specificity) = {fpr_val:.4f}")print()print("Tabla resumen de las tres métricas de relevancia:")print(f" Precision = {precision_score(y_te_m, y_pred_lr):.4f} "f"— fiabilidad al predecir positivo")print(f" Recall = {recall_score(y_te_m, y_pred_lr):.4f} "f"— cobertura de positivos reales")print(f" Specificity = {specificity:.4f} "f"— protección de los negativos reales")
Specificity = 273 / (273+2) = 0.9927
FPR (1 − Specificity) = 0.0073
Tabla resumen de las tres métricas de relevancia:
Precision = 0.7500 — fiabilidad al predecir positivo
Recall = 0.2400 — cobertura de positivos reales
Specificity = 0.9927 — protección de los negativos reales
Valor Predictivo Negativo (VPN): La fiabilidad del silencio
Mientras que la Precisión se enfoca en la confianza de una alerta (“¡Positivo!”), el Valor Predictivo Negativo (VPN) mide qué tanto podemos confiar en el “silencio” del sistema. Responde a la pregunta crucial: Si el modelo dice que el caso es negativo, ¿qué tan seguro es que realmente lo sea?
Definición Formal
\[
\text{NPV} = \frac{TN}{TN + FN}
\]
Un VPN alto permite que el usuario tome una decisión basada en el resultado negativo con total tranquilidad. Si el VPN es bajo, un resultado negativo no es confiable, pues existe un riesgo alto de que se trate de un positivo que el sistema no logró identificar.
Cálculo en Código
Mostrar código
# ── Valor Predictivo Negativo (VPN) ─────────vpn = TN / (TN + FN) if (TN + FN) >0else0print(f"VPN = {TN} / ({TN}+{FN}) = {vpn:.4f}")print("Indica la probabilidad de que un caso sea realmente negativo tras una predicción negativa.")
VPN = 273 / (273+19) = 0.9349
Indica la probabilidad de que un caso sea realmente negativo tras una predicción negativa.
Resumen de las “Cuatro Clásicas” de Confianza
Para que no te confundas, piensa en esto como un espejo:
Realidad del Modelo
Nombre de la Métrica
Lo que el usuario siente
El modelo dice “¡Positivo!”
Precisión (VPP)
“¿Será verdad o es una falsa alarma?”
El modelo dice “Negativo”
VPN
“¿Puedo estar tranquilo o me estaré confiando?”
El dato es Positivo
Sensibilidad (Recall)
“¿Tendrá el modelo la capacidad de verme?”
El dato es Negativo
Especificidad
“¿Sabrá el modelo dejarme en paz si no pasa nada?”
La Regla SnNout / SpPin en Medicina
James et al. (2021) introducen la regla mnemotécnica clínica:
SnNout (alta Snsensibilidad → resultado Negativo descarta la enfermedad): las pruebas de screening deben tener alta Sensibilidad para no dejar pasar enfermos.
SpPin (alta Specificidad → resultado Positivo confirma la enfermedad): las pruebas confirmatorias deben tener alta Especificidad para no confirmar falsos positivos.
F1-Score y sus Variantes Multiclase
La Necesidad de una Métrica Compuesta
El dilema entre Precision y Recall genera la necesidad de una métrica que capture ambas dimensiones en un solo número. El F1-Score resuelve esto mediante la media armónica (Han et al., 2011).
La media aritmética de Precision y Recall puede ocultar valores extremos. La media armónica penaliza asimétricamente a los clasificadores que sacrifican completamente una de las dos métricas:
Mostrar código
# ── Demostración: media aritmética vs. armónica ─────────────────────────────def f1_manual(p, r):return2* p * r / (p + r) if (p + r) >0else0def media_aritmetica(p, r):return (p + r) /2casos = [ ("Modelo A — balanceado", 0.90, 0.90), ("Modelo B — solo Precision", 0.99, 0.01), ("Modelo C — solo Recall", 0.01, 0.99), ("Modelo D — umbral alto moderado", 0.85, 0.60),]print(f"{'Modelo':<35}{'P':>5}{'R':>5}{'Aritmética':>11}{'F1 (armónica)':>14}")print("─"*78)for nombre, p, r in casos:print(f"{nombre:<35}{p:>5.2f}{r:>5.2f} "f"{media_aritmetica(p,r):>11.4f}{f1_manual(p,r):>14.4f}")print()print("La media armónica domina hacia el valor más bajo: castiga los extremos.")print("(Han et al., 2011)")
Modelo P R Aritmética F1 (armónica)
──────────────────────────────────────────────────────────────────────────────
Modelo A — balanceado 0.90 0.90 0.9000 0.9000
Modelo B — solo Precision 0.99 0.01 0.5000 0.0198
Modelo C — solo Recall 0.01 0.99 0.5000 0.0198
Modelo D — umbral alto moderado 0.85 0.60 0.7250 0.7034
La media armónica domina hacia el valor más bajo: castiga los extremos.
(Han et al., 2011)
La elección de \(\beta\) es una declaración explícita sobre la preferencia relativa entre minimizar FP o FN (James et al., 2021):
Mostrar código
# ── F-beta: el parámetro β como declaración de prioridad ───────────────────p_lr = precision_score(y_te_m, y_pred_lr, zero_division=0)r_lr = recall_score(y_te_m, y_pred_lr)print(f"Modelo LR médico — P = {p_lr:.3f}, R = {r_lr:.3f}\n")print(f"{'β':>5}{'Énfasis':<40}{'F-beta':>8}")print("─"*58)for beta, desc in [ (0.5, "Precision pesa 2× más (FP es más costoso)"), (1.0, "Precision = Recall (F1 estándar)"), (2.0, "Recall pesa 2× más (FN es más costoso)"), (5.0, "Recall domina (contexto crítico: FN catastrófico)"),]: fb = fbeta_score(y_te_m, y_pred_lr, beta=beta, zero_division=0)print(f"{beta:>5}{desc:<40}{fb:>8.4f}")
Modelo LR médico — P = 0.750, R = 0.240
β Énfasis F-beta
──────────────────────────────────────────────────────────
0.5 Precision pesa 2× más (FP es más costoso) 0.5263
1.0 Precision = Recall (F1 estándar) 0.3636
2.0 Recall pesa 2× más (FN es más costoso) 0.2778
5.0 Recall domina (contexto crítico: FN catastrófico) 0.2464
# ── F1 macro / micro / weighted / binario ───────────────────────────────────print("Variantes del F1-Score:")for avg in ["macro", "micro", "weighted", "binary"]: val = f1_score(y_te_m, y_pred_lr, average=avg, zero_division=0)print(f" F1 {avg:<10}: {val:.4f}")print()print("Reporte completo por clase:")print(classification_report( y_te_m, y_pred_lr, target_names=["Sano (0)", "Enfermo (1)"],))
Variantes del F1-Score:
F1 macro : 0.6633
F1 micro : 0.9300
F1 weighted : 0.9130
F1 binary : 0.3636
Reporte completo por clase:
precision recall f1-score support
Sano (0) 0.93 0.99 0.96 275
Enfermo (1) 0.75 0.24 0.36 25
accuracy 0.93 300
macro avg 0.84 0.62 0.66 300
weighted avg 0.92 0.93 0.91 300
Visualización Comparativa de Variantes
Mostrar código
# ── Comparación de variantes del F1 ─────────────────────────────────────────variantes = ["macro", "micro", "weighted", "binary\n(clase positiva)"]valores_f1 = [ f1_score(y_te_m, y_pred_lr, average="macro"), f1_score(y_te_m, y_pred_lr, average="micro"), f1_score(y_te_m, y_pred_lr, average="weighted"), f1_score(y_te_m, y_pred_lr, average="binary"),]descripciones = ["Todas las clases\npeso igual","Todas las instancias\npeso igual","Ponderado por\nfrecuencia de clase","Solo clase\npositiva (enfermos)",]fig = go.Figure(go.Bar( x=variantes, y=valores_f1, marker_color=["#4a90d9", "#27ae60", "#e67e22", "#e74c3c"], text=[f"{v:.3f}<br><i>{d}</i>"for v, d inzip(valores_f1, descripciones)], textposition="auto",))fig.update_layout( title=("Variantes del F1-Score — ¿Qué mide cada una?<br>""<sup>Han et al. (2011); Pedregosa et al. (2011)</sup>" ), yaxis=dict(title="F1", range=[0, 1.1]), height=420, xaxis_title="Tipo de promedio",)fig.show()
Curva ROC y el Área Bajo la Curva (AUC)
Motivación
Muchas métricas de clasificación binaria dependen del umbral de decisión\(\theta\). Por ejemplo, un mismo modelo puede tener alta sensibilidad con un umbral bajo, pero también producir más falsos positivos. La curva ROC (Receiver Operating Characteristic) permite analizar este intercambio sin fijar un único umbral, evaluando el comportamiento del clasificador a lo largo de todos los umbrales posibles (Fawcett, 2006).
Definición formal
Sea \(\hat{p}(x)\) el score o probabilidad estimada de que la observación \(x\) pertenezca a la clase positiva. Para cada umbral \(\theta\), el modelo predice:
obtenidos al variar \(\theta\) desde los valores más estrictos hasta los más flexibles.
Interpretación de sus ejes
1. Tasa de Verdaderos Positivos (TPR)
La TPR coincide con el Recall o la Sensibilidad. Indica qué proporción de los casos realmente positivos es identificada correctamente por el modelo.
Nombre común: Sensibilidad o Exhaustividad.
En lenguaje sencillo: Si un caso realmente es positivo, ¿qué tan probable es que el modelo lo detecte?
2. Tasa de Falsos Positivos (FPR)
La FPR es el complemento de la Especificidad. Indica qué proporción de los casos realmente negativos es clasificada incorrectamente como positiva.
Nombre común: Tasa de falsos positivos o fall-out.
En lenguaje sencillo: Si un caso realmente es negativo, ¿qué tan probable es que el modelo genere una falsa alarma?
¿Qué representa la curva ROC?
La curva ROC muestra el compromiso entre:
detectar más positivos (aumentar TPR),
evitar falsas alarmas (mantener baja FPR).
Un clasificador deseable es aquel cuya curva se acerca a la esquina superior izquierda del plano ROC, pues allí se combina una alta sensibilidad con una baja tasa de falsos positivos.
Como referencia:
la diagonal \(\text{TPR} = \text{FPR}\) representa un clasificador sin capacidad discriminativa, equivalente a adivinar al azar;
cuanto más por encima de esa diagonal se encuentre la curva, mejor será la capacidad del modelo para separar ambas clases.
Área Bajo la Curva (AUC)
El AUC (Area Under the Curve) resume la curva ROC en un solo número:
En términos prácticos, el AUC mide la capacidad global del modelo para ordenar correctamente a los positivos por encima de los negativos, sin depender de un umbral específico.
donde \(x^+\) es una instancia positiva elegida al azar y \(x^-\) una negativa, también elegida al azar.
Esto significa que el AUC puede entenderse como la probabilidad de que el modelo asigne un score mayor a un caso positivo que a uno negativo. Por ejemplo, un valor de \(\text{AUC} = 0.85\) indica que, en promedio, existe un 85 % de probabilidad de que el modelo puntúe más alto a un positivo que a un negativo seleccionados aleatoriamente.
Ventajas de la curva ROC y del AUC
No dependen de un único umbral de decisión.
Permiten comparar clasificadores de forma global.
Son especialmente útiles cuando interesa evaluar la capacidad de discriminación del modelo más allá de una configuración específica.
Limitación importante
Aunque la curva ROC y el AUC son muy útiles, no siempre reflejan adecuadamente el desempeño en problemas con clases muy desbalanceadas. En esos casos, puede ser más informativa la curva Precision–Recall, porque penaliza de forma más directa los falsos positivos en contextos donde la clase positiva es rara.
Saito y Rehmsmeier (2015) demostraron que en datasets con desbalance severo, el AUC-ROC puede sobreestimar el desempeño del clasificador porque incluye los TN en el denominador de la FPR: cuando los negativos son muy numerosos, incluso muchos FP producen una FPR aparentemente pequeña, haciendo que la curva parezca optimista.
Curva Precision–Recall y Average Precision
Motivación
En muchos problemas de clasificación, especialmente cuando la clase positiva es poco frecuente, no basta con saber cuántos aciertos globales tiene el modelo. En estos contextos suele ser más importante responder dos preguntas:
cuando el modelo predice un positivo, ¿qué tan confiable es esa predicción?;
de todos los positivos reales, ¿cuántos está logrando recuperar?
La curva Precision–Recall (PR) se construye precisamente para estudiar este compromiso. A diferencia de la curva ROC, aquí los verdaderos negativos (TN) no intervienen de forma explícita. Por ello, esta curva se enfoca en el desempeño del modelo sobre la clase positiva, lo cual resulta particularmente útil en escenarios con clases desbalanceadas (Saito & Rehmsmeier, 2015).
Definición formal
Sea \(\hat{p}(x)\) el score o probabilidad estimada para la clase positiva, y sea \(\theta\) un umbral de decisión. Para cada valor de \(\theta\), se definen:
La Precision evalúa la pureza de las predicciones positivas. Es alta cuando, entre los casos señalados como positivos por el modelo, predominan los verdaderos positivos.
Nombre común: Precisión.
En lenguaje sencillo: Si el modelo dice “positivo”, ¿qué tan probable es que tenga razón?
2. Recall
El Recall o Sensibilidad mide la capacidad del modelo para no dejar escapar positivos reales.
Nombre común: Sensibilidad o Exhaustividad.
En lenguaje sencillo: De todos los positivos reales, ¿cuántos logra detectar el modelo?
¿Qué representa la curva Precision–Recall?
La curva PR muestra el compromiso entre:
recuperar más positivos (aumentar Recall),
mantener confiables las alertas positivas (conservar alta Precision).
En general, al hacer el umbral más flexible, el modelo detecta más positivos y el Recall aumenta, pero también tiende a cometer más falsos positivos, por lo que la Precision puede disminuir. La curva permite visualizar este intercambio de manera completa.
Un modelo deseable es aquel cuya curva se mantiene lo más arriba posible, pues eso indica que conserva una alta precisión incluso cuando incrementa su cobertura de positivos.
Baseline de la curva PR
A diferencia de la curva ROC, cuya referencia natural es la diagonal aleatoria, en la curva PR la línea de referencia depende de la prevalencia de la clase positiva en el conjunto de datos:
Esto significa que, si la clase positiva representa por ejemplo el 10 % de los datos, entonces un clasificador sin capacidad real de discriminación tenderá a tener una Precision cercana a 0.10.
Por ello, en problemas desbalanceados, una curva PR y un valor de AP por encima de la prevalencia indican que el modelo está haciendo algo mejor que una asignación aleatoria proporcional.
Average Precision (AP)
El Average Precision (AP) resume la curva Precision–Recall en un solo número. Puede entenderse como un promedio ponderado de la precisión a lo largo de distintos niveles de recall:
\[
\text{AP} = \sum_k (R_k - R_{k-1})\, P_k
\]
donde \(R_k\) representa el Recall en el punto \(k\) y \(P_k\) la Precision correspondiente.
En términos prácticos, el AP asigna mayor valor a los modelos que no solo recuperan positivos, sino que lo hacen manteniendo una buena precisión a medida que aumenta la cobertura.
Interpretación del AP
Un valor de AP cercano a 1 indica que el modelo logra identificar positivos con alta precisión a lo largo de casi todo el rango de recall.
Un valor de AP cercano a la prevalencia sugiere un comportamiento cercano al azar bajo esta métrica.
Cuanto mayor sea el AP respecto a la prevalencia, mejor será la capacidad del modelo para priorizar correctamente la clase positiva.
¿Por qué es útil en clases desbalanceadas?
Cuando la clase positiva es rara, puede ocurrir que un modelo tenga una curva ROC aparentemente buena y, sin embargo, produzca demasiados falsos positivos para que sus predicciones sean útiles en la práctica. La curva PR evita que los TN dominen la evaluación y se concentra en lo que muchas veces realmente importa: la capacidad de encontrar positivos sin generar demasiadas falsas alarmas.
Por esta razón, en aplicaciones médicas, detección de fraude, mantenimiento predictivo o búsqueda de eventos raros, la curva PR y el AP suelen ser más informativos que la curva ROC.
Mostrar código
# ── Curva Precision-Recall: LR vs. RF ──────────────────────────────────────prec_lr_c, rec_lr_c, _ = precision_recall_curve(y_te_m, y_prob_lr)prec_rf_c, rec_rf_c, _ = precision_recall_curve(y_te_m, y_prob_rf)ap_lr = average_precision_score(y_te_m, y_prob_lr)ap_rf = average_precision_score(y_te_m, y_prob_rf)prevalencia = y_te_m.mean()print(f"Average Precision — Reg. Logística: {ap_lr:.4f}")print(f"Average Precision — Random Forest : {ap_rf:.4f}")print(f"Baseline (prevalencia clase +) : {prevalencia:.4f}")print()print("Un AP mayor que la prevalencia indica que el modelo supera")print("una predicción aleatoria proporcional. (Saito & Rehmsmeier, 2015)")
Average Precision — Reg. Logística: 0.4637
Average Precision — Random Forest : 0.7544
Baseline (prevalencia clase +) : 0.0833
Un AP mayor que la prevalencia indica que el modelo supera
una predicción aleatoria proporcional. (Saito & Rehmsmeier, 2015)
La mayoría de los clasificadores probabilísticos producen \(\hat{p}(x) \in [0, 1]\) antes de asignar una clase. El umbral por defecto \(\theta = 0.5\) es una convención, no una elección óptima (Provost & Fawcett, 2013). Si se conocen los costos de cada tipo de error, el umbral óptimo puede derivarse analíticamente (James et al., 2021):
Finanzas con \(C_{FP} = 10 \cdot C_{FN}\): \(\theta^* = 10/11 \approx 0.91\) (se requiere certeza alta antes de bloquear)
Efecto del Umbral sobre Todas las Métricas
Mostrar código
# ── Efecto del umbral sobre accuracy, precision, recall y F1 ───────────────umbrales_demo = np.arange(0.05, 0.95, 0.02)metricas_umbral = []for u in umbrales_demo: y_u = (y_prob_lr >= u).astype(int) tp_u =int(((y_u==1) & (y_te_m==1)).sum()) tn_u =int(((y_u==0) & (y_te_m==0)).sum()) fp_u =int(((y_u==1) & (y_te_m==0)).sum()) fn_u =int(((y_u==0) & (y_te_m==1)).sum()) n = tp_u + tn_u + fp_u + fn_u metricas_umbral.append({"umbral": u,"accuracy": (tp_u + tn_u) / n if n >0else0,"precision": tp_u / (tp_u + fp_u) if (tp_u + fp_u) >0else0,"recall": tp_u / (tp_u + fn_u) if (tp_u + fn_u) >0else0,"f1": 2*tp_u / (2*tp_u+fp_u+fn_u) if (2*tp_u+fp_u+fn_u)>0else0, })df_umb = pd.DataFrame(metricas_umbral)fig = go.Figure()for col, color, name in [ ("accuracy", "#4a90d9", "Accuracy"), ("precision", "#27ae60", "Precision"), ("recall", "#e74c3c", "Recall"), ("f1", "#e67e22", "F1-Score"),]: fig.add_trace(go.Scatter( x=df_umb["umbral"], y=df_umb[col], name=name, line=dict(color=color, width=2.2), mode="lines", ))idx_best = df_umb["f1"].idxmax()best_u = df_umb.loc[idx_best, "umbral"]fig.add_vline(x=0.5, line_dash="dash", line_color="gray", annotation_text="θ=0.5 (default)")fig.add_vline(x=best_u, line_dash="dot", line_color="#e67e22", annotation_text=f"θ*_F1 = {best_u:.2f}")fig.update_layout( title=("Efecto del Umbral de Decisión sobre las Métricas<br>""<sup>Provost & Fawcett (2013)</sup>" ), xaxis_title="Umbral de Decisión (θ)", yaxis_title="Valor", yaxis=dict(range=[0, 1.05]), height=430, legend=dict(orientation="h", yanchor="bottom", y=1.02),)fig.show()
Búsqueda del Umbral Óptimo según el Criterio
Mostrar código
# ── Tres estrategias de selección del umbral ───────────────────────────────print("Estrategia 1: Maximizar F1-Score")f1_arr = [f1_score(y_te_m, (y_prob_lr >= u).astype(int), zero_division=0)for u in np.arange(0.01, 0.99, 0.01)]u_best_f1 = np.arange(0.01, 0.99, 0.01)[np.argmax(f1_arr)]y_best_f1 = (y_prob_lr >= u_best_f1).astype(int)print(f" θ* = {u_best_f1:.2f} → F1={max(f1_arr):.4f}, "f"P={precision_score(y_te_m, y_best_f1, zero_division=0):.4f}, "f"R={recall_score(y_te_m, y_best_f1):.4f}\n")print("Estrategia 2 (médica): mayor θ con Recall ≥ 0.90")best_med_u =Nonefor u in np.arange(0.01, 0.99, 0.01):if recall_score(y_te_m, (y_prob_lr >= u).astype(int), zero_division=0) >=0.90: best_med_u = uif best_med_u isnotNone: y_med_u = (y_prob_lr >= best_med_u).astype(int)print(f" θ* = {best_med_u:.2f} → "f"P={precision_score(y_te_m, y_med_u, zero_division=0):.4f}, "f"R={recall_score(y_te_m, y_med_u):.4f}\n")print("Estrategia 3: Punto de Youden (max TPR − FPR)")J = tpr_lr - fpr_lridx_yo = np.argmax(J)u_youden = thresholds_roc[idx_yo]y_yo = (y_prob_lr >= u_youden).astype(int)print(f" θ* = {u_youden:.2f} → J={J[idx_yo]:.4f}, "f"P={precision_score(y_te_m, y_yo, zero_division=0):.4f}, "f"R={recall_score(y_te_m, y_yo):.4f}")print(" (James et al., 2021)")
Estrategia 1: Maximizar F1-Score
θ* = 0.34 → F1=0.5000, P=0.6667, R=0.4000
Estrategia 2 (médica): mayor θ con Recall ≥ 0.90
θ* = 0.01 → P=0.0924, R=0.9200
Estrategia 3: Punto de Youden (max TPR − FPR)
θ* = 0.27 → J=0.3927, P=0.4583, R=0.4400
(James et al., 2021)
Comparación Final de Modelos
Esta sección demuestra que un modelo con menor accuracy puede ser estratégicamente superior cuando el contexto así lo demanda (Provost & Fawcett, 2013). Se comparan tres configuraciones del clasificador médico:
Mostrar código
# ── Tres configuraciones del clasificador médico ───────────────────────────y_base = y_pred_lr # LR θ=0.50y_medico = (y_prob_lr >=0.25).astype(int) # LR θ=0.25 (médico)y_rf_b = y_pred_rf # RF θ=0.50modelos_cmp = {"LR θ=0.50\n(base)": y_base,"LR θ=0.25\n(médico)": y_medico,"RF θ=0.50\n(base)": y_rf_b,}resultados = []for nombre, y_c in modelos_cmp.items(): resultados.append({"Modelo": nombre,"Accuracy": round(accuracy_score(y_te_m, y_c), 4),"Precision": round(precision_score(y_te_m, y_c, zero_division=0), 4),"Recall": round(recall_score(y_te_m, y_c, zero_division=0), 4),"F1": round(f1_score(y_te_m, y_c, zero_division=0), 4), })df_cmp = pd.DataFrame(resultados)print("Comparación de modelos — Dataset Médico")print("(Provost & Fawcett, 2013)\n")print(df_cmp.to_string(index=False))print()print("⚠ El modelo 'LR θ=0.25' tiene menor Accuracy que el base,")print(" pero detecta más enfermos reales (mayor Recall).")print(" Para un médico, ese es el modelo correcto.")
Comparación de modelos — Dataset Médico
(Provost & Fawcett, 2013)
Modelo Accuracy Precision Recall F1
LR θ=0.50\n(base) 0.9300 0.7500 0.24 0.3636
LR θ=0.25\n(médico) 0.8967 0.3929 0.44 0.4151
RF θ=0.50\n(base) 0.9500 1.0000 0.40 0.5714
⚠ El modelo 'LR θ=0.25' tiene menor Accuracy que el base,
pero detecta más enfermos reales (mayor Recall).
Para un médico, ese es el modelo correcto.
Mostrar código
# ── Visualización comparativa ────────────────────────────────────────────────metricas_plot = ["Accuracy", "Precision", "Recall", "F1"]colores_plot = ["#4a90d9", "#27ae60", "#e74c3c", "#e67e22"]fig = go.Figure()for metrica, color inzip(metricas_plot, colores_plot): fig.add_trace(go.Bar( name=metrica, x=df_cmp["Modelo"], y=df_cmp[metrica], marker_color=color, text=[f"{v:.3f}"for v in df_cmp[metrica]], textposition="auto", ))fig.update_layout( barmode="group", title=("Comparación de Modelos: ¿Más Accuracy = Mejor Modelo?<br>""<sup>La métrica correcta depende del costo del error — Provost & Fawcett (2013)</sup>" ), yaxis=dict(title="Valor", range=[0, 1.1]), height=450, legend=dict(orientation="h", yanchor="bottom", y=1.02),)fig.show()
Tabla-Resumen: ¿Qué Métrica Usar y Cuándo?
Situación
Métrica recomendada
Referencia clave
Dataset balanceado
Accuracy, F1 macro
Han et al. (2011)
Clase positiva rara (< 10 %)
F1 clase positiva, PR-AUC
Saito & Rehmsmeier (2015)
FN muy costoso (medicina, seguridad)
Recall / Sensibilidad
James et al. (2021)
FP muy costoso (spam, fraude leve)
Precision
Fawcett (2006)
FP y FN con costo similar
F1-Score
Han et al. (2011)
Comparar modelos sin fijar umbral
ROC-AUC
Fawcett (2006); Bamber (1975)
Clase escasa + interés en ranking
Precision-Recall AUC
Saito & Rehmsmeier (2015)
Multiclase, todas las clases importan igual
F1 macro
Pedregosa et al. (2011)
Errores Comunes en la Interpretación de Métricas
Reportar Solo Accuracy en Datos Desbalanceados
El error más frecuente y más dañino (Saito & Rehmsmeier, 2015). La paradoja de la exactitud demostrada en la Sección 4.3 hace que la accuracy sea engañosamente alta en datos desbalanceados. Regla práctica: siempre reportar Precision, Recall y F1 cuando la proporción entre clases supera 80/20.
Confundir Precision con Recall
Precision mide la fiabilidad de las predicciones positivas; Recall mide la cobertura de los positivos reales. Son métricas ortogonales y complementarias, no intercambiables. Confundirlas lleva a interpretar que un modelo con alta Precision “no falla con los enfermos” cuando en realidad puede estar dejando sin detectar a la mayoría (Fawcett, 2006).
Asumir que Mayor AUC = Mejor Modelo en Producción
Mostrar código
# ── AUC alto pero F1 bajo en el umbral operativo ───────────────────────────# Modelo bien rankeado pero con probabilidades mal calibradasnp.random.seed(99)y_fake_prob = np.where( y_te_m ==1, np.random.beta(3, 1, size=y_te_m.shape), np.random.beta(1, 2, size=y_te_m.shape),)y_fake_pred = (y_fake_prob >=0.50).astype(int)fpr_fk, tpr_fk, _ = roc_curve(y_te_m, y_fake_prob)auc_fk = auc(fpr_fk, tpr_fk)print("Modelo con AUC alto pero mal calibrado (F1 bajo al umbral default):")print(f" AUC : {auc_fk:.4f}")print(f" F1 (θ = 0.50) : {f1_score(y_te_m, y_fake_pred, zero_division=0):.4f}")print(f" Recall : {recall_score(y_te_m, y_fake_pred, zero_division=0):.4f}")print()print("Conclusión: el AUC mide capacidad de ranking, no desempeño operativo.")print("Validar siempre las métricas en el umbral de producción.")print("(Fawcett, 2006)")
Modelo con AUC alto pero mal calibrado (F1 bajo al umbral default):
AUC : 0.9340
F1 (θ = 0.50) : 0.3962
Recall : 0.8400
Conclusión: el AUC mide capacidad de ranking, no desempeño operativo.
Validar siempre las métricas en el umbral de producción.
(Fawcett, 2006)
Optimizar el Umbral en el Conjunto de Prueba (Data Leakage)
Ajustar el umbral sobre los mismos datos con los que se evalúa constituye data leakage y produce estimaciones optimistas del desempeño real (James et al., 2021). El umbral debe seleccionarse en el conjunto de validación y evaluarse únicamente en el conjunto de prueba.
Usar F1 Macro Ocultando el Desempeño en la Clase Minoritaria
Mostrar código
# ── F1 macro oculta mal desempeño en clase positiva ─────────────────────────y_casi_trivial = np.zeros_like(y_te_m)idx_top2 = np.argsort(y_prob_lr)[-2:]y_casi_trivial[idx_top2] =1f1_macro_mal = f1_score(y_te_m, y_casi_trivial, average="macro")f1_pos_mal = f1_score(y_te_m, y_casi_trivial, average="binary")print("Modelo que casi nunca detecta enfermos (solo 2 predicciones positivas):")print(f" F1 macro = {f1_macro_mal:.4f} ← parece 'aceptable'")print(f" F1 clase + = {f1_pos_mal:.4f} ← revela el verdadero problema")print()print("Siempre revisar el F1 por clase, no solo el promedio.")print("(Han et al., 2011)")
Modelo que casi nunca detecta enfermos (solo 2 predicciones positivas):
F1 macro = 0.5540 ← parece 'aceptable'
F1 clase + = 0.1481 ← revela el verdadero problema
Siempre revisar el F1 por clase, no solo el promedio.
(Han et al., 2011)
Guía de Selección de Métricas
La selección de la métrica adecuada debe seguir un proceso de análisis del contexto antes de calcular cualquier número (Provost & Fawcett, 2013):
Paso 1 — Caracterizar el desbalance: Si la proporción entre clases supera 80/20, descartar la accuracy como métrica principal (Saito & Rehmsmeier, 2015).
Paso 2 — Identificar el costo diferencial del error: FN catastróficos → Recall/Sensibilidad. FP muy costosos → Precision. Ambos similares → F1-Score (James et al., 2021).
Paso 3 — Determinar si se comparan modelos o se elige un umbral: Comparación global → ROC-AUC o PR-AUC. Configuración en producción → métricas en el umbral operativo (Fawcett, 2006).
Paso 4 — Naturaleza multiclase: Todas las clases con igual importancia de negocio → F1 Macro. Volumen total de errores → F1 Micro. Ponderación por prevalencia → F1 Weighted (Han et al., 2011; Pedregosa et al., 2011).
Paso 5 — Traducción a impacto de negocio: Las métricas estadísticas deben conectar con decisiones reales: ¿cuántos pacientes se salvan?, ¿cuánto fraude se evita?, ¿cuántos clientes se retienen? (Provost & Fawcett, 2013).
Conclusiones
La evaluación de clasificadores en Minería de Datos no es un paso mecánico al final del proceso KDD (Fayyad et al., 1996): es una actividad de análisis estratégico que conecta la calidad estadística del modelo con la utilidad práctica del conocimiento descubierto.
Las métricas presentadas en este capítulo forman un sistema complementario (Han et al., 2011). La accuracy provee contexto general pero falla en datos desbalanceados (Saito & Rehmsmeier, 2015). Precision y Recall cuantifican el balance entre los dos tipos de error con implicaciones radicalmente diferentes según el costo asimétrico del dominio (Provost & Fawcett, 2013). El F1-Score sintetiza ese balance mediante la media armónica, penalizando asimétricamente los extremos (James et al., 2021). La curva ROC y el AUC, con su interpretación probabilística directa (Bamber, 1975; Fawcett, 2006), caracterizan la capacidad discriminativa independiente del umbral, pero deben complementarse con la curva Precision-Recall cuando la clase positiva es minoritaria (Saito & Rehmsmeier, 2015). El umbral de decisión, finalmente, es una palanca estratégica derivable del costo diferencial del error (Provost & Fawcett, 2013).
La competencia central del científico de datos en este dominio no es memorizar fórmulas, sino comprender qué declara cada métrica sobre el modelo y el problema, y elegir la que mejor alinea la optimización estadística con el objetivo de negocio.
Referencias
Bamber, D. (1975). The area above the ordinal dominance graph and the area below the receiver operating characteristic graph. Journal of Mathematical Psychology, 12(4), 387–415. https://doi.org/10.1016/0022-2496(75)90001-2
Fawcett, T. (2006). An introduction to ROC analysis. Pattern Recognition Letters, 27(8), 861–874. https://doi.org/10.1016/j.patrec.2005.10.010
Fayyad, U., Piatetsky-Shapiro, G., & Smyth, P. (1996). From data mining to knowledge discovery in databases. AI Magazine, 17(3), 37–54. https://doi.org/10.1609/aimag.v17i3.1230
Han, J., Kamber, M., & Pei, J. (2011). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. https://doi.org/10.1016/B978-0-12-381479-1.00001-0
James, G., Witten, D., Hastie, T., & Tibshirani, R. (2021). An Introduction to Statistical Learning with Applications in R (2nd ed.). Springer. https://doi.org/10.1007/978-1-0716-1418-1
Pedregosa, F., Varoquaux, G., Gramfort, A., Michel, V., Thirion, B., Grisel, O., Blondel, M., Prettenhofer, P., Weiss, R., Dubourg, V., Vanderplas, J., Passos, A., Cournapeau, D., Brucher, M., Perrot, M., & Duchesnay, É. (2011). Scikit-learn: Machine learning in Python. Journal of Machine Learning Research, 12, 2825–2830.
Provost, F., & Fawcett, T. (2013). Data Science for Business: What You Need to Know about Data Mining and Data-Analytic Thinking. O’Reilly Media.
Saito, T., & Rehmsmeier, M. (2015). The precision-recall plot is more informative than the ROC plot when evaluating binary classifiers on imbalanced datasets. PLOS ONE, 10(3), e0118432. https://doi.org/10.1371/journal.pone.0118432
Fin del Capítulo 7 — Métricas de Clasificación en Minería de Datos
Provost, Foster, y Tom Fawcett. 2013. Data Science for Business: What You Need to Know about Data Mining and Data-Analytic Thinking. Sebastopol, CA: O’Reilly Media.