827 lines
63 KiB
HTML
827 lines
63 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>AritmoLab — Role of AI in the Analysis of Unstructured Clinical Databases</title>
|
||
<link rel="stylesheet" href="dist/reveal.css">
|
||
<link rel="stylesheet" href="fonts.css?v=8">
|
||
<link rel="stylesheet" href="deck.css?v=40">
|
||
</head>
|
||
<body>
|
||
<div class="reveal"><div class="slides">
|
||
|
||
|
||
<section class="arit title-slide">
|
||
<header class="head">
|
||
<img src="logo.png" alt="Policlinico San Donato">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">AritmoLab · Clinical Data Platform</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<h1>Role of AI in the Analysis of Unstructured Clinical Databases</h1>
|
||
<div class="hgap"></div>
|
||
<p class="lead">How we turned a mix of structured and free-text clinical records into a research data platform — the AritmoLab platform experience.</p>
|
||
<div class="title-meta">
|
||
<span class="pill">AritmoLab</span>
|
||
<span class="what">A clinical data platform hosting cardiology data and analysis tools</span>
|
||
</div>
|
||
<div class="speakers">
|
||
<span>Dr. Marco Pancotti - MultiPhysixLab</span>
|
||
<span>Dr. Sara Paratico - I.R.C.C.S. Policlinico San Donato</span>
|
||
</div>
|
||
<p class="event">“Multidimensional Characterization of Cardiac Arrhythmias: Role of Electrocardiology in the Artificial Intelligence Era”</p>
|
||
<p class="event-where">San Donato Milanese, Milan, Italy · 2–3 October 2026</p>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">01 / 14</span></footer>
|
||
<aside class="notes">
|
||
Good morning everyone, and thank you for being here. I'm Marco Pancotti, and with my colleague Dr. Sara Paratico we work on the clinical data platform of AritmoLab at Policlinico San Donato.
|
||
Today I want to talk about a problem that every hospital knows very well. The most valuable clinical information we have — the diagnosis, the patient's history, the outcome of a test — is written in plain free text, inside systems that were never designed to make that text usable.
|
||
Over the next twelve minutes I'll show you how we used artificial intelligence, in three different roles, to transform twenty years of cardiology records into a database that researchers can actually query — and that clinicians can verify. My colleague Dr. Sara Paratico will join me to show how we read the clinical text itself.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Where we started</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<h2>Four islands and four missing pieces</h2>
|
||
<div class="scatter">
|
||
<div class="todo-stack" style="left:30%; top:22%; width:40%">
|
||
<div class="todo" data-missing="ci" style="margin-left:0"><span class="q">?</span><span class="tx"><b>Clinical Intelligence</b><span>dashboards on the clinical history of the Unit and its patients</span></span></div>
|
||
<div class="todo" data-missing="dwh" style="margin-left:9%"><span class="q">?</span><span class="tx"><b>Datawarehouse</b><span>the single source of truth</span></span></div>
|
||
<div class="todo" data-missing="ml" style="margin-left:4%"><span class="q">?</span><span class="tx"><b>ML-ready data</b><span>cohort tables, ready for model training</span></span></div>
|
||
<div class="todo" data-missing="portal" style="margin-left:12%"><span class="q">?</span><span class="tx"><b>Professional Portal</b><span>AritmoLab — the grown-up Omics Portal</span></span></div>
|
||
</div>
|
||
<button class="isle" data-start="cardioref" style="--rot:-3deg; --size:114px; left:calc(1% + 39px); top:4%; width:230px">
|
||
<svg class="ico" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M24 33 c-6-3.5-9-7-9-10.5 0-3 4-5.5 7-3 1 .8 1.6 1.8 2 3 .4-1.2 1-2.2 2-3 3-2.5 7 0 7 3 0 3.5-3 7-9 10.5z" fill="currentColor" stroke="none"/><path d="M22.5 21 h3 v3 h3 v3 h-3 v3 h-3 v-3 h-3 v-3 h3 z" fill="#fff" stroke="none"/></svg>
|
||
<span class="nm">Cardioref</span><span class="sc" style="width:300px">visits · operations · reports — twenty years of management on structured forms</span></button>
|
||
<button class="isle" data-start="genetic" style="--rot:3deg; --size:114px; left:calc(73% + 39px); top:4%; width:230px">
|
||
<svg class="ico" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8 C28 14 28 20 18 26 C12 30 12 35 18 40"/><path d="M30 8 C20 14 20 20 30 26 C36 30 36 35 30 40"/><path d="M17 12 H31"/><path d="M15.5 18 H32.5"/><path d="M17 24 H31"/><path d="M14.5 31 H22"/><rect x="25" y="29" width="12" height="12" rx="1.5" stroke-width="2"/><path d="M25 34 H37"/><path d="M30.5 29 V41"/></svg>
|
||
<span class="nm">Genetic data</span><span class="sc">DNA instruments — results typed into Excel by hand</span></button>
|
||
<button class="isle" data-start="omics" style="--rot:-2deg; --size:114px; left:calc(1% + 39px); top:55%; width:230px">
|
||
<svg class="ico" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="24" cy="24" r="20" stroke-width="2.6"/><circle cx="24" cy="24" r="4" fill="currentColor" stroke="none"/><circle cx="24" cy="10" r="3.4"/><circle cx="11.5" cy="31" r="3.4"/><circle cx="36.5" cy="31" r="3.4"/><path d="M24 20.5 V13.5"/><path d="M21 26.5 L14 29.5"/><path d="M27 26.5 L34 29.5"/></svg>
|
||
<span class="nm">Omics Portal</span><span class="sc">meant to host Cardioref + genetics — still immature</span></button>
|
||
<button class="isle" data-start="ecg" style="--rot:2deg; --size:114px; left:calc(73% + 39px); top:55%; width:230px">
|
||
<svg class="ico" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="24" cy="24" r="20" stroke-width="2.6"/><path d="M9 24 h6 l3-9 5 16 3-7 h14" stroke-width="2.4"/><circle cx="24" cy="24" r="1.8" fill="currentColor" stroke="none"/></svg>
|
||
<span class="nm">ECG</span><span class="sc">paper strip + a CSV on request</span></button>
|
||
<div class="missing-title" style="left:calc(30% + 14px); top:calc(22% - 46px); width:40%">Missing Pieces</div>
|
||
<p class="hint" style="position:absolute; left:26%; width:48%; bottom:2px; margin:0; text-align:center">▸ click a system: how it was used — and what held it back · click a missing piece: what it would have given</p>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">02 / 14</span></footer>
|
||
<aside class="notes">
|
||
Before I show you what we built, let me take you back twenty years. Cardioref, our electrophysiology database, had twenty years of clinical records: as a management tool for patients, daily activity and planning it was — and still is — excellent. But for research it was almost unusable, because most of the clinical knowledge was written in free text, outside the structured fields. The genetic data lived in its own system, which produced Excel sheets, with no connection to Cardioref. Our first Omics Portal was a promising idea, still embryonic and not integrated with anything. And a constellation of ECG subsystems that could only hand you a CSV file, on request. On top of that: no clinical intelligence on the historical data, no single datawarehouse as the source of truth, no ML-ready data for predictive statistics, and no professional portal to bring it all together. Four islands, four missing pieces. Click any system and I'll show you exactly what held it back. And here is what we built.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit flow-slide">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">The project</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">The plan</div>
|
||
<h2>What we wanted to build</h2>
|
||
<p class="lead">One platform, two destinations: the 360° patient portal and a research-ready warehouse. Built on open-source pillars, with AI coding agents wherever they helped. This talk follows the hardest piece — the unstructured data.</p>
|
||
<div class="flow">
|
||
<div class="zone existed">
|
||
<span class="zlabel">What existed</span>
|
||
<div class="fcol">
|
||
<div class="fbox src"><b>Cardioref</b><span>cardiology records · procedures · letters</span></div>
|
||
<div class="fbox src"><b>Genetic data</b><span>labs · variants · nomenclature</span></div>
|
||
<div class="fbox src"><b>ECG</b><span>signals · device follow-up</span></div>
|
||
<div class="fbox src future"><b><span class="plus">+</span>Future sources</b><span>new subsystems can be connected as sources</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="fcol ai-hit">
|
||
<button class="brain brain-btn" data-ai="ingestion" aria-label="AI contribution: the mappings">🧠</button>
|
||
<div class="farrow">→</div>
|
||
</div>
|
||
<div class="zone built">
|
||
<span class="zlabel">What we built</span>
|
||
<div style="display:flex; align-items:center; gap:10px; height:100%;">
|
||
<div class="fcol" style="flex:0.8; justify-content:center"><div class="fbox"><b>Staging</b><span>raw replica of the sources</span></div></div>
|
||
<div class="farrow">→</div>
|
||
<div class="fcol ai" style="flex:1.25">
|
||
<button class="brain brain-btn" data-ai="integration" aria-label="AI contribution: clinical text reading">🧠</button>
|
||
<div class="fbox"><b>Integration</b><span>cleaned, normalized, deduplicated</span></div>
|
||
<div class="fcap">AI reads the clinical text: pathologies, procedures, drug-challenge outcomes</div>
|
||
</div>
|
||
<div class="farrow">→</div>
|
||
<div class="fcol" style="flex:1.05; justify-content:center"><div class="fbox"><b>Star schema</b><span>dimensional transformation</span></div></div>
|
||
<div class="farrow">→</div>
|
||
<div class="fcol ai" style="flex:1.25">
|
||
<button class="brain brain-btn" data-ai="datamarts" aria-label="AI contribution: datamart generation">🧠</button>
|
||
<div class="fbox"><b>Datamarts</b><span>research-ready marts</span></div>
|
||
<div class="fcap">AI builds them on demand from plain-English questions (ThothII)</div>
|
||
</div>
|
||
<div class="farrow">→</div>
|
||
<div class="fcol" style="flex:1">
|
||
<div class="fbox end"><b>AritmoLab Data Warehouse</b><span>queried for research</span></div>
|
||
<div class="fbox end"><b>AritmoLab Portal</b><span>management & exploration</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="pillars">
|
||
<span class="agents">AI coding agents — Anthropic · OpenAI — used wherever they helped, 360° in the code</span>
|
||
<span class="pillar"><b>Airflow</b> · orchestration</span>
|
||
<span class="pillar"><b>Superset</b> · dashboards</span>
|
||
<span class="pillar"><b>Django</b> · portal scaffolding</span>
|
||
<span class="pillar"><b>Authentik</b> · auth — GSD LDAP</span>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">03 / 14</span></footer>
|
||
<aside class="notes">
|
||
Here is the whole project on one slide. On the left, what already existed: three hospital data sources — Cardioref, our electrophysiology records; the genetic data coming from the labs; and the ECG signals. On the right, what we built in AritmoLab: a staging replica, an integration layer where the data is cleaned and normalized, a star-schema warehouse, and the research datamarts — delivered through the data warehouse and a management portal.
|
||
Everywhere you see the brain symbol, that's where AI works for us — click each brain and its contribution card opens, tied to the brain by a red line. At integration, AI reads the clinical text. At the end, AI builds the datamarts on demand from plain-English questions. And the mappings that bring the sources in were themselves drafted by AI, then revised by humans.
|
||
That was the plan. And to build it, we used AI everywhere it helped — drafting configs, reading clinical text, and at the very end, producing the datamarts themselves. This talk follows the hardest piece of that plan: the unstructured data. My colleague Dr. Paratico will show you exactly how the text reading works. But first, the first step of the climb: the mappings.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit center-v problem">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">The problem</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">From free text to a research platform</div>
|
||
<h2>The clinical truth lives in unstructured columns</h2>
|
||
<div class="quote">
|
||
<div class="tr-grid">
|
||
<p class="it">“Il paziente riferisce sincope ricorrente; ECG basale con sopraslivellamento ST in V1–V3; test provocativo con flecainide positivo per pattern Brugada.”</p>
|
||
<p class="en"><span class="en-tag">EN</span>“The patient reports recurrent syncope; baseline ECG with ST-segment elevation in V1–V3; flecainide provocation test positive for a Brugada pattern.”</p>
|
||
</div>
|
||
</div>
|
||
<div class="pipeline" id="pipeline">
|
||
<div class="stage"><b>Sources</b><span>Cardioref · Genetic data<br>Omics Portal · ECG</span><span class="brain-ai"><svg viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4 C11 2 5.5 4 6 8.5 C2.5 10 2 14.5 4.5 17 C2.5 20 4 24.5 8 25 C9 28 14 29.5 16 27"/><path d="M16 4 C21 2 26.5 4 26 8.5 C29.5 10 30 14.5 27.5 17 C29.5 20 28 24.5 24 25 C23 28 18 29.5 16 27"/><path d="M16 4.5 V26.5"/><path d="M16 10 h4.5"/><circle cx="23" cy="10" r="1.7" fill="currentColor" stroke="none"/><path d="M16 15.5 h-4.5"/><circle cx="8.5" cy="15.5" r="1.7" fill="currentColor" stroke="none"/><path d="M16 21 h4.5"/><circle cx="23" cy="21" r="1.7" fill="currentColor" stroke="none"/></svg></span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage"><b>Staging</b><span>raw replica</span><span class="brain-ai"><svg viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4 C11 2 5.5 4 6 8.5 C2.5 10 2 14.5 4.5 17 C2.5 20 4 24.5 8 25 C9 28 14 29.5 16 27"/><path d="M16 4 C21 2 26.5 4 26 8.5 C29.5 10 30 14.5 27.5 17 C29.5 20 28 24.5 24 25 C23 28 18 29.5 16 27"/><path d="M16 4.5 V26.5"/><path d="M16 10 h4.5"/><circle cx="23" cy="10" r="1.7" fill="currentColor" stroke="none"/><path d="M16 15.5 h-4.5"/><circle cx="8.5" cy="15.5" r="1.7" fill="currentColor" stroke="none"/><path d="M16 21 h4.5"/><circle cx="23" cy="21" r="1.7" fill="currentColor" stroke="none"/></svg></span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage ai"><b>Integration</b><span>3NF · text mining</span><span class="brain-ai"><svg viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4 C11 2 5.5 4 6 8.5 C2.5 10 2 14.5 4.5 17 C2.5 20 4 24.5 8 25 C9 28 14 29.5 16 27"/><path d="M16 4 C21 2 26.5 4 26 8.5 C29.5 10 30 14.5 27.5 17 C29.5 20 28 24.5 24 25 C23 28 18 29.5 16 27"/><path d="M16 4.5 V26.5"/><path d="M16 10 h4.5"/><circle cx="23" cy="10" r="1.7" fill="currentColor" stroke="none"/><path d="M16 15.5 h-4.5"/><circle cx="8.5" cy="15.5" r="1.7" fill="currentColor" stroke="none"/><path d="M16 21 h4.5"/><circle cx="23" cy="21" r="1.7" fill="currentColor" stroke="none"/></svg></span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage"><b>Data Warehouse</b><span>star schema</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage hot" id="marts"><b>Marts</b><span>dbt · Superset</span></div>
|
||
</div>
|
||
<div class="midrow">
|
||
<div class="concepts">
|
||
<div class="lbl">four challenges to face · the answers come next</div>
|
||
<div class="citem"><span class="n">01</span><div><b>Volume without structure</b><span class="d">twenty years of records locked in free-text columns</span></div></div>
|
||
<div class="citem"><span class="n">02</span><div><b>Ambiguous language</b><span class="d">negation · family history · bilingual shorthand</span></div></div>
|
||
<div class="citem"><span class="n">03</span><div><b>No shared ontology</b><span class="d">every system names conditions its own way</span></div></div>
|
||
<div class="citem"><span class="n">04</span><div><b>Proving reliability</b><span class="d">extraction must be demonstrably correct</span></div></div>
|
||
</div>
|
||
<div class="railwrap" id="railwrap">
|
||
<div class="conn-drop" id="conn-drop"></div>
|
||
<div class="conn-elbow" id="conn-elbow"></div>
|
||
<div class="rail" id="rail">
|
||
<div class="lbl">outputs of the datamarts</div>
|
||
<div class="row"><svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M4 29 h26"/><rect x="7" y="17" width="5" height="12" rx="1"/><rect x="15" y="9" width="5" height="20" rx="1"/><rect x="23" y="13" width="5" height="16" rx="1"/></svg><b>Dashboards</b><span class="d">clinical review on Superset</span></div>
|
||
<div class="row"><svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="7" cy="7" r="2.6"/><circle cx="7" cy="27" r="2.6"/><circle cx="17" cy="17" r="3.4"/><circle cx="27" cy="7" r="2.6"/><circle cx="27" cy="27" r="2.6"/><path d="M9.3 8.8 L14.6 14.6"/><path d="M9.3 25.2 L14.6 19.4"/><path d="M19.4 14.6 L24.7 8.8"/><path d="M19.4 19.4 L24.7 25.2"/></svg><b>Machine Learning</b><span class="d">cohort tables for model training</span></div>
|
||
<div class="row"><svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M3 27 L11 19 L17 22 L23 13"/><path d="M23 13 L30 6" stroke-dasharray="3 3.4"/><circle cx="30" cy="6" r="2.2" fill="currentColor" stroke="none"/><path d="M3 31 h28" opacity=".35"/></svg><b>Predictive Statistics</b><span class="d">outcomes & risk analysis</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="thoth-wrap" id="thothwrap">
|
||
<div class="t-conn" id="t-conn"></div>
|
||
<div class="t-head" id="t-head"></div>
|
||
<div class="thoth-col">
|
||
<div class="thoth-box"><span class="brain-ai"><svg viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4 C11 2 5.5 4 6 8.5 C2.5 10 2 14.5 4.5 17 C2.5 20 4 24.5 8 25 C9 28 14 29.5 16 27"/><path d="M16 4 C21 2 26.5 4 26 8.5 C29.5 10 30 14.5 27.5 17 C29.5 20 28 24.5 24 25 C23 28 18 29.5 16 27"/><path d="M16 4.5 V26.5"/><path d="M16 10 h4.5"/><circle cx="23" cy="10" r="1.7" fill="currentColor" stroke="none"/><path d="M16 15.5 h-4.5"/><circle cx="8.5" cy="15.5" r="1.7" fill="currentColor" stroke="none"/><path d="M16 21 h4.5"/><circle cx="23" cy="21" r="1.7" fill="currentColor" stroke="none"/></svg></span>ThothII — natural-language → SQL over the DWH, builds the final datamarts</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">04 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT] Here is one real sentence from a discharge letter — in Italian, as the clinicians wrote it. In this single sentence there is a diagnosis (syncope), an ECG finding (ST elevation in V1–V3), and a drug-challenge result (flecainide positive for Brugada pattern). For a human cardiologist this is readable in two seconds. For a database, this is just a blob of text in a column.
|
||
The structured tables — demographics, procedures, dates — only tell half the story. The rest is locked inside these free-text fields, in every hospital system we have.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit center-v stats">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Text analysis</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">What the text mining produced</div>
|
||
<h2>Deterministic AI, audited numbers</h2>
|
||
<div class="strip">
|
||
<div class="sstat"><span class="n">58,438</span><span class="l">letters read</span></div>
|
||
<div class="sstat"><span class="n">73,389</span><span class="l">pathologies extracted</span></div>
|
||
<div class="sstat"><span class="n">10,908</span><span class="l">tests parsed</span></div>
|
||
<div class="sstat"><span class="n">2,307</span><span class="l">Brugada patients</span></div>
|
||
<div class="sstat"><span class="n">≈ 240</span><span class="l">letters / month</span></div>
|
||
</div>
|
||
<div class="cycle" id="cycle">
|
||
<svg class="cycle-svg" viewBox="0 0 1184 270">
|
||
<defs><marker id="arrw" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#cb333b"/></marker></defs>
|
||
<g id="cycle-arcs" fill="none" stroke="#cb333b" stroke-width="2">
|
||
<path marker-end="url(#arrw)"/>
|
||
<path marker-end="url(#arrw)"/>
|
||
<path marker-end="url(#arrw)"/>
|
||
<path marker-end="url(#arrw)"/>
|
||
</g>
|
||
</svg>
|
||
<div class="vcomp" style="left:592px; top:50px"><svg class="cicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="4" width="20" height="26" rx="2.5"/><path d="M12 11h10"/><path d="M12 16h10"/><path d="M12 21h6"/></svg><b>Letter reader</b><span class="d">every discharge letter, Italian & English</span></div>
|
||
<div class="vcomp" style="left:992px; top:135px"><svg class="cicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="14.5" cy="14.5" r="8"/><path d="M20.5 20.5 L29 29"/><path d="M11 14.5 h7"/><path d="M14.5 11 v7"/></svg><b>Clinical matcher</b><span class="d">recognises diagnoses, procedures, test outcomes</span></div>
|
||
<div class="vcomp" style="left:592px; top:220px"><svg class="cicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3.5 L28 8 v8.5 c0 7-4.7 11.6-11 14 C10.7 28.1 6 23.5 6 16.5 V8 Z"/><path d="M12.5 16.5 l3.2 3.2 L23.5 12"/></svg><b>Context guard</b><span class="d">negations & family history kept apart</span></div>
|
||
<div class="vcomp" style="left:192px; top:135px"><svg class="cicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="17" cy="7" r="3"/><circle cx="7" cy="26.5" r="3"/><circle cx="27" cy="26.5" r="3"/><path d="M17 10 v7"/><path d="M17 17 L8 23.5"/><path d="M17 17 L26 23.5"/></svg><b>Ontology sorter</b><span class="d">every finding into its clinical category</span></div>
|
||
</div>
|
||
<div class="note-row">
|
||
<div class="nitem"><b>Traceable.</b> Every finding carries the version of the rules that produced it (<code>pattern_version</code>): any number can be rebuilt years later.</div>
|
||
<div class="nitem"><b>Audited.</b> A new rule set goes live only after it proves ≥95% accuracy on 100 records reviewed by hand.</div>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">05 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT — Sara] I'll take you inside these numbers. We read 58,438 discharge letters — every single one, every night. From them the text miner extracted 73,389 pathology records, classified into our clinical ontology. It parsed 10,908 drug-challenge tests, distinguishing the therapy from the actual test. And at the end of the chain: 2,307 patients with a confirmed Brugada pattern — a cohort nobody could have built by hand.
|
||
This is not a one-off migration: the pipeline runs every night, and today it processes on average two hundred and forty letters a month. Four simple pieces do the work: a letter reader for the Italian and English text, a clinical matcher that recognises diagnoses, procedures and test outcomes, a context guard that keeps negations and family history apart, and the ontology sorter that files every finding into its clinical category.
|
||
The key word here is deterministic: no black box. Every number can be traced back to the rule that produced it.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit nlp">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Text analysis</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">Clinical NLP</div>
|
||
<h2>Reading clinical text is not keyword matching</h2>
|
||
<div class="hgap"></div>
|
||
<div class="split">
|
||
<div class="left">
|
||
<ul class="points">
|
||
<li><b>Negation</b> — “fibrillazione atriale <i>esclusa</i>”, “<i>non</i> FA” <span class="g">(“AF ruled out”, “no AF”)</span>: the rule scans a window around every match before deciding</li>
|
||
<li><b>Family ≠ patient</b> — “padre con FA” <span class="g">(“father with AF”)</span> is attributed to the family member, not to the patient</li>
|
||
<li><b>Two languages</b> — every pattern matches IT and EN forms: fibrillazione atriale / atrial fibrillation</li>
|
||
<li><b>Abbreviations & noise</b> — FA, f.a., “TA 140/90” (blood pressure, not a diagnosis)</li>
|
||
<li><b>One field, many statements</b> — the text is split into clauses before matching</li>
|
||
</ul>
|
||
</div>
|
||
<div class="right">
|
||
<div class="code">
|
||
<div class="code-head"><span>what the rules see</span><span>examples from the corpus</span></div>
|
||
<pre>“<i>padre con</i> fibrillazione atriale”
|
||
<span class="en">“father with atrial fibrillation”</span>
|
||
→ patologia: FA · attribuzione: <b>familiare</b>
|
||
<span class="en">pathology: AF · attribution: family</span>
|
||
|
||
“fibrillazione atriale <i>esclusa</i>”
|
||
<span class="en">“atrial fibrillation ruled out”</span>
|
||
→ patologia: FA · <b>negato</b>
|
||
<span class="en">pathology: AF · negated</span>
|
||
|
||
“test provocativo con flecainide
|
||
positivo per pattern Brugada”
|
||
<span class="en">“flecainide provocation test, positive for Brugada pattern”</span>
|
||
→ test: flecainide · esito: <b>POSITIVO</b>
|
||
<span class="en">test: flecainide · outcome: POSITIVE</span></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">06 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT — Sara] Why can't we just search for "FA"? Because clinical text lies to naive search. "Fibrillazione atriale esclusa" contains the words of a diagnosis but negates it — so every rule scans a window around the match, looking for negation cues. "Padre con FA" is real atrial fibrillation — but in the father, not the patient: we record it as family history. Letters mix Italian and English, abbreviations collide ("TA" is blood pressure, not a therapy), and one field can contain five different statements — so we split the text into clauses first.
|
||
Each of these problems has a specific, versioned solution. Sara to expand with real corpus examples.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit center-v">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Text analysis</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">The clinical ontology</div>
|
||
<h2>Two tiers, one clinical order</h2>
|
||
<div class="onto-split">
|
||
<div class="onto-left">
|
||
<div class="tier-h">TIER 1 · arrhythmic<span class="cnt">11</span></div>
|
||
<div class="cats">
|
||
<span class="cat fill"><b>AF</b><span class="it">FA</span></span><span class="cat fill"><b>Brugada</b></span><span class="cat fill"><b>Flutter</b></span><span class="cat fill"><b>AT</b><span class="it">TA</span></span><span class="cat fill"><b>AVNRT</b><span class="it">TRN</span></span><span class="cat fill"><b>PSVT</b><span class="it">WPW / TPSV</span></span><span class="cat fill"><b>VT</b><span class="it">TV</span></span><span class="cat fill"><b>VF</b><span class="it">FV</span></span><span class="cat fill"><b>Ventricular ectopy</b><span class="it">Extrasistolia V.</span></span><span class="cat fill"><b>Syncope</b><span class="it">Sincope</span></span><span class="cat fill"><b>Long QT</b><span class="it">QT lungo</span></span>
|
||
</div>
|
||
<div class="tier-h">TIER 2 · structural<span class="cnt">5</span></div>
|
||
<div class="cats">
|
||
<span class="cat"><b>AV block</b><span class="it">Blocco AV</span></span><span class="cat"><b>Bundle branch block</b><span class="it">Blocco di branca</span></span><span class="cat"><b>Cardiomyopathy</b><span class="it">Cardiomiopatia</span></span><span class="cat"><b>Heart failure</b><span class="it">Scompenso</span></span><span class="cat"><b>Valvular disease</b><span class="it">Valvulopatia</span></span>
|
||
</div>
|
||
<div class="onto-points">
|
||
<div class="oitem"><span class="n">01</span><div><b>Order encodes clinical precedence</b> <span class="d">— “Brugada” is matched before “TV”, so “substrato per TV” <span class="g">(“substrate for VT”)</span> can't mask a Brugada pattern</span></div></div>
|
||
<div class="oitem"><span class="n">02</span><div><b>Synonyms live inline</b> <span class="d">— FA / f.a. / fib. atriale / atrial fibrillation → one canonical label</span></div></div>
|
||
<div class="oitem"><span class="n">03</span><div><b>Versioned like software</b> <span class="d">— semver for the pattern library, <code>pattern_version</code> on every extracted row</span></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="out-panel">
|
||
<div class="out-h">The output: text becomes recorded data</div>
|
||
<div class="out-item"><span class="n">01</span><div><b>Quantitative data on the records</b><span class="d">every procedure and implant the text analysis reads is written back as structured, quantitative values on the record that describes it</span></div></div>
|
||
<div class="out-item"><span class="n">02</span><div><b>Straight into the DWH flow</b><span class="d">these records enter the data-warehouse generation like any other source</span></div></div>
|
||
<div class="out-item"><span class="n">03</span><div><b>As if typed at the visit</b><span class="d">the data lands exactly as if clinicians had keyed it in themselves during the visits</span></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">07 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT — Sara] Extraction needs a target vocabulary — that's the ontology. Tier 1 holds the eleven arrhythmological categories that matter most for our research; Tier 2 holds five structural conditions. The order of the rules is itself clinical knowledge: Brugada patterns are tested before TV, because "substrato per TV" often appears in Brugada reports and would otherwise mask the diagnosis.
|
||
The ontology is versioned like software: a semantic version for the pattern library, stamped on every extracted row. When we add a synonym or fix a rule, the change is traceable — and the data can be rebuilt.
|
||
And this is the output of the whole work: reading a letter writes structured, quantitative values back onto the records that describe the procedures and implants it contains — and those records enter the warehouse generation exactly as if the clinicians had typed them during the visits. Text becomes data, indistinguishable from bedside data entry.
|
||
Sara: review the category list and the Italian labels before final. Labels are now English-first (audience is mostly foreign) — please confirm the EN terms, especially PSVT as the umbrella for WPW/TPSV and AT/AVNRT for TA/TRN.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section class="arit center-v">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Text analysis</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">Validation</div>
|
||
<h2>Trusting the text is a process, not a promise</h2>
|
||
<div class="onto-split">
|
||
<div class="onto-left">
|
||
<div class="onto-points">
|
||
<div class="oitem"><span class="n">01</span><div><b>Quality gates</b> <span class="d">— ≥95% procedure classification, ≥85% pathology detection, validated on 100 manually reviewed records</span></div></div>
|
||
<div class="oitem"><span class="n">02</span><div><b>Clinical review</b> <span class="d">— the extracted Brugada cohort was validated by clinicians on Superset dashboards</span></div></div>
|
||
<div class="oitem"><span class="n">03</span><div><b>Feedback loop</b> <span class="d">— ~893 Brugada false positives found and removed in pattern-library v1.3.1</span></div></div>
|
||
<div class="oitem"><span class="n">04</span><div><b>Nothing is silently dropped</b> <span class="d">— negated and family-attributed findings are stored too, filtered only at the mart layer</span></div></div>
|
||
</div>
|
||
<div class="roi roi-quiet"><b class="k">Quality gate</b>An extraction is accepted only after 100 manual reviews confirm the agreed accuracy — the same standard for every new pattern release.</div>
|
||
</div>
|
||
<div class="out-panel">
|
||
<div class="out-h">The advantage: speed, precision, determinism</div>
|
||
<div class="out-item"><span class="n">01</span><div><b>Machine speed</b><span class="d">twenty years of letters are read by rules, not by hand; when a pattern improves, the whole archive is rebuilt from the letters themselves</span></div></div>
|
||
<div class="out-item"><span class="n">02</span><div><b>Audited precision</b><span class="d">every release clears the same gate before production: ≥95% procedure classification, ≥85% pathology detection</span></div></div>
|
||
<div class="out-item"><span class="n">03</span><div><b>Deterministic by design</b><span class="d">the same letter always yields the same values: no LLM sampling, no randomness, every result traceable to a versioned pattern</span></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">08 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT — Sara] How do we know the extraction is right? Not by faith. Before any pattern release goes to production it passes a gate: on 100 manually reviewed records, procedure classification must reach 95% accuracy, pathology detection 85%. The Brugada cohort itself was reviewed by clinicians on dashboards — and that review fed back into the library: version 1.3.1 alone removed roughly 900 false positives.
|
||
And we never throw information away: negated and family findings are stored, only filtered when the research mart needs them. Sara: adjust the numbers/wording to what you are comfortable presenting.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ============ 10 · ARITMOLAB TODAY ============ -->
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">AritmoLab today</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">The portal, today</div>
|
||
<h2>AritmoLab — a quick tour</h2>
|
||
<div class="shots">
|
||
<div class="shot">screenshot</div><div class="shot">screenshot</div><div class="shot">screenshot</div>
|
||
<div class="shot">screenshot</div><div class="shot">screenshot</div><div class="shot">screenshot</div>
|
||
</div>
|
||
<p class="hint">[MP: 6-7 real screenshots of the portal, anonymized or demo data]</p>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">09 / 14</span></footer>
|
||
<aside class="notes">[DRAFT] A quick tour of what AritmoLab looks like today — six screenshots: patient profile, genetics, dashboards, the 360-degree view. What began as four islands is now one platform the Unit uses every day. MP: replace with real screenshots and a spoken tour.</aside>
|
||
</section>
|
||
<!-- ============ 11 · CRISIS ============ -->
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">The crisis</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">All good? Not yet.</div>
|
||
<h2>The warehouse speaks SQL. Research needs more.</h2>
|
||
<div class="hgap"></div>
|
||
<ul class="points">
|
||
<li><b>Clinicians and researchers don't write SQL</b> — a star schema is built for analysts, not for the ward</li>
|
||
<li><b>Health Intelligence</b> — to present the past, the Unit needs ready-made aggregates: volumes, incidence, outcomes</li>
|
||
<li><b>Predictive research</b> — multivariate analysis and ML models need clean, wide, cohort-shaped tables</li>
|
||
<li><b>Hand-made datamarts are the bottleneck</b> — even with coding agents, each one takes hours of careful work</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">The gap</b>Twenty years of data, one warehouse — and no fast road from a research question to an answer.</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">10 / 14</span></footer>
|
||
<aside class="notes">[DRAFT] And here is the crisis of our story. The warehouse holds everything — but it speaks SQL. A clinician cannot query a star schema between two visits, and a researcher cannot build a study on raw tables. What research needs are datamarts: ready-made views for Health Intelligence, and clean cohort tables for multivariate analysis and machine learning. And building them by hand — even with coding agents helping — takes hours of careful, repetitive work for every single study. This is where our story got stuck.</aside>
|
||
</section>
|
||
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">ThothII</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">Role 3 — AI for analysis</div>
|
||
<h2>Ask the warehouse in plain English</h2>
|
||
<div class="hgap"></div>
|
||
<div class="quote">“How many Brugada patients had an effective ablation?”
|
||
<small>a researcher, in natural language</small>
|
||
</div>
|
||
<div class="pipeline">
|
||
<div class="stage ai"><b>NL → SQL</b><span>AI writes the query over the star schema</span><span class="tag">AI</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage"><b>Human review</b><span>the researcher checks every proposed query</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage ai"><b>Datamart</b><span>curated, research-ready marts</span><span class="tag">AI</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="stage"><b>Superset</b><span>statistics & dashboards</span></div>
|
||
</div>
|
||
<p class="stat-note">The session is a conversation: refine the question, iterate on the datamart — every step reviewable.</p>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">11 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT] Third role: AI for the analysis itself. This is ThothII, our natural-language layer over the warehouse. A researcher asks "how many Brugada patients had an effective ablation?" in plain English; the AI writes the SQL over the star schema, proposes a datamart, and the researcher reviews the query before it runs. The result lands in Superset as statistics and dashboards.
|
||
The human stays in the loop — the AI proposes, the researcher approves. This is where the demo will go live in a couple of slides.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ============ 13 · THOTHII TOUR ============ -->
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">A tour of ThothII</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">ThothII, step by step</div>
|
||
<h2>From question to datamart — the app</h2>
|
||
<div class="shots">
|
||
<div class="shot">screenshot</div><div class="shot">screenshot</div><div class="shot">screenshot</div>
|
||
<div class="shot">screenshot</div><div class="shot">screenshot</div><div class="shot">screenshot</div>
|
||
</div>
|
||
<p class="hint">[MP: 6-7 real screenshots of ThothII — ask, review SQL, datamart, dashboard]</p>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">12 / 14</span></footer>
|
||
<aside class="notes">[DRAFT] Let me walk you through ThothII: the researcher asks the question in plain English; the AI proposes the SQL; the query is reviewed; the datamart is assembled; the dashboard comes alive. Six screens, a few minutes. MP: real screenshots.</aside>
|
||
</section>
|
||
<!-- ============ 14 · HAPPY ENDING ============ -->
|
||
<section class="arit">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">The happy ending</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">From datamart to discovery</div>
|
||
<h2>One datamart — many questions answered</h2>
|
||
<div class="charts">
|
||
<div class="chart"><b>Multivariate analysis</b><span>[SVG chart — next step]</span></div>
|
||
<div class="chart"><b>Machine learning</b><span>[SVG chart — next step]</span></div>
|
||
</div>
|
||
<p class="hint">[Illustrative data, modeled on published arrhythmology predictors — Brugada focus]</p>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">13 / 14</span></footer>
|
||
<aside class="notes">[DRAFT — next step: two SVG charts, illustrative data modeled on real literature] And this is the happy ending. From one datamart, the Unit can run a multivariate analysis — which factors truly drive arrhythmic risk in Brugada patients — and train machine-learning models on the same table. What used to take weeks of manual data preparation now takes minutes. The AI did not replace the researcher: it gave the researcher back their time.</aside>
|
||
</section>
|
||
|
||
<section class="arit title-slide">
|
||
<header class="head">
|
||
<img src="logo.png" alt="">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Thank you</span>
|
||
</nav>
|
||
<div class="sbody">
|
||
<div class="kicker">Questions?</div>
|
||
<h1>Thank you</h1>
|
||
<div class="hgap"></div>
|
||
<p class="lead">Want the deep dives? The technical walkthroughs of the text miner, the mapping agents and ThothII are coming as videos on our Substack.</p>
|
||
<div class="speakers" style="margin-top:32px">
|
||
<span>Dr. Marco Pancotti - MultiPhysixLab</span>
|
||
<span>Dr. Sara Paratico - I.R.C.C.S. Policlinico San Donato</span>
|
||
</div>
|
||
</div>
|
||
<footer class="foot"><span class="g">Role of AI in the Analysis of Unstructured Clinical Databases</span><span class="g">Dr. Marco Pancotti - MultiPhysixLab</span><span class="g">Dr. Sara Paratico - Gruppo San Donato</span><span class="g">San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span class="g num">14 / 14</span></footer>
|
||
<aside class="notes">
|
||
[DRAFT] Thank you for your attention. If you want to go deeper — the text miner, the mapping agents, ThothII — we are publishing the technical walkthroughs as videos on our Substack; the link is on the final version of this deck. And now, happy to take your questions.
|
||
</aside>
|
||
</section>
|
||
|
||
</div></div>
|
||
|
||
<!-- AI contribution popup -->
|
||
<div class="ai-pop" id="aiPop">
|
||
<svg class="ai-line"><line x1="0" y1="0" x2="0" y2="0"></line><circle cx="0" cy="0" r="5"></circle></svg>
|
||
<div class="ai-pop-card" role="dialog" aria-modal="true">
|
||
<button class="ai-pop-x" aria-label="Close">✕</button>
|
||
<div class="ai-pop-kicker" id="aiPopKicker">AI contribution</div>
|
||
<div class="ai-pop-title"><span class="brain brain-btn" id="aiPopBrain">🧠</span><b id="aiPopTitle"></b></div>
|
||
<div id="aiPopBody"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="dist/reveal.js"></script>
|
||
<script src="plugin/notes/notes.js?v=3"></script>
|
||
<script>
|
||
Reveal.initialize({
|
||
width: 1280, height: 720, margin: 0,
|
||
hash: true, controls: false, progress: false,
|
||
transition: 'fade', overview: true,
|
||
plugins: [RevealNotes],
|
||
});
|
||
|
||
// jump dropdown — one select per slide, in the red band
|
||
const JUMP_TITLES = ['Title', 'Where we started', 'What we wanted to build',
|
||
'The problem', 'What the text miner reads', 'Hard problems of clinical NLP', 'The clinical ontology',
|
||
'Trust the text', 'AritmoLab today', 'All good? Not yet', 'ThothII', 'A tour of ThothII',
|
||
'From datamarts to models', 'Thank you'];
|
||
document.querySelectorAll('.arit .band').forEach((band) => {
|
||
const sel = document.createElement('select');
|
||
sel.className = 'jump';
|
||
sel.setAttribute('aria-label', 'Jump to slide');
|
||
sel.innerHTML = JUMP_TITLES.map((t, i) => `<option value="${i}">${String(i + 1).padStart(2, '0')} · ${t}</option>`).join('');
|
||
sel.addEventListener('change', () => { Reveal.slide(parseInt(sel.value, 10)); sel.blur(); });
|
||
band.appendChild(sel);
|
||
});
|
||
Reveal.on('slidechanged', () => {
|
||
const idx = Reveal.getIndices().h;
|
||
document.querySelectorAll('.arit .band select.jump').forEach((s) => { s.selectedIndex = idx; s.blur(); });
|
||
});
|
||
|
||
// slide "The problem": rail drops from Marts center, ThothII arrow rises to Marts.
|
||
// Reveal scales the canvas — all rect offsets are divided by the current scale.
|
||
function alignProblem() {
|
||
const pipe = document.getElementById('pipeline');
|
||
if (!pipe || !pipe.closest('section').classList.contains('present')) return;
|
||
const s = Reveal.getScale() || 1;
|
||
const pr = pipe.getBoundingClientRect();
|
||
const marts = document.getElementById('marts').getBoundingClientRect();
|
||
const rail = document.getElementById('rail').getBoundingClientRect();
|
||
const rwrap = document.getElementById('railwrap').getBoundingClientRect();
|
||
const drop = document.getElementById('conn-drop');
|
||
const elbow = document.getElementById('conn-elbow');
|
||
const cx = (marts.left + marts.width / 2 - rwrap.left) / s;
|
||
const railLeft = (rail.left - rwrap.left) / s;
|
||
const railTop = (rail.top - rwrap.top) / s;
|
||
const mBottom = (marts.bottom - rwrap.top) / s;
|
||
drop.style.left = (cx - 1) + 'px';
|
||
drop.style.top = mBottom + 'px';
|
||
drop.style.height = Math.max(0, railTop - mBottom) + 'px';
|
||
elbow.style.left = railLeft + 'px';
|
||
elbow.style.width = Math.max(0, cx - railLeft) + 'px';
|
||
elbow.style.top = (railTop - 1) + 'px';
|
||
const twrap = document.getElementById('thothwrap');
|
||
const box = twrap.querySelector('.thoth-box');
|
||
const tr = twrap.getBoundingClientRect();
|
||
const br = box.getBoundingClientRect();
|
||
const conn = document.getElementById('t-conn');
|
||
const head = document.getElementById('t-head');
|
||
const x = tr.width / s - 10;
|
||
const top = (pr.bottom - tr.top) / s + 2;
|
||
const boxTop = (br.top - tr.top) / s;
|
||
conn.style.left = x + 'px';
|
||
conn.style.top = top + 'px';
|
||
conn.style.height = Math.max(0, boxTop - top) + 'px';
|
||
head.style.left = x + 'px';
|
||
head.style.top = (top - 7) + 'px';
|
||
}
|
||
addEventListener('load', alignProblem);
|
||
addEventListener('resize', alignProblem);
|
||
Reveal.on('slidechanged', alignProblem);
|
||
Reveal.on('ready', alignProblem);
|
||
if (document.fonts && document.fonts.ready) document.fonts.ready.then(alignProblem);
|
||
|
||
// slide "What the text miner reads": curved arrows between the four components,
|
||
// drawn from the measured edges of each block so every arrow starts and lands precisely.
|
||
function alignCycle() {
|
||
const cyc = document.getElementById('cycle');
|
||
if (!cyc || !cyc.closest('section').classList.contains('present')) return;
|
||
const s = Reveal.getScale() || 1;
|
||
const cr = cyc.getBoundingClientRect();
|
||
const boxes = [...cyc.querySelectorAll('.vcomp')].map(c => {
|
||
const r = c.getBoundingClientRect();
|
||
const l = (r.left - cr.left) / s, t = (r.top - cr.top) / s, w = r.width / s, h = r.height / s;
|
||
return { l, t, w, h, cx: l + w / 2, cy: t + h / 2 };
|
||
});
|
||
function edgePoint(from, to, box) {
|
||
const dx = to.cx - from.cx, dy = to.cy - from.cy;
|
||
let t = 1;
|
||
if (dx > 0) t = Math.min(t, ((box.l + box.w) - from.cx) / dx);
|
||
if (dx < 0) t = Math.min(t, (box.l - from.cx) / dx);
|
||
if (dy > 0) t = Math.min(t, ((box.t + box.h) - from.cy) / dy);
|
||
if (dy < 0) t = Math.min(t, (box.t - from.cy) / dy);
|
||
return { x: from.cx + dx * t, y: from.cy + dy * t };
|
||
}
|
||
const paths = cyc.querySelectorAll('#cycle-arcs path');
|
||
const seq = [0, 1, 2, 3];
|
||
for (let k = 0; k < 4; k++) {
|
||
const a = boxes[seq[k]], b = boxes[seq[(k + 1) % 4]];
|
||
const start = edgePoint(a, b, a), end = edgePoint(b, a, b);
|
||
const mid = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
|
||
let ox = mid.x - 592, oy = mid.y - 135;
|
||
const len = Math.hypot(ox, oy) || 1;
|
||
const cpx = mid.x + (ox / len) * 46, cpy = mid.y + (oy / len) * 46;
|
||
paths[k].setAttribute('d',
|
||
`M ${start.x.toFixed(1)} ${start.y.toFixed(1)} Q ${cpx.toFixed(1)} ${cpy.toFixed(1)} ${end.x.toFixed(1)} ${end.y.toFixed(1)}`);
|
||
}
|
||
}
|
||
addEventListener('load', alignCycle);
|
||
addEventListener('resize', alignCycle);
|
||
Reveal.on('slidechanged', alignCycle);
|
||
Reveal.on('ready', alignCycle);
|
||
if (document.fonts && document.fonts.ready) document.fonts.ready.then(alignCycle);
|
||
|
||
const AI_CONTRIBS = {
|
||
ingestion: {
|
||
title: 'The mappings',
|
||
body: `
|
||
<p class="lead">The mappings that turn three raw sources into clean data are <b>crafted by AI</b> — <b>revised by humans</b>.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>AI drafts</b> — LLM agents write the mappings from prompts and schema samples</li>
|
||
<li><b>What is a mapping?</b> — a plain instruction sheet that tells the system, field by field, where each piece of data comes from and where it has to land</li>
|
||
<li><b>At scale</b> — <span class="n">19,764</span> lines of instructions, all machine-validated</li>
|
||
<li><b>Humans revise</b> — every line is checked, then recorded in <b>git</b>, the archive that keeps every version of the instructions and can undo any change</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Positive effects</b>Weeks of hand-writing became days of reviewing.</div>`,
|
||
},
|
||
integration: {
|
||
title: 'Reading the clinical text',
|
||
body: `
|
||
<p class="lead">A deterministic, bilingual (IT/EN) pattern library reads the free text inside the nightly pipeline.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Pathologies</b> — <span class="n">73,389</span> extracted from <span class="n">58,438</span> discharge letters, into a two-tier clinical ontology</li>
|
||
<li><b>Procedures ↔ pathologies</b> — linked at clause level</li>
|
||
<li><b>Drug-challenge tests</b> — <span class="n">10,908</span> parsed (flecainide, ajmaline, adrenaline, isoprenaline)</li>
|
||
<li><b>No black box</b> — semver rules, <code>pattern_version</code> on every row, <span class="n">≥95%</span> accuracy gate on 100 manual reviews</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Positive effects</b>58,438 letters of free text became an analysable research asset — automatically, every night.</div>`,
|
||
},
|
||
datamarts: {
|
||
title: 'Datamarts from plain English',
|
||
body: `
|
||
<p class="lead">Researchers ask in plain English; the AI writes the SQL over the star schema.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Ask</b> — “how many Brugada patients had an effective ablation?”</li>
|
||
<li><b>Generate</b> — AI builds the SQL and assembles a curated datamart</li>
|
||
<li><b>Serve</b> — results flow to Superset for statistics and machine learning</li>
|
||
<li><b>In the loop</b> — the researcher reviews every proposed query before it runs</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Positive effects</b>A new research datamart in minutes instead of weeks of hand-written SQL — with the researcher approving every query.</div>`,
|
||
},
|
||
};
|
||
const pop = document.getElementById('aiPop');
|
||
const card = pop.querySelector('.ai-pop-card');
|
||
const line = pop.querySelector('.ai-line line');
|
||
const dot = pop.querySelector('.ai-line circle');
|
||
const closePop = () => pop.classList.remove('open');
|
||
const placeNear = (brainEl) => {
|
||
const b = brainEl.getBoundingClientRect();
|
||
const vw = window.innerWidth, vh = window.innerHeight;
|
||
const cw = card.offsetWidth, ch = card.offsetHeight;
|
||
const bx = b.left + b.width / 2, by = b.top + b.height / 2;
|
||
const gap = 52;
|
||
let left;
|
||
if (bx + gap + cw < vw - 12) left = bx + gap;
|
||
else if (bx - gap - cw > 12) left = bx - gap - cw;
|
||
else left = Math.max(12, Math.min(vw - cw - 12, bx - cw / 2));
|
||
const top = Math.max(12, Math.min(vh - ch - 12, by - ch / 2));
|
||
card.style.left = Math.round(left) + 'px';
|
||
card.style.top = Math.round(top) + 'px';
|
||
const ly = Math.max(top + 24, Math.min(by, top + ch - 24));
|
||
const ax = left < bx ? left + cw : left;
|
||
line.setAttribute('x1', bx); line.setAttribute('y1', by);
|
||
line.setAttribute('x2', ax); line.setAttribute('y2', ly);
|
||
dot.setAttribute('cx', bx); dot.setAttribute('cy', by);
|
||
};
|
||
const MISSING_CONTRIBS = {
|
||
ci: { kicker: 'The missing piece · 20 years ago', title: 'Clinical Intelligence — the history, made visible',
|
||
body: `<p class="lead">Twenty years of clinical history, with no way to show it.</p>
|
||
<ul class="ai-pop-list"><li><b>What it is</b> — dashboards on the Unit's activity and its patients: volumes, incidence, outcomes</li>
|
||
<li><b>Who needed it</b> — management, audit, clinical research</li>
|
||
<li><b>Why it never existed</b> — the data was in prose; building dashboards on text was impractical</li></ul>
|
||
<div class="roi"><b class="k">The gap</b>No fast answer to “how are we doing?” — every question meant a manual query.</div>` },
|
||
dwh: { kicker: 'The missing piece · 20 years ago', title: 'Datawarehouse — one analysis-ready home',
|
||
body: `<p class="lead">One home for every record the Unit produces: text, structured fields, genetics, ECG.</p>
|
||
<ul class="ai-pop-list"><li><b>What it is</b> — a single schema joining all the sources, designed for analysis</li>
|
||
<li><b>Why it never existed</b> — each system kept its own silo; joining them meant hand-stitching exports</li></ul>
|
||
<div class="roi"><b class="k">The gap</b>Every research question started with manual exports — and ended in fragile spreadsheets.</div>` },
|
||
ml: { kicker: 'The missing piece · 20 years ago', title: 'ML-ready data — cohorts for research',
|
||
body: `<p class="lead">Tables shaped for statistics and model training: clean, wide, reproducible.</p>
|
||
<ul class="ai-pop-list"><li><b>What it is</b> — one row per patient, features beside outcomes, rebuildable at will</li>
|
||
<li><b>Why it never existed</b> — building it by hand from free text and separate silos took weeks</li></ul>
|
||
<div class="roi"><b class="k">The gap</b>Predictive research on the Unit's data stayed out of reach.</div>` },
|
||
portal: { kicker: 'The missing piece · 20 years ago', title: 'Professional Portal — the 360° view',
|
||
body: `<p class="lead">The 360° view of the patient: Cardioref and genetics finally in one place.</p>
|
||
<ul class="ai-pop-list"><li><b>What it is</b> — the grown-up Omics Portal: management, care and research on the same platform</li>
|
||
<li><b>Why it never existed</b> — the idea was there; the integrated data to feed it was not</li></ul>
|
||
<div class="roi"><b class="k">The gap</b>A single door for patients and clinicians — promised, never delivered.</div>` },
|
||
};
|
||
const START_CONTRIBS = {
|
||
cardioref: {
|
||
kicker: 'The starting point · 20 years ago',
|
||
title: 'Cardioref — the daily workhorse',
|
||
body: `
|
||
<p class="lead">For twenty years, the database running the Arrhythmology Unit.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Used for</b> — patient records, the Unit's daily activity, planning and logging of everything that was done</li>
|
||
<li><b>Good at it</b> — as a management database, it did (and still does) its job</li>
|
||
<li><b>The catch</b> — most clinical information lives in free text, outside the structured fields</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Problem</b>Almost unusable for research and data extraction: the knowledge is there — locked in prose.</div>`,
|
||
},
|
||
genetic: {
|
||
kicker: 'The starting point · 20 years ago',
|
||
title: 'Genetic data — an island',
|
||
body: `
|
||
<p class="lead">A dedicated system collecting the genetic results of our patients.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Used for</b> — storing genetic tests, variants and lab reports</li>
|
||
<li><b>The catch</b> — its only output is Excel sheets, with no integration into Cardioref</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Problem</b>Genotypes on one side, clinical records on the other — no way to join them.</div>`,
|
||
},
|
||
omics: {
|
||
kicker: 'The starting point · 20 years ago',
|
||
title: 'Omics Portal — born too early',
|
||
body: `
|
||
<p class="lead">A 360° patient-management system, still in its infancy.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Used for</b> — the first patient-management features beyond the hospital EHR</li>
|
||
<li><b>The catch</b> — embryonic functionality, and no integration with Cardioref</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Problem</b>A promising idea without a bridge: it could not see the clinical history stored elsewhere.</div>`,
|
||
},
|
||
ecg: {
|
||
kicker: 'The starting point · 20 years ago',
|
||
title: 'ECG subsystems — one per device',
|
||
body: `
|
||
<p class="lead">A constellation of ECG systems, each with its own software and its own silo.</p>
|
||
<ul class="ai-pop-list">
|
||
<li><b>Used for</b> — acquiring and storing ECG signals</li>
|
||
<li><b>The catch</b> — no machine-to-machine communication: the only export is a CSV file, on request</li>
|
||
</ul>
|
||
<div class="roi"><b class="k">Problem</b>Every ECG meant a manual download — automation was impossible by design.</div>`,
|
||
},
|
||
};
|
||
const openPop = (key, brainEl, dict) => {
|
||
const c = (dict || AI_CONTRIBS)[key];
|
||
if (!c) return;
|
||
const isAI = (dict || AI_CONTRIBS) === AI_CONTRIBS;
|
||
document.getElementById('aiPopBrain').style.display = isAI ? 'flex' : 'none';
|
||
document.getElementById('aiPopKicker').textContent = c.kicker || 'AI contribution';
|
||
document.getElementById('aiPopTitle').textContent = c.title;
|
||
document.getElementById('aiPopBody').innerHTML = c.body;
|
||
pop.classList.add('open');
|
||
if (brainEl) placeNear(brainEl);
|
||
};
|
||
document.addEventListener('click', (e) => {
|
||
const b = e.target.closest('.brain-btn[data-ai]');
|
||
if (b) { openPop(b.dataset.ai, b, AI_CONTRIBS); return; }
|
||
const s = e.target.closest('.isle[data-start]');
|
||
if (s) { openPop(s.dataset.start, s, START_CONTRIBS); return; }
|
||
const m = e.target.closest('.todo[data-missing]');
|
||
if (m) { openPop(m.dataset.missing, m, MISSING_CONTRIBS); return; }
|
||
if (e.target.closest('.ai-pop-x') || e.target === pop) closePop();
|
||
});
|
||
// Escape closes the popup first (capture), without toggling Reveal's overview
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape' && pop.classList.contains('open')) { closePop(); e.stopPropagation(); }
|
||
}, true);
|
||
</script>
|
||
</body>
|
||
</html>
|