
Proyecto Laennec — Sesión 1: por qué este proyecto, de dónde vienen los datos, y el médico que inventó una forma nueva de escuchar
Empezamos un proyecto nuevo, distinto de todos los anteriores. Hasta ahora hemos trabajado con datos en tablas (genes, mediciones celulares, un electrocardiograma convertido en números) y con imágenes. Hoy cambiamos de sentido por completo: vamos a trabajar con sonido real, grabado con un estetoscopio.
Un motivo real, no un ejercicio de clase
Según la Organización Mundial de la Salud, las enfermedades cardiovasculares son la principal causa de muerte en el mundo: se estima que causaron 19,8 millones de muertes en 2022. Y la herramienta más antigua, más barata y más usada del planeta para detectar sus primeras señales sigue siendo la misma desde hace más de doscientos años: un médico con un estetoscopio, escuchando.
Escuchar bien un corazón no es fácil. Un latido normal suena “lub-dub, lub-dub”, con un ritmo constante. Pero un soplo (murmullo, o murmur) añade un sonido de turbulencia entre esos dos golpes — puede ser una señal de que una válvula del corazón no cierra bien. Y una extrasístole es un latido que se sale del ritmo, que en muchos casos es completamente normal (le pasa a mucha gente, sobre todo a niños) pero que, si se repite mucho, puede ser señal de un problema real. Distinguir estos tres sonidos a oído requiere años de práctica clínica, e incluso médicos experimentados pueden no ponerse de acuerdo sobre lo que están oyendo. La pregunta de este proyecto es: ¿puede un modelo entrenado con sonidos reales aprender a distinguirlos?
Por qué se llama Proyecto Laennec
Le ponemos nombre a este proyecto en honor a René Laennec (1781-1826), el médico francés que inventó el propio estetoscopio — el instrumento que ha hecho posible, durante dos siglos, todo lo que vamos a intentar enseñarle a hacer a una máquina.
La historia de cómo lo inventó, en 1816, es una de las mejores de toda la medicina. Laennec tenía que examinar a una joven paciente con sobrepeso a la que sospechaba una enfermedad de corazón. La costumbre de la época era aplicar directamente el oído sobre el pecho del paciente, pero aquí eso no funcionaba bien (por la corpulencia de la paciente) y, además, a Laennec le pareció poco decoroso hacerlo con una mujer joven. Se acordó entonces de un fenómeno acústico sencillo: si dos niños golpean con un alfiler un extremo de un palo de madera largo, el sonido se oye con mucha claridad aplicando el oído al otro extremo. Enrolló un cuaderno de papel en forma de cilindro, apoyó un extremo sobre el pecho de la paciente y el otro en su propio oído — y descubrió, sorprendido, que oía el corazón “de una manera mucho más clara y distinta” que aplicando el oído directamente. Había inventado, sin buscarlo, una forma completamente nueva de escuchar.
Laennec llamó a su invento “estetoscopio” (del griego stethos, pecho, y skopein, examinar) y publicó en 1819 su gran tratado, De l’Auscultation Médiate, donde describió por primera vez sonidos que los médicos siguen usando hoy en día, como los estertores. Murió en 1826 de tuberculosis — la misma enfermedad que, según se cuenta, le diagnosticó su propio sobrino escuchándole el pecho con el propio estetoscopio que él había inventado.
Hay un hilo que conecta directamente esta historia con la de Helen Taussig, la cardióloga que dio nombre a nuestro proyecto anterior. Taussig se quedó sorda y no podía usar el estetoscopio de Laennec — tuvo que sustituir el oído por el tacto, sintiendo el latido con los dedos. Este proyecto construye, con datos reales, justamente la capacidad que a ella le faltaba: enseñarle a un modelo a “escuchar” el corazón.
De dónde vienen los datos
Vamos a trabajar con grabaciones de audio reales, no simuladas: el dataset “Heartbeat Sounds”, publicado en Kaggle a partir del PASCAL Classifying Heart Sounds Challenge 2011 (CHSC2011), organizado por Peter Bentley, Glenn Nordehn, Miguel Coimbra, Shie Mannor y Rita Getz. Su licencia es CC0 (dominio público).
El dataset tiene dos partes, recogidas de formas distintas. Nosotros vamos a usar la parte llamada Dataset B: 656 archivos de audio en formato .wav, grabados en un ensayo clínico real en hospitales, con un estetoscopio digital llamado DigiScope — no son grabaciones caseras, son datos clínicos de verdad. De esos 656 archivos, 461 están etiquetados con su clase real (para que el modelo aprenda) y 195 quedan sin etiquetar (para poner a prueba el modelo más adelante, como hicimos con el conjunto de prueba en otros proyectos). Cada archivo dura entre 1 y 30 segundos.
Las clases del Dataset B son tres:
| Clase | Significado |
|---|---|
| normal | Latido sano, patrón “lub-dub” constante |
| murmur | Soplo — un sonido de turbulencia entre los golpes del latido, posible señal de un problema de válvula |
| extrastole | Extrasístole — un latido que se sale del ritmo esperado (así, sin la “y”, es como aparece escrito literalmente en los datos) |
Cómo preparar todo antes de la próxima sesión
- Entra en
kaggle.com/datasets/kinguistics/heartbeat-soundscon tu cuenta de Kaggle y descarga el dataset. - Descomprime el contenido en una carpeta llamada
heartbeatsounds, dentro de la carpeta de este proyecto (al mismo nivel que vuestro cuaderno de Jupyter), manteniendo la estructura de carpetas tal cual viene (no muevas los archivos de audio a otro sitio) — así las rutas que aparecen enset_b.csvseguirán apuntando al sitio correcto. - Instala una librería que no hemos usado en ningún proyecto anterior, pensada para trabajar con audio:
librosa. Se instala como cualquier otra, conpip install librosadesde una terminal (o!pip install librosaen una celda de Jupyter).
Si guardáis la carpeta descomprimida con otro nombre, o en otro sitio, solo tenéis que cambiar la variable carpeta_datos del siguiente código para que apunte donde corresponda.
Es la primera vez en toda la familia de proyectos que trabajamos con sonido, así que es normal que necesitemos una herramienta nueva — el resto del cuaderno, para cargar la tabla de etiquetas, seguirá usando pandas, que ya conocéis.
Primer vistazo a los datos
Empezamos por la tabla de metadatos, que nos dice qué archivo de audio corresponde a cada clase:
import pandas as pd
# Carpeta donde descomprimisteis el dataset (cambiadla si usasteis otro nombre)
carpeta_datos = "heartbeatsounds/"
metadatos = pd.read_csv(carpeta_datos + "set_b.csv")
metadatos.head()

