
Proyecto Taussig — Sesión 3: la matriz de confusión, ahora con cinco clases
En la Sesión 2 entrenamos el árbol y le pedimos, por primera vez en toda la familia de proyectos, que eligiera entre cinco clases en vez de dos. Sacamos una exactitud. Hoy, igual que hicimos en CUTLER y en Javierre, abrimos la caja de las predicciones — pero esta vez la caja tiene bastantes más compartimentos.
Por qué un solo número ya no basta (y aquí, menos que nunca)
En CUTLER, la matriz de confusión tenía 4 casillas. En Javierre, también 4. Hoy, con cinco clases (N, S, V, F, Q), la matriz tiene 25 casillas: 5 en la diagonal, que son los aciertos, y 20 fuera de la diagonal, que son 20 formas distintas de equivocarse.
Y hay otra cosa que ya adelantamos en la Sesión 1: las clases no están ni de lejos repartidas por igual. La inmensa mayoría de los 109.446 latidos son normales (N); las arritmias ventriculares (V) — las que de verdad nos importan, las que conectan con Antonio Puerta y Hank Gathers — son una minoría dentro del conjunto. Eso significa que un modelo bastante torpe podría conseguir una exactitud que suena muy bien simplemente acertando casi siempre con N y fallando en silencio con V, S, F y Q. La exactitud no distingue esos dos escenarios. La matriz de confusión, sí.
Dibujando la matriz de confusión
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
etiquetas = ["N", "S", "V", "F", "Q"]
# 1. Obtenemos las predicciones del árbol sobre los latidos de prueba
predicciones = arbol.predict(X_prueba)
# 2. Calculamos y dibujamos la matriz de confusión, 5x5
cm = confusion_matrix(y_prueba, predicciones, labels=[0, 1, 2, 3, 4])
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=etiquetas)
disp.plot(cmap="Blues")
plt.title("Matriz de confusión — clasificación de latidos (5 clases)")
plt.show()
Cómo leer una matriz más grande
El principio es el mismo que en CUTLER y Javierre, solo que con más filas y columnas. Practicamos con un ejemplo pequeño e inventado, de solo 20 latidos, para no perdernos entre tantos números:
Predicho N Predicho S Predicho V Predicho F Predicho Q
Real N 13 1 0 0 0
Real S 1 2 0 0 0
Real V 0 0 2 1 0
Real F 0 0 0 0 0
Real Q 0 0 0 0 0
Para leerla, el procedimiento no cambia: la diagonal (13 + 2 + 2 = 17) son los aciertos. Fuera de la diagonal están los errores, y aquí es donde hay que fijarse bien — no todas las celdas fuera de la diagonal pesan igual. En este ejemplo, la fila “Real V” tiene 2 aciertos y 1 fallo (un latido ventricular que el árbol confundió con uno de fusión, F). Ese es, con diferencia, el error que más nos debería preocupar de toda la tabla: un latido ventricular real que el árbol no reconoce como tal.
Cuando tengáis vuestra matriz real, buscad la fila “Real V” y mirad cuánto se sale de la diagonal — es la pregunta que motivó este proyecto desde la Sesión 1.

El informe completo, y un concepto nuevo: promediar cinco clases distintas
Con dos clases, classification_report nos daba dos filas y ya está. Con cinco, nos da cinco — y encima, al final, un resumen que promedia esas cinco filas en un solo número. Ahí aparece hoy un concepto nuevo:
from sklearn.metrics import classification_report
print(classification_report(y_prueba, predicciones, target_names=etiquetas))
Al final de la tabla vais a ver dos filas de promedio con nombres distintos: macro avg y weighted avg. No son lo mismo, y la diferencia importa mucho en un caso como el nuestro:
- macro avg calcula la media de las cinco clases tratándolas exactamente igual — como si N (con decenas de miles de latidos) pesara lo mismo que Q (con apenas unos pocos). Si a un modelo se le da fatal reconocer una clase minoritaria como V, esa mala nota se nota mucho en el macro avg.
- weighted avg calcula la media dando más peso a las clases con más latidos. Como N es aplastantemente mayoritaria, el weighted avg se parece mucho a “cómo de bien le va al modelo con los latidos normales” — y puede salir muy alto aunque el modelo sea bastante malo detectando V.
Esto no es un detalle técnico sin más: es la misma trampa de la que hablábamos al principio, ahora con nombre y apellido. Un weighted avg alto puede estar disfrazando una exhaustividad muy baja en la clase V, precisamente la que más nos importa.
[COMPLETAR CON VUESTROS DATOS REALES: exhaustividad de V = ___ | macro avg = ___ | weighted avg = ___ → ¿se parece el weighted avg al macro avg, o hay una diferencia grande entre los dos?]

Próximo paso
En la próxima sesión entrenaremos muchos árboles a la vez y los dejaremos votar: el Random Forest, el mismo algoritmo que mejoró los resultados en CUTLER y en Javierre. Comprobaremos si, además de subir la exactitud general, también ayuda con el problema que hemos visto hoy — reconocer mejor las clases minoritarias, y en especial la V.