Antes de dar nada por hecho, miremos qué columnas trae realmente el fichero — así descubrís vosotros mismos su estructura, en vez de que os la cuente yo:
metadatos.columns

Y contemos cuántos archivos hay de cada clase (incluidos los que todavía no tienen etiqueta):
metadatos["label"].value_counts(dropna=False)

Ahora, lo más parecido que vamos a hacer nunca a lo que hacía Laennec con su cilindro de papel: escuchar de verdad un latido normal y uno con murmullo, uno junto al otro.
Un aviso honesto sobre los datos reales: si intentáis coger el nombre de archivo directamente de la columna fname de metadatos, os va a dar un error raro (LibsndfileError). No es un fallo vuestro: el nombre que guarda esa columna (por ejemplo set_b/Btraining_normal_103_...wav) no coincide con el nombre real del archivo tal y como está empaquetado en Kaggle (que en realidad se llama normal__103_...wav, con doble guion bajo y sin el prefijo Btraining_). Es el tipo de inconsistencia con la que os vais a encontrar constantemente trabajando con datos reales — la solución es no fiarnos ciegamente del nombre que promete la tabla, y buscar directamente en la carpeta los archivos que empiezan por el nombre de cada clase:
import glob
import librosa
import matplotlib.pyplot as plt
# En vez de fiarnos de la columna fname (no coincide con los nombres reales
# de archivo en esta versión del dataset), buscamos directamente en la carpeta
# los archivos que empiezan por el nombre de cada clase
archivo_normal = glob.glob(carpeta_datos + "set_b/normal_*.wav")[0]
archivo_murmullo = glob.glob(carpeta_datos + "set_b/murmur_*.wav")[0]
señal_normal, frecuencia_normal = librosa.load(archivo_normal, sr=None)
señal_murmullo, frecuencia_murmullo = librosa.load(archivo_murmullo, sr=None)
fig, ejes = plt.subplots(1, 2, figsize=(10, 4))
ejes[0].plot(señal_normal)
ejes[0].set_title("Latido normal")
ejes[1].plot(señal_murmullo)
ejes[1].set_title("Latido con murmullo")
plt.tight_layout()
plt.show()

Y, a diferencia de cualquier proyecto anterior, hoy podemos reproducir el sonido directamente en el cuaderno:
from IPython.display import Audio
Audio(archivo_normal)
Audio(archivo_murmullo)

[COMPLETAR EN CLASE: escuchad los dos audios con atención. ¿Se nota claramente el “silbido” del murmullo entre los dos golpes del latido? ¿Se aprecia también esa diferencia con solo mirar la forma de la onda dibujada, o hace falta oírlo para darse cuenta?]
Próximo paso
Aquí nos encontramos con un problema nuevo que ningún proyecto anterior nos había planteado: un modelo de scikit-learn no puede entrenar directamente sobre un audio en bruto, como sí podía hacerlo con una tabla de genes o con los 188 números de un latido de ECG. En la próxima sesión aprenderemos a convertir cada sonido en un puñado de números que sí describen bien lo esencial del latido — y con esos números, entrenaremos nuestro primer modelo.




