Kapitel 4. Vertiefung: Elektronische Gesundheitsdaten

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Wir haben viel Zeit mit grundlegenden Ideen verbracht, daher bin ich mir sicher, dass du jetzt bereit bist, mit Daten zu spielen. In diesem Kapitel werden wir uns mit den Daten der elektronischen Gesundheitsakte beschäftigen. Wie wir in Kapitel 1 besprochen haben, verwende ich die Begriffeelektronische Gesundheitsakte und elektronische Patientenakte synonym. Für die Zwecke dieses Buches sind elektronische Gesundheitsakten die Daten, die von Krankenhäusern und Kliniken im Rahmen der Gesundheitsversorgung gesammelt werden. Einige der Hauptgründe dafür sind die Erfassung von Daten für die Rechnungsstellung, die Einhaltung von Gesetzen oder die Kommunikation mit anderen Pflegekräften. Das ist wichtig zu wissen, da es sich je nach Anwendungsfall auf die Qualität und Bedeutung der Daten auswirken kann.

Wir kehren nun zu einem Datensatz zurück, den ich in Kapitel 3 in der EHR-Fallstudie vorgestellt habe. Der MIMIC-Datensatz ist ein öffentlich zugänglicher Datensatz aus einer Krankenhaus-Intensivstation. Auch wenn die meisten ihn einfach als "MIMIC-Daten" bezeichnen, handelt es sich um den Medical Information Mart for Intensive Care-Datensatz. Obwohl dieser Datensatz eine spezielle Untergruppe von EHR-Daten darstellt, handelt es sich um reale Daten, die viele der Herausforderungen widerspiegeln, die wir als Branche bei der Arbeit mit realen Daten haben. Ich werde auch über den synthetischen Datensatz Synthea sprechen, obwohl wir nicht im Detail mit ihm arbeiten werden.

Bevor wir uns näher mit dem MIMIC-Datensatz befassen, möchte ich einen Schritt zurücktreten und einen Blick auf öffentlich zugängliche Daten im Allgemeinen werfen, insbesondere im Zusammenhang mit der De-Identifizierung und Anonymisierung.

Öffentlich zugängliche Daten

Von gesetzlichen Vorschriften über Datenschutzbedenken bis hin zur Komplexität der Daten gibt es viele Herausforderungen bei der Bereitstellung von Daten für die Allgemeinheit. In den Vereinigten Staaten ist eine gängige gesetzliche Hürde der Health Insurance Portability and Accountability Act (HIPAA), obwohl viele Bundesstaaten neben dem HIPAA auch eigene Vorschriften haben (z.B. California Consumer Privacy Act), die für das Gesundheitswesen spezifisch sein können oder auch nicht. In der Europäischen Union gibt es neben den länderspezifischen Vorschriften auch die Allgemeine Datenschutzverordnung (GDPR) .

Selbst wenn Unternehmen die gesetzlichen Bestimmungen vollständig einhalten, besteht immer noch das Risiko von Datenschutzverletzungen und den daraus resultierenden politischen und öffentlichkeitswirksamen Herausforderungen. So veröffentlichte Netflix im Jahr 2006 einen anonymisierten Datensatz, mit dem Forscher anschließend 68 % der Rezensenten wieder identifizieren konnten.1 Darüber hinaus gibt es moralische und ethische Überlegungen zum Schutz der Privatsphäre von Patienten im Gesundheitssystem.

In diesem Abschnitt werden wir zwei Datensätze besprechen. Der eine ist ein echter Datensatz, der einer De-Identifizierung unterzogen wurde. Der andere ist ein synthetischer Datensatz, der mit einer Vielzahl vonDatenmodellen erstellt wurde.

De-Identifizierung versus Anonymisierung

De-Identifizierung, Anonymisierung und Pseudo-Identifizierung sind verschiedene Ansätze, um Datenschutzbedenken in Datensätzen zu entkräften. Es gibt zwar allgemein anerkannte Definitionen, aber diese Begriffe werden auch durch Gesetze, die Rechtsprechung und den rechtlichen Rahmen definiert. Deshalb ist es wichtig, dass du genau verstehst, was von jedem Begriff verlangt wird.

Schauen wir uns nun den MIMIC-Datensatz etwas genauer an.

Medizinischer Informationsmarkt für Intensivpflege

Der MIMIC-Datensatz ist ein von den NIH finanziertes Projekt, das als Zusammenarbeit zwischen Harvard und dem MIT begann. Es begann 2003 als Multiparameter-Datensatz mit Daten von Intensivpatienten und wurde 2011 erstmals für die breite Öffentlichkeit freigegeben.

Seitdem wurde es mehrmals überarbeitet und ist derzeit als MIMIC-IV verfügbar. Jede der Hauptversionen (MIMIC-II, MIMIC-III, MIMIC-IV) ist im Wesentlichen ein anderer Datensatz mit sich überschneidenden Patientenpopulationen. Innerhalb jeder Version gibt es eine zusätzliche Versionierung - die letzte Version von MIMIC-III ist v1.4, die letzte Version von MIMIC-IV ist v1.0.

Die MIMIC-Daten sind eine großartige Datenquelle und enthalten alles von demografischen Daten über Medikamente und Labortests bis hin zu klinischen Notizen im Freitextformat. In einigen Fällen gibt es dazugehörige Datensätze mit Elektrokardiogrammen (EKG/EKG) und anderen bettseitigen Überwachungs- und physiologischen Wellenformdaten. Die MIMIC-Daten sind bei Datenwissenschaftlern beliebt, die digitale Biomarker entwickeln, klinische Algorithmen zur Verarbeitung natürlicher Sprache erstellen und sogar traditionelle klinische Forschung betreiben wollen.

In diesem Buch werden wir mit MIMIC-III arbeiten, da MIMIC-IV erst kürzlich veröffentlicht wurde. Ich werde einen kurzen Überblick über das Schema und einige Tabellen geben, auf die wir in diesem Buch Bezug nehmen werden. Auf der MIMIC-Website findest du eine ausführliche Dokumentation.

MIMIC-III-Schema

Das MIMIC-III-Schema ist ziemlich einfach und spiegelt die stationäre Pflege wider. In diesem Abschnitt werden wir einige Tabellen durchgehen und einige wichtige Überlegungen für diejenigen anstellen, die mit der stationären Patientenversorgung weniger vertraut sind.

Patienten

Der MIMIC-III-Datensatz ist ein Datensatz auf Patientenebene, und ist es am einfachsten, unsere Diskussion auf die TabellePATIENTS zu konzentrieren. Jeder einzelne Patient im Datensatz enthält einen Eintrag in PATIENTS, der auch grundlegende demografische Daten wie Geburts- und Sterbedatum und das genotypische Geschlecht (auch "Gender" genannt) des Patienten enthält. Zu beachten ist, dass mehrere Sterbedaten erfasst werden - das Datum aus dem Krankenhaussystem und das Datum aus der Sozialversicherungsdatenbank. Es gibt auch eine Spalte mit dem allgemeinen Sterbedatum, in der beide Daten zusammengeführt werden, wobei das Krankenhaussystem Vorrang hat.

Zulassungen

Als Nächstes wird jeder Patient mit Zeilen in der TabelleADMISSIONS verbunden, eine Zeile pro Krankenhausaufnahme. Diese Tabelle stammt aus und wird üblicherweise als ADT-System (Aufnahme-, Entlassungs- und Verlegungssystem) bezeichnet. Sie enthält das Datum und die Uhrzeit, zu der ein Patient in das Krankenhaus aufgenommen und wieder entlassen wurde, sowie den Ort der Aufnahme/Entlassung (z. B. wenn der Patient über die Notaufnahme aufgenommen, aber in eine Pflegeeinrichtung oder in ein Heim entlassen wurde). Diese Tabelle enthält auch andere Informationen, die normalerweise als demografische Daten gelten. Zum Beispiel sind Religion, Sprache, Familienstand usw. ebenfalls in dieser Tabelle enthalten. Daher ist es wichtig zu wissen, dass ein Patient mehrmals ins Krankenhaus eingeliefert werden kann und für jedes dieser Felder unterschiedliche Daten hat.

Aufenthalte auf der Intensivstation

Zu beachten ist, dass ein Patient, der ins Krankenhaus eingeliefert wird ( ), mehrmals auf die Intensivstation verlegt werden kann. Zum Beispiel kann ein Patient auf der medizinisch-chirurgischen Etage des Krankenhauses liegen, eine Sepsis entwickeln und auf die Intensivstation verlegt werden, sich erholen und zurück auf die ursprüngliche Station verlegt werden, sich dann aber wieder verschlechtern und erneut auf die Intensivstation verlegt werden. In diesem Fall handelt es sich um eine einzige Krankenhauseinweisung mit mehreren Aufenthalten auf der Intensivstation. Jeder ICU-Aufenthalt ist ein Eintrag in derTabelleICUSTAYS . Ähnlich wie in der Tabelle ADMISSIONS gibt es Zeitstempel, die angeben, wann der Patient auf die/von der Intensivstation verlegt wurde.

Wie in den meisten Krankenhäusern gibt es verschiedene Arten von Intensivstationen wie die medizinische Intensivstation, die chirurgische Intensivstation und die Koronarstation. Patienten können auch von einer Intensivstation auf eine andere verlegt werden; in dieser Tabelle werden die erste und die letzte Station erfasst, weitere Details zu Verlegungen sind in der Tabelle TRANSFERSzu finden. Abkürzungen und Beschreibungen der verschiedenen Stationen findest du in Tabelle 4-1.

Tabelle 4-1. MIMIC-III Pflegeeinheiten
Abkürzung Beschreibung

CCU

Koronare Pflegestation

CSRU

Aufwachstation für Herzchirurgie

MICU

Medizinische Intensivstation

NICU

Neugeborenen-Intensivstation

NWARD

Neugeborenenstation

SICU

Chirurgische Intensivstation

TSICU

Trauma/chirurgische Intensivstation

Überweisungen

Die TabelleTRANSFERS bezieht ihre Daten aus dem ADT-System und enthält eine Auflistung jeder Verlegung eines Patienten zwischen den Pflegestationen. Während die Tabelle ICUSTAYS die erste und die letzte Station erfasst, können in dieser Tabelle auch Zwischenstationen gefunden werden. Wenn ein Patient innerhalb von 24 Stunden nach einem vorherigen Aufenthalt auf der Intensivstation auf eine andere Station verlegt wird, wird dies als derselbe Aufenthalt betrachtet, so dass nur ein einziger Eintrag in der Tabelle ICUSTAY erscheint.

Dabei ist zu beachten, dass die Daten für Patienten aus den Jahren 2001 bis 2008 aus dem CareVue-System stammen, während die Daten von Patienten aus den Jahren 2008 bis 2012 aus dem MetaVision-System stammen. Es handelt sich um zwei unterschiedliche Systeme zur Erfassung von Intensivpatienten, was sich auf die zugrundeliegenden Daten auswirken kann (z. B. bei der Abfrage von Daten zu Eingangsereignissen).2

Verschreibungen

Nachdem wir nun die Tabellen mit den allgemeineren Daten über die Patienten und ihre Aufenthalte in dem Krankenhaus ein wenig verstanden haben, wollen wir uns nun mit den eher klinisch ausgerichteten Tabellen beschäftigen. Da wir einige Zeit damit verbringen werden, die Daten aus der elektronischen Patientenakte mit den Daten aus den Leistungsabrechnungen zu vergleichen, liegt es auf der Hand, dass wir uns auf die Daten zu den Medikamenten/Verschreibungen konzentrieren werden.

Die TabellePRESCRIPTIONS enthält Medikamentenbestellungen, die im Auftragserfassungssystem des Krankenhauses (auch bekannt als Computerized Provider Order Entry, CPOE) eingegeben wurden. Natürlich gibt es die typischen Spalten, die du erwarten würdest, wie z.B. Zeitstempel für den Beginn oder das Absetzen des Medikaments oder den Aufenthalt auf der Intensivstation oder die Krankenhauseinweisung, mit der die Bestellung verbunden ist.

Die interessanteren Spalten sind jedoch diejenigen, die den Namen des Medikaments, die Dosierungsinformationen und Links zu Standard-Codierungssystemen enthalten. Die Spalte PROD_STRENGTH enthält zum Beispiel eine Freitext-Zeichenkette, die Informationen enthält, die typischerweise auf Produktverpackungen zu finden sind (z.B. "25mcg/0.5mL Vial" oder "10mg Suppository"). Außerdem gibt es zusätzliche Spalten DOSE_VAL_RXund DOSE_UNIT_RX, die diese Informationen zur einfacheren Berechnung aufschlüsseln. Eine weitere interessante Spalte ist ROUTE, in der der Verabreichungsweg erfasst wird (z. B. "PO" für orale Medikamente oder "SC" für Injektionen unter die Haut).

Dosis vs. Dosierung

Die Dosis ist die Menge eines Medikaments, die auf einmal eingenommen wird. Eine übliche Dosis für Ibuprofen ist zum Beispiel 200 mg, obwohl es auf Rezept auch in einer 800-mg-Dosis erhältlich ist.

Die Dosierung enthält weitere Informationen wie die Menge, die Anzahl der Pillen, die Häufigkeit und die Dauer.

Eine der Herausforderungen, die wir bei der Arbeit mit Daten wie Medikamenten oft haben, ist die Verknüpfung mit anderen Daten, z. B. mit Ansprüchen. Diese Tabelle enthält auch NDC- und GSN-Codes, um diese Verknüpfungen zu erleichtern. NDC-Codes sind nationale Arzneimittelcodes, die in den Vereinigten Staaten verwendet werden. Dabei ist zu beachten, dass der NDC-Code eine Kombination aus Etikettierer (Hersteller, Umpacker, Umetikettierer, Vertreiber), Produktinformation (Stärke, Dosierung und Formulierung) und Verpackungsinformation (Größe und Art derVerpackung) ist.

Infolgedessen bietet der NDC-Code für viele Anwendungsfälle eine zu große Granularität. Angenommen, wir versuchen, unseren EHR-Datensatz mit dem FDA Adverse Event Reporting System (FAERS)zu verknüpfen, um herauszufinden, ob bestimmte Medikamentenkombinationen mit der Aufenthaltsdauer oder der Verlegung auf die Intensivstation korrelieren, wobei der Schwerpunkt auf Paracetamol liegt.

Paracetamol ist als Wirkstoff in vielen Medikamenten enthalten und wird daher unter vielen NDC-Codes versteckt. An dieser Stelle kann die UMLS sehr hilfreich sein. Sie ist zwar nicht perfekt, aber sie enthält viele Zuordnungen und Beziehungen, die uns helfen können, alle NDC-Codes zu finden, die Paracetamol als Wirkstoff enthalten. Dann können wir diese Codes mit den in dieser Tabelle enthaltenen NDC-Codes verknüpfen.

Ablauf der Ereignisse

Ähnlich wie die Tabelle PRESCRIPTIONS erfasst auch der Datensatz die Eingriffe, die für Patienten angeordnet werden. Obwohl es zwei verschiedene Systeme gibt (CareVue und MetaVision), gibt es nur eine einzige Tabelle, die Verfahrensereignisse enthält: dieTabellePROCEDUREEVENTS_MV .

Diese Tabelle enthält eine Auflistung der verschiedenen Verfahren, denen sich ein Patient unterziehen kann, vom Anlegen einer Infusion über die Bildgebung bis hin zum Verbandswechsel. Sie ist so aufgebaut, dass sie alle Eingriffe für alle Patienten über alle Aufnahmen und Aufenthalte auf der Intensivstation hinweg enthält. Daher ist sie stark auf Primär-/Fremdschlüsselbeziehungen mit der Tabelle D_ITEMS angewiesen.

Es gibt auch eine ganze Reihe von Feldern, die zusätzlichen Kontext und Informationen zu einem bestimmten Verfahren liefern. Einige davon sind in der Regel selbsterklärend (z. B. Start-/Endzeit), die meisten erfordern jedoch Kenntnisse der klinischen Praxis oder der Anatomie und Physiologie.

D_ITEMS

Es gibt mehrere Tabellen die mit D_ beginnen, eine davon ist D_ITEMS. Diese Tabellen werden als Definitionstabellen bezeichnet und speichern Definitionen von Codesätzen (allerdings nicht unbedingt standardisierte Codes). D_ITEMS speichert Definitionen für Elemente in den Datenbanken CareVue oder MetaVision. Die Spalte DBSOURCE enthält eine Zeichenfolge, die angibt, welches System.

Da diese Tabelle mit einer Vielzahl verschiedener Ereignistabellen verknüpft ist, ist sie eine sehr allgemeine Tabelle und enthält zwei Spalten(UNITNAME und PARAM_TYPE), die zur Interpretation der spezifischen Werte in der entsprechenden Ereignistabelle verwendet werden. In einigen Fällen wird der Name der Einheit auch in der verknüpften Tabelle erfasst (z. B. wird in der Spalte VALUEUOM in PROCEDUREEVENTS_MV auch die Maßeinheit erfasst).

Es ist zu beachten, dass diese Tabelle zwar den Anschein erweckt, ein kontrolliertes Vokabular zu enthalten, dies aber nicht der Fall ist! Es kann sein, dass es in dieser Tabelle mehrere Einträge (d.h. Zeilen) gibt, die dasselbe Konzept erfassen, aber Schreibfehler oder Synonyme enthalten. Das liegt daran, dass die Daten aus zwei verschiedenen Systemen stammen (CareVue und MetaVision) und auch daran, dass in CareVue Freitexte eingegeben wurden.

Dies ist ein Bereich, in dem Graphdatenbanken glänzen, wenn wir daran arbeiten, synonyme oder gleichwertige Konzepte im gesamten Datensatz zu verknüpfen. Die Items mit denITEMIDs211 und 220045 stehen zum Beispiel beide für "Herzfrequenz" aus CareVue und MetaVision. In einem SQL-basierten System würde die Verknüpfung auf der Ebene der SQL-Abfrage erfolgen. Dies erfordert, dass die Dateningenieure und -wissenschaftler dies in ihren Abfragen verfolgen und dokumentieren. In einem graphenbasierten System können wir sie ganz einfach mit einer Kante verknüpfen, die die Synonymie oder Äquivalenz erfasst, so dass auch andere von der Zuordnung profitieren können.

D_CPT, D_ICD_DIAGNOSES, D_ICD_PROCEDURES, und D_LABITEMS

Wie bei D_ITEMS enthalten auch die Tabellen D_CPT, D_ICD_DIAGNOSES, D_ICD_PROCEDURES undD_LABITEMS Definitionen, auf die in anderen Tabellen verwiesen wird. Sie enthalten jedoch alle Definitionen, die mit kontrollierten Vokabularen übereinstimmen. Die Spalten in jeder Tabelle sind auf die Nuancen des zugehörigen kontrollierten Vokabulars zugeschnitten.

Die TabelleD_CPT enthält Definitionen für Positionen, die Teil der Current Procedural Terminology (CPT) sind, die eine Reihe von Codes ist, die von der American Medical Association gepflegt werden. CPT-Codes werden in erster Linie für die Abrechnung von Leistungen zwischen Leistungserbringern und Kostenträgern (sowohl öffentlichen als auch privaten) verwendet.

Die TabelleD_ICD_DIAGNOSES enthält ICD-9 (nicht ICD-10) Codes, die sich auf die Diagnosen der Patienten beziehen. Da der MIMIC-III-Datensatz nur Daten aus den Jahren 2001-2012 enthält, sind darin keine ICD-10-Codes enthalten, da das Mandat zur Umstellung von ICD-9 auf ICD-10 erst2015 in Kraft trat.

Ähnlich wie D_ICD_DIAGNOSES enthält die TabelleD_ICD_PROCEDURES ICD-9 Codes, die sich auf Krankenhausverfahren vor 2015 beziehen.

CPT- versus ICD-9-Verfahrenscodes

Sowohl die CPT- als auch die ICD-9- (und ICD-10-) Prozedurencodes erfassen medizinische Prozeduren, aber vielleicht fragst du dich, was der Unterschied ist.

CPT-Codes werden hauptsächlich für die Abrechnung von ärztlichen Leistungen verwendet, während ICD-Verfahrenscodes für die Abrechnung von Krankenhausleistungen verwendet werden. Obwohl es nicht allgemein bekannt ist, werden ärztliche Leistungen und Krankenhausleistungen getrennt abgerechnet und verwenden unterschiedliche Codesätze, um den Erstattungsprozess zu verwalten.

Wenn du schon einmal mit der Abrechnung eines Krankenhausaufenthalts zu tun hattest, ist das ein Grund, warum es so verwirrend sein kann! Du bekommst eine Reihe verschiedener Rechnungen, weil keine einzige Organisation hinter allen erbrachten Leistungen steht. Eine Ausnahme bildet das integrierte Gesundheitssystem, bei dem die Mitarbeiter (Ärzte, Krankenschwestern, Apotheker usw.) und das Krankenhaus selbst alle Teil derselben Organisation sind.

Während der Umstellung der Branche (in den USA) auf ICD-10 für die Abrechnung und Erstattung im Jahr 2015 gab es ein Zeitfenster, in dem viele Einrichtungen im Rahmen der Umstellung eine "Doppelkodierung" vornahmen. Während dieser Zeit verwendeten die Einrichtungen oft gleichzeitig ICD-9- und ICD-10-Codes.

Allerdings enthalten nicht alle Datensätze, die sich über das Einführungsdatum 2015 erstrecken, beide Kodesätze. Bei der Arbeit mit Datensätzen, die sich über das Einführungsdatum erstrecken, müssen entweder alle Codes aus der Zeit vor 2015 auf ihre ICD-10-Entsprechung oder alle Codes aus der Zeit nach 2015 auf ihre ICD-9-Entsprechung abgebildet werden. Dies wird oft durch die Verwendung von "Crosswalks" erreicht, einer Tabelle mit Zuordnungen von einem Kodierungssystem zum anderen.

Abbildung von ICD-9 und ICD-10

Es gibt viel mehr ICD-10-Codes als ICD-9-Codes, daher liegt die Vermutung nahe, dass die ICD-10-Codes granularer und detaillierter sind. Es kann zum Beispiel zwei verschiedene Codes geben, um die Lateralität (z. B. linker Arm oder rechter Arm) einer bestimmten Diagnose zu behandeln. Die natürliche Schlussfolgerung ist, dass ein bestimmter ICD-9-Code einem oder mehreren ICD-10-Codes entspricht (1:1 oder 1:viele Zuordnungen). Das ist jedoch nicht immer der Fall. In Anbetracht des Verwendungszwecks der ICD gab es einige Codes, bei denen mehrere ICD-9-Codes zu einem einzigen ICD-10-Code zusammengefasst wurden.

Zum Beispiel werden die ICD-9-Codes 493.00 (Extrinsisches Asthma, nicht spezifiziert) und 493.10 (Intrinsisches Asthma, nicht spezifiziert) auf einen einzigen ICD-10-Code J45.50 (Schweres persistierendes Asthma, unkompliziert) abgebildet. Das liegt daran, dass die ICD-9 Asthma-Codes nach intrinsischem und extrinsischem Asthma geordnet sind, während die ICD-10 Asthma-Codes nach Schweregrad geordnet sind.3,4

Die letzte Tabelle, die wir in diesem Abschnitt behandeln , ist D_LABITEMSSie enthält Definitionen für Labor- und andere Beobachtungstests und ist mit LOINC-Codes verknüpft. Zwar wurden die meisten Begriffe aus dem Laborsystem des Krankenhauses den LOINC-Codes zugeordnet, aber nicht alle.

Das war ein Schnelldurchlauf durch das Schema und sollte eher als Orientierung dienen. Je mehr Zeit du mit den Daten verbringst, desto intuitiver werden sie. Ein weiteres Element der Erfahrung ist die Fähigkeit zu erkennen, wann ein Datensatz für einen bestimmten Anwendungsfall nicht geeignet ist. Auch wenn wir begeistert sind, wenn wir die Daten in die Hände bekommen, ist es wichtig, dass wir die Grenzen kennen. Auch wenn die MIMIC-Datensätze sehr wertvoll sind, sollten wir uns ein wenig Zeit nehmen, um uns einige Einschränkungen anzusehen.

Einschränkungen

Die größte Einschränkung des MIMIC-III-Datensatzes besteht darin, dass er sich auf Patienten konzentriert, die während ihrer Aufnahme einen Aufenthalt auf der Intensivstation hatten. Das hat zur Folge, dass der Datensatz eine große Verzerrung in Richtung der Patienten mit höherem Schweregrad aufweist (d.h. Patienten mit komplexeren medizinischen Situationen). Alle Erkenntnisse, die aus diesem Datensatz gewonnen werden, gelten also wahrscheinlich nicht für die Mehrheit der Bevölkerung.

Nehmen wir zum Beispiel an, wir wollten Diabetiker in diesem Datensatz untersuchen und den Blutzuckerspiegel mit Krankenhauseinweisungen oder unerwünschten Ereignissen im Zusammenhang mit Insulin in Verbindung bringen. Es wäre schwierig, Störfaktoren wie Wechselwirkungen mit stationären Medikamenten wie Beruhigungsmitteln auszuschließen, die normalerweise nicht außerhalb des Krankenhauses verabreicht werden. Wie bei jedem Datensatz ist es also wichtig, über mögliche Verzerrungen nachzudenken. Die Verzerrungen in diesem Datensatz könnten jedoch tief im klinischen Kontext verborgen sein und sonst nicht auffallen.

Dieser Datensatz wird zum Beispiel in einem akademischen medizinischen Zentrum erfasst, das mit einer hochrangigen medizinischen Fakultät verbunden ist. Die Daten sind tief in den klinischen Arbeitsabläufen verankert und stellen eine direkte Verbindung zu Spitzenforschungsergebnissen her, die in den Krankenhäusern der Gemeinde vielleicht erst nach Jahren ankommen. Selbst wenn du an einem stationären Analyseprojekt arbeitest und deine Patientenpopulationen mit denen im MIMIC-Datensatz übereinstimmen, musst du dir überlegen, ob deine klinischen Praktiken eng genug aufeinander abgestimmt sind.

Manchmal brauchst du einen Datensatz, den du frei teilen kannst und bist nicht daran interessiert, tatsächliche Erkenntnisse zu gewinnen. Synthetische Daten sind ein aktiver Forschungsbereich und haben das Potenzial, uns dabei zu helfen, die Bedenken bezüglich des Datenschutzes zu überwinden, die Forscher und Innovatoren oft einschränken. Im nächsten Abschnitt werden wir über Synthea sprechen, einen der ersten synthetischen Datensätze, der der Öffentlichkeit zugänglich gemacht wurde.

Synthea

Ein weiterer gängiger Datensatz, der vor allem im Zusammenhang mit Data Engineering verwendet wird, ist der Synthea-Datensatz. Es handelt sich um einen vollständig synthetischen Datensatz, d.h. er enthält keine tatsächlichen Patientendaten. Das erleichtert die Arbeit mit den Daten, vor allem wenn du Daten in verschiedenen Formaten oder Datenmodellen in Unit- und Integrationstests einbeziehen willst.

Das Projekt Synthea ist ein Open-Source-Datensimulator/-generator. Das heißt, es erzeugt realistische Gesundheitsdaten (im Gegensatz zu Daten, die zwar syntaktisch korrekt, aber semantisch bedeutungslos sind). Es gibt jedoch auch ein paar vorgenerierte Datensätze, die du von der Synthea-Website herunterladen kannst.

Synthea ist ein leistungsfähiges Framework, das einen modularen Ansatz für die Generierung verschiedener Aspekte von Gesundheitsdaten verwendet. So können Dateningenieure und Wissenschaftler das System bei Bedarf an die lokale Patientenpopulation anpassen. Die Dokumentation bietet gute Beispiele und Anleitungen für die Erstellung eigener Module.

Wie bei jedem Datensatz ist eines der ersten Dinge, die wir beachten müssen, das Schema oder das Datenwörterbuch und wie gut es mit unseren Bedürfnissen übereinstimmt. Werfen wir also einen Blick auf das Synthea-Schema.

Schema

Anders als der MIMIC-Datensatz sind die Daten von SyntheticMass in einer Vielzahl von Datenmodellen/-formaten verfügbar. Das macht SyntheticMass zu einer sehr reichhaltigen Quelle für realistische Daten, vor allem beim Testen von Data-Engineering- und Mapping-Pipelines. Wie bei jedem Datensatz gibt es jedoch Kompromisse zwischen den verschiedenen vorgenerierten Datensätzen, so dass du möglicherweise einen benutzerdefinierten Datensatz erstellen musst, der deinen Bedürfnissen und Anforderungen entspricht.

Einige der Datensätze sind sowohl im CSV- als auch im FHIR-Format verfügbar und eignen sich daher hervorragend zum Testen von Data-Engineering-Pipelines, die nach/von FHIR konvertieren. Es ist jedoch wichtig zu beachten, dass es bei solchen Tests eher um die Technologie deiner Pipeline geht. Da es sich um synthetische Tests handelt, helfen sie dir nicht, deine Datenmappings oder semantischen Harmonisierungsaufgaben zu validieren.

Die CSV-Version des Datensatzes ist eine normalisierte, relationale Struktur, so dass jede CSV-Datei als separate Tabelle in eine relationale Datenbank geladen werden kann.Universell eindeutige Bezeichner (UUIDs) werden verwendet, um zwischen den Dateien zu verknüpfen.

Es gibt jeweils eine CSV-Datei für die folgenden Punkte:

  • Allergien

  • Pflegepläne

  • Ansprüche

  • Schaden-Transaktionen

  • Bedingungen

  • Geräte

  • Begegnungen

  • Bildgebende Studien

  • Impfungen

  • Medikamente

  • Beobachtungen

  • Organisationen

  • Patienten

  • Übergänge zwischen den Kostenträgern

  • Zahler

  • Abläufe

  • Anbieter

  • Vorräte

In jeder CSV-Datei ist die erste Zeile eine Kopfzeile, die die einzelnen Spalten identifiziert. Die Datei conditions.csv enthält zum Beispiel die folgende Kopfzeile:

START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION

In der FHIR-Version des generierten Datensatzes entspricht jede JSON-Datei einem einzelnen Patienten und folgt dem FHIR-Schema. Wir werden dies später in diesem Kapitel im Abschnitt "Fast Health Interoperability Resources" besprechen .

Obwohl ich bereits einige mögliche Einschränkungen der Synthea-Daten erwähnt habe, möchte ich kurz auf die offensichtlichste Einschränkung bei der Arbeit mit Synthea eingehen.

Einschränkungen

Die größte Einschränkung des Synthea-Datensatzes ist natürlich, dass es sich nicht um einen echten Datensatz handelt. Die Module, mit denen die verschiedenen Aspekte generiert werden, sind einstellbar und spiegeln oft unser aktuelles Wissen wider (hauptsächlich aus Beobachtungsstudien). Dies setzt jedoch voraus, dass die zugrunde liegende Forschung, die zur Abstimmung der Module verwendet wird, korrekt ist und die Patientenpopulation widerspiegelt, die wir untersuchen wollen. Da sich diese Beobachtungsstudien in der Regel auf deskriptive Statistiken der beobachteten Variablen konzentrieren, spiegeln sie möglicherweise nicht die übergeordneten Wechselwirkungen zwischen den Variablen wider (vor allem, wenn diese übergeordneten Wechselwirkungen noch nicht verstanden werden oder den Forschern noch nicht einmal bekannt sind).

Wir haben uns die MIMIC-III- und Synthea-Daten angesehen, die jeweils ihr eigenes Datenmodell enthalten. Wenn jede Datenquelle ihr eigenes Datenmodell hat, müssen wir unsere Datenpipelines anpassen oder neu aufbauen, um das neue Datenmodell zu berücksichtigen. Im nächsten Abschnitt werden wir uns mit zwei allgemein diskutierten Datenmodellen für die Arbeit mit EHR-Daten beschäftigen. Beide versuchen, auf ein gemeinsames Datenmodell hinzuarbeiten, wobei sich das eine auf die Speicherung von Daten für Analysen konzentriert ( ), während sich das andere auf die Kommunikation und den Transport von Daten konzentriert.

Datenmodelle

Wenn wir mit Daten jeglicher Art arbeiten, ist eine der ersten Überlegungen das Datenmodell. Meistens haben wir als Datenwissenschaftler/innen nicht viel Einfluss darauf, welches Datenmodell verwendet wird, denn diese Entscheidung wird viel weiter vorne getroffen. Im Falle von Gesundheitsdaten wird das Datenmodell oft vom Hersteller der elektronischen Patientenakte oder dem Team, das das klinische Data Warehouse entwickelt hat, festgelegt. Wenn wir Zugang zu Datensätzen in den Biowissenschaften erhalten, sind wir oft den Datenanbietern und dem von ihnen gewählten Datenmodell ausgeliefert, das häufig ein proprietäres Modell ist. Unabhängig davon ist es wichtig, dass wir uns die zugrunde liegenden Ziele oder Absichten des Datenmodells ansehen, die wir im Folgenden erörtern werden.

Ziele

Es gibt einen langsamen, aber zunehmenden Trend, sich auf die Verwendung von "gemeinsamen" Datenmodellen zu konzentrieren. Die Idee ist, ein Ökosystem von Tools und Datenquellen zu schaffen, die alle auf einem einzigen Datenmodell basieren, damit das Rad nicht ständig neu erfunden werden muss. Eine der besten Analogien, die ich je gehört habe, ist, dass wir alle nach Erkenntnissen dürsten, aber wir alle unsere eigenen Brunnen zu denselben Datenquellen graben.

Wir werden ein paar Beispiele für Projekte vorstellen, die dies versuchen, aber vorher ist es wichtig zu wissen, dass es nie ein Patentrezept gibt. Wenn du mit den meisten Daten- und Informationsarchitekten sprichst (mich eingeschlossen), wird einer der ersten Punkte sein, dass das Modell vom Anwendungsfall abhängt - woher kommen die Daten und welche Art von Fragen werden an die Daten gestellt? Je nach denAnforderungen des Projekts kann das Datenmodell auch von anderen Anforderungen abhängen, z. B. von der Schreiblatenz (wie bei transaktionalen, relationalen Datenmodellen häufig der Fall).

Die größte Herausforderung bei dem Versuch, ein gemeinsames Datenmodell für Gesundheitsdaten (insbesondere RWD) zu erstellen, sind die unterschiedlichen Daten, die je nach Krankheitsbereich erfasst werden müssen. In der Onkologie müssen wir zum Beispiel das Staging eines Patienten (von dem es bereits zwei Optionen gibt, das TNM-Staging und das Gruppen-Staging), die Morphologie und Histologie des Krebses und den Körperteil bzw. die Körperstelle erfassen.

Daten zur Krebsstadienbestimmung bereiten denjenigen, die in der Onkologie mit Daten arbeiten, häufig Kopfzerbrechen. Gleich zu Beginn besteht die Notwendigkeit, "Gruppen-Staging" und "TNM-Staging" zu harmonisieren, zwei unterschiedliche Ansätze zur Bestimmung des Krebsstadiums eines Patienten. Das Gruppen-Staging ist die allgemein anerkannte Methode und besteht nur aus einer einzigen Zahl (z.B. Stadium 1, 2, 3, ...). Sie erfasst das Vorhandensein von Krebs, die Größe des Tumors und wie weit er sich auf nahe gelegenes Gewebe oder andere Teile des Körpers ausgebreitet hat.

Angesichts unseres zunehmenden Verständnisses von Krebserkrankungen gibt es eine weitere Form der Stadieneinteilung, das TNM-Staging:

T

Primärtumor - Größe oder Ausmaß des Haupttumors

N

Regionale Lymphknoten - ob Lymphknoten betroffen sind und, wenn ja, die Anzahl und Lage

M

Metastasen - ob sich der Krebs auf andere Teile des Körpers ausgebreitet hat

Zusätzlich zu den verschiedenen Arten der Stadieneinteilung gibt es noch die AJCC-Richtlinien für die TNM-Einteilung. Das AJCC ist das American Joint Committee on Cancer und hat Richtlinien für die Krebseinteilung entwickelt, die von Klinikern und Wissenschaftlern verwendet werden. Während sie in der Vergangenheit in Form von Ausgaben veröffentlicht wurden, ist das AJCC kürzlich (ab 2021) dazu übergegangen, die Richtlinien zu versionieren.

Während die Staging-Richtlinien ursprünglich als Richtlinien für den menschlichen Konsum gedacht waren, werden sie nun weiterentwickelt, um besser berechenbar zu werden, einschließlich API-Zugang und einer Verlagerung weg von der Veröffentlichung von "Editionen" hin zu "Versionen".

Dabei sind die neuesten Entwicklungen bei der genetischen Sequenzierung und den Biomarker-Daten noch gar nicht berücksichtigt! All diese Datenelemente sind spezifisch für solide Tumore und lassen sich nicht auf andere Krankheitsbereiche übertragen. Wie können wir also ein gemeinsames Datenmodell erstellen, das es einem Krankenhaus ermöglicht, mit Daten aus allen Krankheitsbereichen und medizinischenFachgebieten zu arbeiten?

Warum gibt es trotz der (manchmal überwältigenden!) Menge an Nuancen in der RWD immer noch einen solchen Druck für gemeinsame Datenmodelle? Wir sehen zum Beispiel, dass Akademiker,Biopharmaunternehmen und Datenanbieter viel Zeit und Geld in solche Bemühungen investieren.5

Es gibt zwar viele Unterschiede zwischen Krebs und Neurologie oder zwischen deskriptiven Statistiken zur Evidenzgewinnung und prädiktiven Analysemodellen zur Entscheidungsunterstützung, aber es gibt auch viele Überschneidungen. Unabhängig von der Art der Analyse besteht einer der ersten Schritte darin, eine Patientenkohorte zu definieren und eine explorative Analyse auf hohem Niveau durchzuführen. Bevor wir uns die Mühe machen, in einen Datensatz einzutauchen, wollen wir zunächst ungefähr wissen, wie viele Patienten unsere Einschluss-/Ausschlusskriterien erfüllen oder wie die Verteilung der Schlüsselvariablen und Datenelemente aussehen könnte.

Vielleicht können wir also ein gemeinsames Datenmodell verwenden, um diese frühe Phase von Analyseprojekten zu erleichtern. Sobald die Datenwissenschaftler mit der von ihnen definierten Kohorte zufrieden sind, können sie dieselbe Teilmenge von Patienten aus dem ursprünglichen Datensatz extrahieren. So können wir die Identifizierung des geeigneten Datensatzes und der Patientenkohorten beschleunigen und den Datenwissenschaftlern gleichzeitig die volle Tiefe und Nuancierung des ursprünglichen Datensatzes zur Verfügung stellen.

Werfen wir also einen Blick auf zwei Beispiele von gemeinsamen Datenmodellen, die sich auf allen Ebenen des Gesundheitswesens durchsetzen.

Beispiele für Datenmodelle

Es gibt eine ganze Reihe "gängiger" Datenmodelle, die meisten stammen aus dem akademischen Bereich des Gesundheitswesens. Es gibt jedoch kein einheitliches, maßgebliches Gremium, so dass die Einführung organisch und von unten nach oben erfolgt. Wir werden zwei der am häufigsten diskutierten Datenmodelle (neben vielen anderen) vorstellen, die sowohl in der akademischen Medizin als auch in der Industrie schnell an Beliebtheit gewinnen.

Observational Health Data Sciences and Informatics (OHDSI) OMOP-Datenmodell

OHDSI (ausgesprochen "Odyssee") ist ein Verbund, der zentral von einer Forschungsgruppe an der Columbia University koordiniert wird, aber zahlreiche Mitarbeiter und Sponsoren aus der Industrie hat. Neben der Bereitstellung einer Community und eines Forums gehören zu den konkreten Artefakten auch die Spezifikationen für ein gemeinsames Datenmodell (OMOP, ausgesprochen "oh mop") sowie Tools für die Abbildung und Interaktion mit den Daten und eine kuratierte Reihe von Terminologien.

Die OHDSI-Dokumentation ist recht gut, einschließlich Materialien von Dritten wie der European Health Data Evidence Network (EHDEN) Academy. Mein Ziel ist es nicht, dich zu einem Experten für OMOP zu machen, sondern dir eine grundlegende Einführung zu geben, während wir FHIR und andere Ansätze vergleichen und gegenüberstellen. Wenn du tiefer in die Welt von OHDSI eintauchen möchtest, solltest du dir das Book of OHDSI und andere OHDSI-Ressourcen ansehen.

Wir werden den allgemeinen Ansatz diskutieren, den OHDSI mit OMOP verfolgt hat, sowie Überlegungen aus unserer Perspektive als Dateningenieure und Datenwissenschaftler, insbesondere in Bezug auf die verschiedenen Arten von Datenbanken, die wir vergleichen.

Arbeiten mit OMOP

Die neueste Version des OMOP CDM ist v5.4 und wurde erst im September 2021 veröffentlicht. Wenn du auf in der OMOP-Dokumentation stöberst, wirst du auch die Version 6.0 finden. Obwohl dies eine "freigegebene" Version ist, empfiehlt OHDSI ausdrücklich, dass neue Projekte weiterhin den 5.x-Zweig des CDM verwenden. Die Version 6.0 bringt einige grundlegende Änderungen mit sich, und die derzeit veröffentlichten Tools (sowie die Community insgesamt) haben noch nicht damit begonnen, die Version 6.0 zu unterstützen.

Wie du in Abbildung 4-1 sehen kannst, ist der CDM patientenzentriert, wobei alles aus der Tabelle Person stammt. Ähnlich wie im Schema des MIMIC-Datensatzes werden die Patienten direkt mit den Besuchen in Verbindung gebracht. Da OMOP sich nicht auf stationäre Daten bezieht, kann ein Besuch sowohl stationär als auch ambulant sein. Danach gibt es Tabellen, die den Patienten mit einer Vielzahl von Beobachtungsdaten verknüpfen, von Zuständen (d.h. Diagnosen) über Medikamente bis hin zu Verfahren usw.

Eine Sache, die dir auffallen wird, ist, dass die Tabellen in verschiedene Kategorien eingeteilt sind:

  • Klinische Daten

  • Daten zum Gesundheitssystem

  • Gesundheitsökonomische Daten

  • Abgeleitete Elemente

  • Metadaten

  • Vokabulare

Neben dem klinischen Fokus, den wir erwarten, wurde OMOP auch entwickelt, um Daten aus dem breiteren Ökosystem des Gesundheitswesens zu integrieren, vor allem Daten aus der Leistungsabrechnung sowie Daten zu einem einzelnen Patienten von mehreren verschiedenen Anbietern.

Schließlich wirst du auch feststellen, dass fast jede klinische Tabelle Verbindungen zu einer Tabelle Concept hat. OMOP ist eng mit der Verwendung von kontrollierten Vokabularen verbunden, insbesondere mit den Standardterminologien der Industrie wie SNOMED-CT und LOINC. Eine vollständige Liste der unterstützten Terminologien findest du in der Athena-Dokumentation. Da viele Terminologien eine Vielzahl von Beziehungen unterstützen, die über eine einfache Hierarchie hinausgehen, können wir diese Beziehungen in den anderen Vokabeltabellen in einem SQL-Format erfassen. Das macht die Arbeit mit diesen Beziehungen äußerst bequem, da sie direkt in die SQL-Abfrage eingebettet werden können (anstatt mit , einem separatenTerminologieverwaltungsdienst, zu arbeiten). Der Kompromiss besteht jedoch darin, dass die SQL-Abfragen selbst ziemlich komplex und schwer zu lesen sein können.

OMOP CDM v5.4
Abbildung 4-1. OMOP CDM v5.4

Ressourcen für schnelle Interoperabilität im Gesundheitswesen

Ein weiteres gängiges Datenmodell, das auf immer mehr an Bedeutung gewinnt, ist FHIR (ausgesprochen "Fire"). Es als "Datenmodell" zu bezeichnen, ist jedoch ein wenig irreführend. Wenn wir von Datenmodellen sprechen, tun wir das normalerweise im Zusammenhang mit der Speicherung und Persistenz von Daten, also in einer Datenbank. FHIR wurde jedoch nicht als Speichermodell entwickelt. Da es jedoch sowohl als JSON als auch als XML vorliegt, haben viele damit begonnen, das JSON direkt in einer dokumentenorientierten Datenbank oder einer anderen Datenbank zu speichern, die JSON-Dokumente nativ unterstützt (z. B. PostgreSQL mit JSON-Unterstützung).

FHIR wurde als Interoperabilitätsstandard entwickelt, um den Austausch von Informationen zwischen verschiedenen klinischen Informationssystemen zu erleichtern. Das hat zur Folge, dass er sehr umfangreich ist und Daten oft an mehreren Stellen dupliziert, anstatt sie effizient zu normalisieren. Andererseits bietet er als Interoperabilitätsstandard einige leistungsstarke Funktionen wie Implementierungsleitfäden und Profile.

Ressourcen

Das Herzstück von FHIR ist die Ressource, eine Definition der Struktur und des Inhalts einer Datenmenge, die zwischen zwei Informationssystemen im Gesundheitswesen ausgetauscht werden soll. In vielerlei Hinsicht ist eine Ressource mit einer Tabelle in einem herkömmlichen Datenbankschema vergleichbar. So gibt es zum Beispiel eine Ressource Patient, mit der eineObservation verknüpft werden kann, ähnlich wie die Tabellen Person und Observation in OMOP.

Für jedes Schlüssel-Wert-Paar innerhalb einer Ressource gibt es eine strenge Definition der Kardinalität, des Datentyps und der Flags, die festlegen, wie die Ressource definiert und verwendet werden soll.

Ressourcendefinitionen sind in der Regel recht allgemein gehalten und umfassen nur das absolute Minimum an Spezifität. Das macht FHIR zu einem hervorragenden Ausgangspunkt, den die Nutzer erweitern und zusätzliche Einschränkungen hinzufügen können. Hierfür werden wirProfile verwenden.

Profile

Wenn wir uns also FHIR-Ressourcen ansehen, sehen wir etwas , das den typischen Schemadefinitionen eines Datenmodells ähnelt. FHIR-Profile sind der Punkt, an dem sich FHIR als Standard für Interoperabilität und Informationsaustausch von einem Daten- oder Informationsmodell abhebt.

Profile sind eine Möglichkeit für bestimmte Implementierungen, einen spezifischen Satz von Ressourcendefinitionen zu erstellen und zu veröffentlichen, der Einschränkungen oder Erweiterungen des Basissatzes von Ressourcen enthält. Das Hauptmerkmal von FHIR ist, dass diese Profile veröffentlicht werden und von Systemen in einer berechenbaren Weise genutzt werden können. Dadurch können Implementierungen in elektronischen Gesundheitsakten oder anderen Softwaresystemen Daten verarbeiten und - was noch wichtiger ist - validieren, die mit bestimmten Profilen und Ressourcendefinitionen zur Laufzeit übereinstimmen.

Leitfäden zur Umsetzung

Ein weiteres Schlüsselelement des FHIR-Ansatzes ist der Implementierungsleitfaden, der im Wesentlichen eine Reihe von Regeln und Einschränkungen enthält, wie bestimmte Ressourcen/Profile in einem bestimmten Kontext verwendet werden sollen. Die Erstellung von Implementierungsleitfäden liegt zwar außerhalb des Rahmens dieses Buches, aber es gibt Tools, die die Erstellung von Implementierungsleitfäden erleichtern, z. B. FHIR Shorthand (FSH, ausgesprochen "Fisch") und SUSHI Unshorttens Short Hand Inputs (SUSHI).

Natürlich sind OMOP und FHIR nicht die einzigen gängigen Datenmodelle, die es gibt. Im Folgenden findest du einige weitere Modelle, die häufig auftauchen.

Andere Datenmodelle

Es gibt viele andere Datenmodelle, die dir bei der Arbeit mit RWD begegnen können. Hier sind einige, auf die ich gestoßen bin oder die ich in der Vergangenheit verwendet habe:

Analyse-Daten-Modell (ADaM)

Das ADaM-Modell stammt vom Clinical Data Interchange Standards Consortium (CDISC) und ist dem SDTM-Modell nachgeschaltet. ADaM konzentriert sich auf die Unterstützung des Analyseprozesses, auch hier mit Blick auf die Vorschriften. Dazu gehört die Erleichterung der Wiederholung von Studien und die Rückverfolgbarkeit der Ergebnisse.

Informatics for Integrating Biology and the Bedside (i2b2)

Das i2b2-Datenmodellist ein weiteres forschungsorientiertes Datenmodell , das ein Sternschema verwendet, um Fakten und Modifikatoren dieser Fakten darzustellen. Das macht es zu einem äußerst flexiblen und leistungsstarken Datenmodell. Der Kompromiss besteht jedoch darin, dass es recht komplex ist und für Neueinsteiger oder Organisationen, die nicht über die nötigen Ressourcen verfügen, um Daten in i2b2 effektiv zu verwalten, eine Herausforderung darstellen kann, obwohl es von mehr als 200 Institutionen verwendet wird.

Das Nationale Netzwerk für Patienten-zentrierte Ergebnisforschung (PCORnet)

Wie der Name schon sagt, wird das PCORnet-Datenmodell zur Unterstützung der patientenzentrierten Ergebnisforschung verwendet. Ähnlich wie OMOP soll PCORnet die Konsolidierung von Daten aus einer Vielzahl von klinischen Informationssystemen innerhalb einer Einrichtung erleichtern und dann den Vergleich von Abfragen und Analysen zwischen mehreren Einrichtungen ermöglichen.

Modell zur Tabellierung von Studiendaten (SDTM)

Das SDTM-Modell ist ein weiterer Standard von CDISC und dem ADaM vorgeschaltet. Es ist einer der erforderlichen Datenstandards für die Einreichung von Daten bei der FDA und anderen Aufsichtsbehörden. Ziel ist es, die Zusammenführung und Speicherung von Studiendaten zu erleichtern, die später im Rahmen des Zulassungsverfahrens verwendet werden sollen.

Es gibt auch viele proprietäre Datenmodelle, die von Anbietern kontrolliert werden, die Datensätze produzieren. Da diese in der Regel Lizenzen für die Daten und Vertraulichkeitsvereinbarungen erfordern, können wir sie nicht wirklich diskutieren.

Ähnlich wie bei Terminologien oder anderen Datenstandards kommt es darauf an, welche Anwendungsfälle beabsichtigt sind und wie man dies mit betrieblichen Überlegungen abwägt. So kann es zum Beispiel sinnvoll sein, ein bestimmtes Datenmodell zu übernehmen, das zwar nicht perfekt ist, das aber bereits von all deinen Mitarbeitern verwendet wird oder für das du bereits Werkzeuge und eine Infrastruktur hast. Kein Datenmodell ist perfekt, und es wird immer Kompromisse geben. Einige Modelle konzentrieren sich ausschließlich auf syntaktische und strukturelle Überlegungen, während andere versuchen, eine gewisse Semantik zu berücksichtigen.

Manche sagen schnell, dass sie Graphen als Datenmodell verwenden. Das mag zwar im technischen Sinne stimmen, aber auch die Verwendung von Graphdatenbanken erfordert, dass wir ein Schema und eine Struktur für unsere Daten definieren, die über die einfachen "Graphen" hinausgehen. Wie wir später in diesem Kapitel sehen werden, behalten wir das MIMIC-III-Datenmodell im Wesentlichen bei, auch wenn wir die Daten in einer Graphdatenbank speichern.

Unabhängig davon, für welches Datenmodell du dich entscheidest, wird es immer Einschränkungen geben. Die offensichtlichste ist, dass es eine Reihe von Anwendungsfällen gibt, die dein Datenmodell einfach nicht unterstützt oder die zu umständlich wären. Eine häufige Herausforderung (auch wenn sie nicht immer offensichtlich ist) besteht darin, dass die Übertragung von Daten von einem Datenmodell in ein anderes natürlich zu einem gewissen Informationsverlust oder einer Verschiebung führt. Wenn du zum Beispiel mit dem OMOP-Datenmodell arbeitest, gibt es sehr strenge Regeln dafür, welche Daten aufgenommen oder entfernt werden müssen (z. B. aufgrund fehlender Werte). Wenn du überlegst, welche Datenmodelle du verwenden willst, ist es wichtig, dass du deine Data-Science-Teams frühzeitig in den Prozess einbeziehst, um Machbarkeitsstudien durchzuführen. Dazu gehört in der Regel, dass du eine Teilmenge deiner Daten (oder alle Daten, wenn du die Zeit und die Ressourcen hast) in das neue Datenmodell überführst und dann die gleiche Analyse mit beiden Datensätzen durchführst. Wenn du die Ergebnisse vergleichst, musst du herausfinden, was für die Unterschiede verantwortlich ist. Das können z. B. gefilterte Daten, Fehler im Übersetzungsprozess oder Unterschiede in den strukturierten Codes sein.

Nachdem wir nun ein Gefühl für die Datenmodelle bekommen haben, von denen einige näher an den EHR-Daten liegen als andere, wollen wir nun einen tieferen Einblick in die EHR-Daten im Zusammenhang mit einem Anwendungsfall rund um Medikationsdaten gewinnen.

Fallstudie: Medikamente

Wie wir bereits besprochen haben, ist ein häufiges Problem bei der Arbeit mit Medikamentendaten die Vielfalt der Kodierungssysteme, vorausgesetzt, es wurde überhaupt ein Kodierungssystem verwendet. Für den Moment ignorieren wir das Problem der Freitextmedikation, bei der die Medikamentennamen nur Freitextstrings sind. Stattdessen werden wir uns auf das Problem der Verknüpfung von Daten konzentrieren, wenn ein Kodierungssystem wie NDC verwendet wird.

Für diese Fallstudie verwenden wir den MIMIC-III-Datensatz und konzentrieren uns auf die Verwendung von Medikamentendaten in der Tabelle PRESCRIPTIONS. Es gibt eine Spalte, ndc, die den NDC-Code enthält, der dem Namen in der Spalte drug entspricht. Wie bereits erwähnt, ist der NDC-Code sehr spezifisch, so dass es einen unterschiedlichen Code für die Tablettenform und die Kapselform eines Medikaments gibt. Es gibt sogar einen unterschiedlichen Code für die 12er-Packung und die 100er-Flasche. Wie können wir als Datenwissenschaftler diese Daten so analysieren, dass wir umfassende Fragen zu einemWirkstoff stellen können?

Eine Lösung wäre, jeden möglichen NDC-Code in eine SQL-Abfrage aufzunehmen, wenn wir die Daten extrahieren. Dies stellt ein Problem bei der Verwaltung unserer Abfragen dar, insbesondere wenn in Zukunft neue Daten oder neue Mappings hinzugefügt werden. Außerdem wird es fürDatenwissenschaftler und Ingenieure schwieriger, mit den Abfragen umzugehen, da sie es mit extrem langen Listen scheinbar zufälliger Zahlen zu tun hätten.

Eine andere Lösung wäre, all diese Anforderungen während der ETL-Phase beim Laden der Daten in unser Analysesystem zusammenzuführen. Das Problem bei diesem Ansatz ist, dass wir davon ausgehen, dass alle Anforderungen während der ETL-Phase bekannt sind. Aber was passiert, wenn wir entscheiden, dass die Medikamente äquivalent sind, nachdem wir alle Daten bereits ETL-geladen haben? Wir könnten dies wieder in die SQL-Abfrage einbetten, aber jetzt müssen wir das Wissen über gleichwertige Medikamente an zwei verschiedenen Stellen verwalten.

Das Problem der Medikamentenharmonisierung

Heparin ist ein häufig verwendetes Medikament im stationären Bereich, da es die Blutgerinnung in verschiedenen Situationen (z. B. nach einer Operation) verringert. Wenn der Patient jedoch bereits ein anderes Medikament einnimmt, das ebenfalls die Blutgerinnung verringert, wie z. B. Aspirin und andere nichtsteroidale Antirheumatika (NSAIDs), kann dies zu einer gefährlichen Situation führen. Natürlich sind sich die Ärzte dieser möglichen Wechselwirkung zwischen den Medikamenten bewusst. Wir wurden jedoch damit beauftragt, eine Entscheidungshilfe zu entwickeln, die auf potenziell gefährliche Situationen hinweist.

Im Großen und Ganzen müssen wir ein paar Dinge mit den Daten machen:

  1. Finde alle Instanzen von Heparin und bestimme alle Arten, wie es im Datensatz erfasst oder dargestellt werden kann.

  2. Bestimme alle anderen Antikoagulanzien (oder Medikamente, die Gerinnungsfaktoren reduzieren).

Mit dem MIMIC-Datensatz können wir eines von zwei Dingen tun (oder eine Kombination davon):

  1. Durchsuche die Spalten drug, drug_name_poe und drug_name_generic in der TabellePRESCRIPTIONS nach "Heparin" (ohne Berücksichtigung der Groß- und Kleinschreibung).

  2. Schlage alle NDC-Codes für Formulierungen nach, die Heparin enthalten.

Das mag zwar die einfachste (und sogar schnellste) Lösung sein, um sofortige Ergebnisse zu erhalten, aber sie ist mit einigen großen Kompromissen verbunden. Wenn wir nur die wenigen Spalten durchsuchen, die Freitextzeichenfolgen enthalten, passen wir uns natürlich zu sehr an die Daten an, auf die wir gerade Zugriff haben. Können wir sicher sein, dass alle zukünftigen Daten, die wir erhalten, übereinstimmen - was ist, wenn es Rechtschreibfehler oder Abkürzungen gibt?

Noch kritischer ist, dass die Zeichenfolge "Heparin" auch mit "Heparinspülung" übereinstimmen könnte, die zum Spülen von Infusionskathetern verwendet wird, um die Bildung von Gerinnseln in den Schläuchen und Kathetern zu verhindern. Heparinspülungen werden nicht zur Behandlung von Patienten verwendet und werden daher wahrscheinlich von den meisten Analysen ausgeschlossen, die sich mit Patienten befassen, denen mehrere Antikoagulanzien verschrieben wurden. Um dem entgegenzuwirken, könnten wir in Erwägung ziehen, den Begriff "Spülung" auszuschließen, obwohl wir jetzt in den Bereich der immer komplexeren (und anfälligeren) Datenpipelines kommen.

Die letztere Option ist die sauberere von beiden, aber wie wir bereits besprochen haben, bringt sie ein gewisses implizites klinisches Wissen in deinen SQL-Abfrage- und Analysecode ein. Für viele Anwendungsfälle ist das vielleicht kein Problem , aber es widerspricht der Idee derTrennung von Belangen.6 Wollen wir wirklich in eine Situation geraten, in der wir unseren Analysecode jedes Mal aktualisieren müssen, wenn es eine Aktualisierung der NDC-Datenbank oder eine Klärung der wissenschaftlichen Frage gibt?

Angesichts der Komplexität biomedizinischer Daten und insbesondere in Kombination mit der klinischen Versorgung ist es wichtig, dass wir robuste Datenpipelines aufbauen. Es zeigt aber auch, dass wir einen guten Kommunikationskanal zwischen unseren Datenteams und unseren klinischen Teams brauchen.

Diese ganze Diskussion wirft ein Schlaglicht auf der wichtigsten Unterschiede in der Perspektive bei der Arbeit mit Daten. Wenn ein Clinician Data Scientist (d.h. jemand mit einer Doppelausbildung als Kliniker und Data Scientist) an das obige Problem herangehen würde, würde er sofort den Unterschied zwischen "Heparin" und "Heparinflush" erkennen. Für einfache Analysen und mit der entsprechenden Ausbildung ist dies also vielleicht der einfachste Ansatz.

Wenn Unternehmen ihren Ansatz für Data Science ausweiten, wäre es jedoch sehr schwierig, ein ganzes Team von klinischen Datenwissenschaftlern zu beschäftigen. Wenn wir Datenwissenschaftler/innen integrieren, die keine klinische Erfahrung haben, wie stellen wir dann sicher, dass solche Nuancen bei der Analyse erfasst werden? Und wenn wir komplexere Analysen entwickeln (z. B. maschinelles Lernen), wie stellen wir dann sicher, dass zukünftige Datenpipelines (z. B. im Rahmen von ML ops) diese Nuancen berücksichtigen können?

Hier ist ein gutes Data Engineering auf der semantischen Ebene entscheidend! Wir brauchen Wege, um den Ansatz des Unternehmens für solch komplexe Probleme zu skalieren und gleichzeitig sicherzustellen, dass wir die feinen Nuancen erfassen, die ein maschinelles Lernmodell ausmachen oder zerstören können. Und, was noch wichtiger ist, wie stellen wir sicher, dass der Einsatz solcher Modelle in der Produktion diese Feinheiten berücksichtigt?

Wir werden diese Fragen zwar nicht direkt beantworten, aber im nächsten Abschnitt werden wir uns mit dem Laden der MIMIC-Daten in verschiedene Datenbanken und der anschließenden Abfrage dieser Daten im Zusammenhang mit unserem Heparin-Beispiel beschäftigen. Die Ansätze, die wir besprechen werden, sind Werkzeuge für deine Toolbox, eine Toolbox, die dir helfen wird Datenpipelines zu entwerfen, die viele dieser Bedenken ausräumen.

Technischer Tieftauchgang

In diesem Abschnitt erforschen wir den Prozess der:

  1. MIMIC-Daten in eine Datenbank laden

  2. Bearbeitung einiger grundlegender Harmonisierungsaufgaben

  3. Extrahieren/Abfragen der Daten aus der Datenbank

Wir werden jeden der oben genannten Punkte anhand verschiedener Datenbanken durchgehen - SQL, Property Graph und Hypergraph. Die SQL-Datenbank ist eine ziemlich unkomplizierte Datenbank, die das bestehende Schema des MIMIC-Datensatzes verwendet. Bei den NoSQL-Datenbanken halten wir das Schema relativ einfach und konzentrieren uns auf die wichtigsten Möglichkeiten und Herausforderungen der jeweiligen Datenbank. Die vorgestellten Schemata wurden nicht für einen bestimmten Anwendungsfall optimiert. Es steht dir also frei, sie an deine Projekte anzupassen, aber bedenke bitte, dass dein Projekt wahrscheinlich ein anderes Schema benötigt.

Wir verwenden die MIMIC-III Clinical Database Demo, eine Teilmenge der vollständigen MIMIC-III-Datenbank mit 100 Patienten. Für den Zugriff auf den vollständigen Datensatz muss eine Datennutzungsvereinbarung (DUA) abgeschlossen werden.

Der Code in diesem Abschnitt setzt voraus, dass du die notwendigen Container in Docker ausführen kannst und deine Umgebung entsprechend konfiguriert hast. Als erstes schauen wir uns MIMIC-III in einer relationalen Datenbank an.

Relationale Datenbank mit SQLite

Wenn du PostgreSQL verwendest, empfehle ich mit demhier verfügbaren Repo, da wir die MIMIC-Daten nicht anders laden als mit den Skripten des MIMIC-Teams. Mein Fork enthält einige sehr kleine Änderungen gegenüber der Upstream-Version. Er setzt nämlich einige Umgebungsvariablen, die der im Buch verwendeten Verzeichnisstruktur entsprechen.

Um die Container zu starten, musst du nur ./build.sh über das Terminal ausführen. Das Skript startet die Container, wartet, bis sie initialisiert sind, und beginnt dann mit dem Kopieren der MIMIC-Daten in die entsprechenden Tabellen. Dadurch werden sowohl der Datenbankserver als auch das Webinterface gestartet. Es gibt auch eine GraphQL-Implementierung.

SQLite ist eine sehr leichtgewichtige SQL-Datenbank, die eine einzige einfache Datei zum Speichern und Verwalten der Datenbank verwendet. Es ist nicht nötig, einen Datenbankserver einzurichten oder sich mit Netzwerkeinstellungen und IP-Adressen herumzuschlagen. Viele Entwickler/innen verwenden SQLite, weil es sich leicht in Anwendungen einbinden lässt, aber es eignet sich auch hervorragend für kleine Experimente oder für die lokale Arbeit auf deinem Rechner. In diesem Abschnitt werden wir das Heparin-Beispiel durchgehen und die MIMIC-Daten in SQLite laden.

Daten in SQLite importieren

Da es sich um einen sehr kleinen Datensatz handelt, werden wir der Einfachheit halber nicht mit Fremdschlüssel-Beschränkungen oder Indizes arbeiten. Wir importieren einfach jede CSV-Datei als eine andere Tabelle mit Python-Code, wie inBeispiel 4-1 gezeigt.

Beispiel 4-1. MIMIC-III-Dateien in SQLite importieren
import os
import pandas as pd
import numpy as np
import sqlite3

mimic_path = "mimic-iii-clinical-database-demo-1.4"
# In case we are using ~ for path
mimic_path = os.path.expanduser(mimic_path)
db_path = "mimic-iii.db"
db_path = os.path.expanduser(db_path)

conn = sqlite3.connect(db_path)

csv_files = [f for f in os.listdir(mimic_path) if f.endswith(".csv")]

# Sort to make it easier to debug since tables are in predictable order
csv_files.sort()

print(f"Found {len(csv_files)} files, expected 26")

for f in csv_files:
    path = os.path.join(mimic_path, f)
    table_name = f.split(".")[0]
    print(f"Importing: {path} into table: {table_name}")
    data = pd.read_csv(path, dtype=str)
    data.to_sql(table_name, conn, if_exists="replace")

Abfragen von Daten in SQLite

Jetzt, da wir die Daten geladen haben, können wir die Datenbank im Zusammenhang mit unserem Heparin-Beispiel abfragen. Beispiel 4-2 enthält eine einfache SQL-Abfrage, die nach Arzneimitteln sucht, die "Heparin" in den Spalten drug, drug_name_generic oder drug_name_poe enthalten, ohne dabei die Groß- und Kleinschreibung zu berücksichtigen. SQLite verwendet standardmäßig die Groß- und Kleinschreibung LIKE, aber wenn das nicht der Fall ist, kannst du einfach den folgenden Befehl im SQLite-Kommandozeilenprogramm ausführen: PRAGMA case_sensitive_like=OFF;.

Beispiel 4-2. Abfrage von PRESCRIPTIONS für "Heparin"
SELECT count(*) FROM `PRESCRIPTIONS`
WHERE drug LIKE "%heparin%"
    OR drug_name_generic LIKE "%heparin%"
    OR drug_name_poe LIKE "%heparin%"

Wie bereits erwähnt, werden alle heparinhaltigen Medikamente angezeigt, sowohl die, die therapeutisch eingesetzt werden, als auch die, die für Infusionen und Katheter verwendet werden. Um dies weiter zu untersuchen, gibt uns Beispiel 4-3 eine eigene Liste.

Beispiel 4-3. Eindeutige Einträge für Heparin
SELECT DISTINCT ndc, drug FROM `PRESCRIPTIONS`
WHERE drug LIKE "%heparin%"
    OR drug_name_generic LIKE "%heparin%"
    OR drug_name_poe LIKE "%heparin%"
ORDER BY ndc

Daten mit SQLite harmonisieren

Wir sehen, dass es 11 Einträge von Interesse gibt, viele mit demselben Namen, aber alle mit unterschiedlichen NDCs. Erinnere dich daran, dass die NDCs sehr detailliert sind und sich je nach Verpackungsart, Hersteller, Größe/Volumen usw. ändern.Beispiel 4-4 zeigt eine Auflistung der NDCs und Medikamentennamen, die es uns leichter macht, zu entscheiden, welche Werte wir in unsere nächste Abfrage einbinden wollen.

Beispiel 4-4. Eindeutige Einträge für Heparin (Ergebnisse)
+-------------+--------------------------------------+
| ndc         | drug                                 |
+-------------+--------------------------------------+
| 63323026201 | Heparin                              |
| 00641040025 | Heparin                              |
| 17191003500 | Heparin CRRT                         |
| 17191003500 | Heparin Flush                        |
| 08290036005 | Heparin Flush (10 units/ml)          |
| 00409115170 | Heparin Flush (10 units/ml)          |
| 64253033335 | Heparin Flush (100 units/ml)         |
| 63323026201 | Heparin Flush (5000 Units/mL)        |
| 00641040025 | Heparin Flush CRRT (5000 Units/mL)   |
| 63323026201 | Heparin Flush CRRT (5000 Units/mL)   |
| 64253033335 | Heparin Flush CVL  (100 units/ml)    |
| 64253033335 | Heparin Flush Hickman (100 units/ml) |
| 64253033335 | Heparin Flush PICC (100 units/ml)    |
| 00409115170 | Heparin Lock Flush                   |
| 00338055002 | Heparin Sodium                       |
| 00074779362 | Heparin Sodium                       |
| 00264958720 | Heparin Sodium                       |
| 00409779362 | Heparin Sodium                       |
| 63323054207 | Heparin Sodium                       |
+-------------+--------------------------------------+

Anhand dieser Ergebnisse entscheiden wir, dass wir sieben der Einträge behalten wollen:

63323026201
00641040025
00338055002
00074779362
00264958720
00409779362
63323054207

Wie in Beispiel 4-5 zu sehen ist, aktualisieren wir unsere Abfrage jetzt so, dass wir nur Rezepte mit diesen NDC-Codes finden, anstatt eine stringbasierte Suche durchzuführen.

Beispiel 4-5. Verschreibungen nach NDC
SELECT ndc, drug FROM `PRESCRIPTIONS` WHERE ndc in (
  "63323026201",
  "00641040025",
  "00338055002",
  "00074779362",
  "00264958720",
  "00409779362",
  "63323054207"
)

Wir können diese Abfrage nun in verschachtelten SELECT-Anweisungen oder in JOIN-Anweisungen verwenden, um Patienten, Einweisungen oder andere Elemente von Interesse zu analysieren, die sich ausschließlich auf Heparin-Verschreibungen konzentrieren, die nicht nur Spülungen waren. Wie du siehst, müssten wir diese Liste in unserem Code und in verschiedenen Abfragen verwenden. Was ist, wenn wir einen Fehler machen und eine andere NDC aus dieser Liste hinzufügen oder entfernen müssen? Das wäre ziemlich problematisch zu verwalten und zu pflegen, vor allem, wenn wir den Code mit anderen Datenwissenschaftlern teilen würden.

Im nächsten Abschnitt gehen wir dasselbe Beispiel durch, verwenden aber stattdessen einen graphenbasierten Ansatz. Dabei wird deutlich, wie wir die Datenbank selbst nutzen können, um das Kopieren und Einfügen einer Liste von NDC-Codes für verschiedene Analysen/Projekte zu vermeiden.

Eigentumsdiagramm mit Neo4j

Die Verwendung von Diagrammen zur Harmonisierung von Daten ist wo die Magie und der Spaß passiert! Einer der Hauptvorteile einer Graphdatenbank ist die Möglichkeit, einzelne Datenpunkte miteinander zu verbinden/zu verknüpfen. Für unseren speziellen Anwendungsfall werden wir ein neues Konzept definieren, das für Medikamente steht, die Heparin enthalten, aber zur Behandlung von Krankheiten eingesetzt werden (also ohne Spülung). Sobald wir dieses Konzept erstellt haben, können wir es in zukünftigen Abfragen verwenden. Das ist etwas, das wir in einer relationalen Datenbank nicht so einfach tun können: Wir erstellen Knoten, die wir mit den tatsächlichen Verordnungsdaten im MIMIC-Datensatz verbinden.

Wenn wir dies in großem Maßstab tun (Hunderte oder Tausende von neuen Konzepten erstellen), sollten wir den neuen Knotenpunkten einige Eigenschaften hinzufügen, damit wir sie verfolgen und verwalten können. Wir könnten zum Beispiel eine Beschreibung des Konzepts und den Grund für seine Erstellung, ein Datum/Zeitstempel für seine Erstellung und letzte Änderung sowie Tags oder andere Metadaten hinzufügen, um die Konzepte zu organisieren. Andernfalls kann es passieren, dass du am Ende einen Haufen Konzepte hast, die du nicht verwalten und pflegen kannst.

Leider lässt sich Neo4j nicht so einfach in unseren Python-Code einbetten wie SQLite. Als Erstes müssen wir also eine Docker-Instanz unserer Datenbank erstellen. Wir können denselben Befehl zum Starten eines Docker-Containers verwenden wie inBeispiel 3-9 oder wir können denselben Container wiederverwenden.

Daten in Neo4j importieren

Um die Daten in Neo4j zu laden, müssen wir auf so ziemlich den gleichen Prozess wie bei SQLite befolgen. Beispiel 4-6 sollte dir bekannt vorkommen, denn es ist fast identisch mit dem, was wir vorher gemacht haben, nur dass wir jetzt vom sqlite3-Python-Modul zuNeointerface wechseln. Es ist ein Wrapper der Neo4j-Python-Bibliothek und bietet auch Funktionen für Pandas-Datenrahmen.

Beispiel 4-6. MIMIC-III in Neo4j importieren
import os
import pandas as pd
import numpy as np
import neointerface

mimic_path = "mimic-iii-clinical-database-demo-1.4"
# In case we are using ~ for path
mimic_path = os.path.expanduser(mimic_path)

db = neointerface.NeoInterface(
    host="bolt://localhost",
    credentials=("neo4j", "test"),
    apoc=True
)

csv_files = [f for f in os.listdir(mimic_path) if f.endswith(".csv")]

# Sort to make it easier to debug since tables are in predictable order
csv_files.sort()

print(f"Found {len(csv_files)} files, expected 26")

for f in csv_files:
    path = os.path.join(mimic_path, f)
    table_name = f.split(".")[0]
    print(f"Importing: {path} into table: {table_name}")
    data = pd.read_csv(path, dtype=str)
    data = data.replace({np.nan: None})

    # This is the only line different compared to SQLite
    db.load_df(data, table_name)

Importe optimieren

Für die MIMIC-III-Demodaten funktioniert der Code in Beispiel 4-6 gut genug und benötigt nur ein oder zwei Minuten zum Laden der Daten. Wenn du versuchst, den gesamten MIMIC-III-Datensatz zu laden, solltest du ähnlich vorgehen wie beim Laden der UMLS in Kapitel 3, da der vorherige Code ziemlich lange dauert.

Nachdem wir nun die Basiskonzepte geladen haben, müssen wir alle Knoten nach dem MIMIC-III-Schema verbinden. Obwohl es sicherlich Möglichkeiten gibt, das gesamte Datenmodell zu optimieren, werden wir uns daran halten, die bestehende Struktur des MIMIC-Schemas zu replizieren, um die Dinge konsistent und einfach zu halten. In Beispiel 4-7 habe ich ein einzelnes Wörterbuch erstellt, das die Beziehungen enthält, die wir erstellen werden. Ich habe es für dieses Buch gekürzt, aber die vollständige Version ist im zugehörigen Repo verfügbar.

Beispiel 4-7. MIMIC-III-Beziehungen in Neo4j
relationships = {
    ('PATIENTS', 'subject_id'): [
        ('has_admission', 'ADMISSIONS'),
        ('has_callout', 'CALLOUT'),
        ('has_chartevent', 'CHARTEVENTS'),
        ('has_cptevent', 'CPTEVENTS'),
        ('has_datetimeevent', 'DATETIMEEVENTS'),
        ('has_diagnoses_icd', 'DIAGNOSES_ICD'),
        ('has_drgcode', 'DRGCODES'),
        ('has_icustay', 'ICUSTAYS'),
        ('has_inputevents_cv', 'INPUTEVENTS_CV'),
        ('has_inputevents_mv', 'INPUTEVENTS_MV'),
        ('has_labevent', 'LABEVENTS'),
        ('has_microbiologyevent', 'MICROBIOLOGYEVENTS'),
        #('has_noteevent','NOTEEVENTS'),
        ('has_outputevent', 'OUTPUTEVENTS'),
        ('has_prescription', 'PRESCRIPTIONS'),
        ('has_procedureevents_mv', 'PROCEDUREEVENTS_MV'),
        ('has_procedures_icd', 'PROCEDURES_ICD'),
        ('has_service', 'SERVICES'),
        ('has_transfer', 'TRANSFERS')
    ],
    ...
    ('MICROBIOLOGYEVENTS', 'spec_itemid'): [
        ('specimen_item', 'D_ITEMS', 'itemid')
    ],
    ('MICROBIOLOGYEVENTS', 'org_itemid'): [
        ('organism_item', 'D_ITEMS', 'itemid')
    ],
    ('MICROBIOLOGYEVENTS', 'ab_itemid'): [
        ('antibody_item', 'D_ITEMS', 'itemid')
    ],
    ('OUTPUTEVENTS', 'itemid'): [
        ('item', 'D_ITEMS')
    ],
    ('PROCEDUREEVENTS_MV', 'itemid'): [
        ('item', 'D_ITEMS')
    ]
}

Dazu habe ich das Wörterbuch so eingerichtet, dass die Schlüssel ein Tupel sind, das aus der Bezeichnung des Quellknotens sowie dem Attribut innerhalb des Quellknotens besteht:(SOURCE_NODE_LABEL, ATTR). Die Werte sind eine Liste aller Beziehungen des Quellknotens als Tupel, die aus der Beziehung und der Bezeichnung des Zielknotens bestehen: (RELATIONSHIP, TARGET_NODE_LABEL). Es wird davon ausgegangen, dass der Name des Attributs, das im Zielknoten abgeglichen werden soll, derselbe ist wie der des Quellknotens. Wie du bei den mikrobiologischen Ereignissen sehen kannst, gibt es ein drittes Element in dem Beziehungstupel. In diesem Fall ist das dritte Element der Attribut-/Eigenschaftsname im Zielknoten.

Die allererste Beziehung, die wir erstellen werden, ist also

(PATIENTS)-[has_admission]->(ADMISSIONS)

und "verbunden" durch die Eigenschaft subject_id.

Ähnlich gilt für die mikrobiologischen Ereignisse die Beziehung

(MICROBIOLOGYEVENT)-[specimen_item]->(D_ITEMS)

und "verbunden" durch spec_itemid in MICROBIOLOGYEVENT und itemid in D_ITEMS.

Um dies auszuführen und die Beziehungen tatsächlich zu erstellen, habe ich eine einfache Funktion erstellt, die die Abfrage gegen die Datenbank konstruiert und ausführt, wie in Beispiel 4-8 gezeigt.

Beispiel 4-8. Funktion zum Erstellen von Neo4j-Beziehungen
def merge_relationship(source, sattr, rel, target, tattr):
    print(f"Creating: ({source}:{sattr})-[{rel}]->({target}:{tattr})")

    query = f"""
    CALL apoc.periodic.iterate(
       "MATCH (s:{source}), (t:{target})
        WHERE s.{sattr} = t.{tattr}
        RETURN s, t",
       "MERGE (s)-[r:{rel}]->(t)
        RETURN s,r,t",
        {{batchSize:1000, parallel: true}}
    )
    """

    print(query)

    result = db.query(query)

Wie du siehst, verwende ich eine der APOC-Funktionen apoc.periodic.iterate, bei der wir die Abfrage in zwei Teile aufteilen müssen - die Abfrage und die Aktion. Wir geben auch die Stapelgröße an, um zu vermeiden, dass wir eine riesige Transaktion erstellen. Am Ende werden wir mehrere Millionen Kanten/Beziehungen erstellen, und der Versuch, das in einer einzigen Transaktion zu tun, kann problematisch sein. Zu guter Letzt bitten wir Neo4j, die Operation mit Hilfe einer Java ThreadPoolExecutor zu parallelisieren.

Jetzt, wo wir unsere Daten in Neo4j geladen haben, können wir sie abfragen.

Daten in Neo4j abfragen

Zurück zu unserem Heparin-Beispiel: Wir können alle Verschreibungen abfragen, die "Heparin" enthalten (Groß- und Kleinschreibung wird nicht berücksichtigt), und diese dann sofort mit einzelnen Patienten verknüpfen, wie in Beispiel 4-9 gezeigt.

Beispiel 4-9. Cypher-Abfragen für Heparin
MATCH (p:PRESCRIPTIONS)
WHERE toLower(p.drug) CONTAINS "heparin"
    OR toLower(p.drug_name_generic) CONTAINS "heparin"
    OR toLower(p.drug_name_poe) CONTAINS "heparin"
RETURN DISTINCT p.drug

MATCH (pt:PATIENTS)-[r:has_prescription]->(p:PRESCRIPTIONS)
WHERE toLower(p.drug) CONTAINS "heparin"
    OR toLower(p.drug_name_generic) CONTAINS "heparin"
    OR toLower(p.drug_name_poe) CONTAINS "heparin"
RETURN DISTINCT pt

Die Cypher-Abfragen sehen ihren SQL-Pendants sehr ähnlich, was aber angesichts der Tiefe und Komplexität der Abfragen nicht überrascht. Als Nächstes gehen wir einen Schritt weiter und konzentrieren auf die NDCs, an denen wir tatsächlich interessiert sind.

Daten mit Neo4j harmonisieren

Schauen wir uns nun an, wie wir mit dieser Situation umgehen wenn wir die Verschreibungen so filtern wollen, dass Heparinspülungen ignoriert werden. Wir folgen demselben Prozess wie im vorherigen Abschnitt. Allerdings fügen wir den zusätzlichen Schritt hinzu, dies in der Datenbank selbst zu erfassen, wie in Beispiel 4-10 gezeigt.

Beispiel 4-10. Ein neues Heparin-Konzept erstellen
CREATE (parent:Drug:Knowledge {
    date_created: "2022-01-01",
    drug: "Heparin (non-flush)",
    description: "MIMIC-III, Heparin, excluding flushes"
    purl: "http://some-ontology.org/12345"
}) WITH parent

MATCH (p:PRESCRIPTIONS)
WHERE p.ndc IN [
  "63323026201",
  "00641040025",
  "00338055002",
  "00074779362",
  "00264958720",
  "00409779362",
  "63323054207"
]

MERGE (p)-[r:for_drug {derived: true}]->(parent)
RETURN DISTINCT parent,p

Diese Abfrage besteht im Wesentlichen aus drei Teilen:

  1. Erstelle das neue Konzept, mit dem wir die Verschreibungen verknüpfen werden.

  2. Finde die Rezepte, die dich interessieren.

  3. Verbinde die abgestimmten Rezepte mit dem neuen Heparin-Konzept.

Dies ist zwar die einfachste und unkomplizierteste Methode, um das neue Konzept für "Heparin (nicht spülbar)" mit den Verschreibungen zu verknüpfen, aber der Nachteil ist, dass wir keine Zwischenkonzepte für jede der sieben einzelnen NDCs haben. Das ist jedoch ein theoretischer Nachteil, denn aus akademischer Sicht möchten wir vielleicht Konzepte für jede NDC verfolgen, aber in der Realität werden wir diese Informationen oder dieses Wissen vielleicht nie nutzen. Das ist ein Beispiel für die Art von Entscheidung, die wir bei der Arbeit mit Diagrammen treffen müssen: Wollen wir Dinge erfassen, die wir vielleicht nie verwenden werden? In der Regel lautet die Antwort nein, aber es ist nicht immer offensichtlich, was wir in der Zukunft brauchen könnten. Andererseits: Wie viel sollten wir in eine Zukunft investieren, die es vielleicht gar nicht gibt?

Ein weiterer wichtiger Punkt ist die Verwendung der Eigenschaft purl. PURL steht für Persistent Uniform Resource Locator und ist eine Möglichkeit, Ressourcen, die über das Internet zugänglich sind, eindeutig zu identifizieren. Viele nutzen PURLs einfach nur, um eindeutige Bezeichner zu erstellen. Die Absicht ist jedoch, dass die PURL eine gültige URL ist, die HTTP-Antwortcodes nutzt, um den Verbrauchern den Typ und den Status einer bestimmten Ressource mitzuteilen.7 Die Open Biological and Biomedical Ontology (OBO) Foundry ist ein weiteres Beispiel dafür, wie PURLs als Bezeichner verwendet werden. Hier verwenden wir sie einfach, um einen Bezeichner für den Zweck des Beispiels zu erstellen. Der verwendete Hostname ist nicht real, und die PURL wird nicht wirklich zu etwas aufgelöst.

Wie können wir also angesichts dieses neuen Konzepts unsere Datenbank nach allen Patienten abfragen, die Heparin erhalten, mit Ausnahme der Spülung? Grundsätzlich können wir unsere Knoten und Beziehungen mit Cypher verfolgen, wie in Beispiel 4-11 zu sehen ist.

Beispiel 4-11. Alle mit dem Heparin-Konzept verbundenen Patienten (Neo4j)
MATCH (pt:PATIENTS)-[rx:has_prescription]->(p:PRESCRIPTIONS)-[:for_drug]->
      (d:Drug {purl: "http://some-ontology.org/12345"})
RETURN pt

Wie du sehen kannst, ist unsere Abfrage recht einfach und intuitiv. Je nach unserem internen Governance-Prozess können wir den neuen Konzepten, die wir erstellen, sowie den Beziehungen, die sie verbinden, so viele Eigenschaften hinzufügen, wie wir brauchen. So können wir nur die Knoten und Beziehungen herausfiltern und verfolgen, die für unsere Frage relevant sind. Ich habe zum Beispiel eine date_created Eigenschaft hinzugefügt, die festhält, wann die Beziehung tatsächlich in der Datenbank erstellt wurde. So können wir nachverfolgen, wie sich die Beziehung im Laufe der Zeit verändert (wenn wir jedes Mal eine neue Beziehung erstellen), aber nur die letzte Beziehung verwenden, wenn wir Abfragen wie die vorherige durchführen.

Angesichts dieser Möglichkeit, Patienten mit Verschreibungen und Verschreibungen mit unserem neuen Arzneimittelkonzept zu verknüpfen, fragst du dich vielleicht, warum wir keine anderen Arzneimittelkonzepte haben. Wenn wir uns die Tabelle PRESCRIPTIONS in der Demo-Datenbank ansehen, stellen wir fest, dass es 2.489 eindeutige Einträge gibt, wenn wir nur die Spalten mit den Arzneimittelinformationen betrachten (ohne die Verschreibungsdaten und die Verknüpfungen zu Patienten, Einweisungen oder Aufenthalten auf der Intensivstation).

Wir möchten einen neuen Knotentyp einführen, der nur die Arzneimitteldetails erfasst und das Anfangs- und Enddatum, die Aufnahme und den Aufenthalt auf der Intensivstation in die Beziehunghas_prescription verschiebt. Wenn wir die Daten weiter umgestalten, möchten wir vielleicht auch ein bestimmtes Rezept mit einer Krankenhauseinweisung oder einem Aufenthalt auf der Intensivstation verknüpfen. An dieser Stelle zeigen sich die Grenzen des Eigenschaftsdiagramms. Wenn wir ein Rezept als Beziehung modellieren, gibt es keine direkte Möglichkeit, diese Beziehung mit einem Knotenpunkt der Krankenhausaufnahme zu verknüpfen. Angesichts der Details der MIMIC-Daten könnten wir dies umgehen, indem wir die Krankenhausaufnahme-ID (hadm_id) sowohl im Aufnahmeknoten als auch in der Rezeptbeziehung speichern und sicherstellen, dass unsere Cypher-Abfrage nach beiden filtert. InBeispiel 4-12 würden wir alle Rezepte abfragen, die mit der Aufnahme in die Notaufnahme eines Patienten mit subject_id12345 verbunden sind.

Beispiel 4-12. Verbinden einer Aufnahme mit einer Verordnungsbeziehung
MATCH (pt:Patients {subject_id: 12345})-[:has_admission]->
      (a:ADMISSIONS {admission_type: "EMERGENCY"}),
      (pt2:PATIENTS)-[rx:has_prescription]->(d:Drug)
WHERE a.hadm_id = rx.hadm_id
RETURN pt, rx, d

Das macht die Abfragen im Vergleich zu SQL zwar einfacher, aber der Kompromiss ist, dass du jetzt das in der Datenbank gespeicherte Wissen verwalten und sicherstellen musst, dass deine Abfragen die richtigen Konzepte verwenden. Was ist, wenn jemand die zugrunde liegenden Mappings ändert, die wir im vorherigen Abschnitt erstellt haben? Wie würde sich das auf deine Abfrage auswirken? Woher würdest du überhaupt wissen, dass solche Änderungen vorgenommen wurden, so dass du deine Abfragen erneut testen könntest, um sicherzustellen, dass sie immer noch sinnvoll sind? Damit wird deutlich, dass dieser Ansatz zwar im semantischen Sinne "sauber" ist, aber dennoch einige der wichtigsten Herausforderungen im Zusammenhang mit dem Wissensmanagement aufzeigt, unabhängig von der gewählten technischen Lösung.

Im nächsten Abschnitt werden wir das Heparin-Beispiel noch einmal mit TypeDB durchgehen. Zusätzlich zu den Hypergraphen-Aspekten werden wir uns auch die Verwendung einer Inferencing- oder Reasoning-Engine ansehen. Dies fügt ein weiteres Element hinzu, mit dem wir Datenharmonisierungs-Mappings durch den Einsatz von Regeln pflegen können.

Hypergraph mit TypeDB

Wir fahren mit TypeDB fort, werden uns aber auch eine TypeDB-spezifische Funktion ansehen - die eingebaute Reasoning-Engine, mit der wir unsere Datenharmonisierungs-Mappings mit zwei verschiedenen Ansätzen verwalten können:

  1. Ähnlich wie bei Eigenschaftsgraphen und RDF-Triple-Speichern können wir neue Beziehungen erstellen, die die verschiedenen Heparin-Konzepte miteinander verbinden und dieses Wissen direkt in den Graphen einbetten.

  2. Wir nutzen die Vorteile der Reasoning-Engine und erstellen Regeln, die die Mappings verarbeiten, indem wir die Regeln je nach Abfrageanforderung laden und entladen.

Es gibt keinen eindeutigen "besten Ansatz" zwischen unseren beiden Optionen, und die Wahl hängt weitgehend von den Details des jeweiligen Anwendungsfalls ab. Wenn sich deine Mappings zum Beispiel wahrscheinlich nicht oft ändern und häufig abgefragt werden, ist es wahrscheinlich am besten, sie direkt in die Datenbank zu schreiben und sie wie andere ETL-Aufträge zu verwalten, die den Inhalt der Datenbank ändern.

Wenn sich deine Mappings hingegen häufig ändern, ist die ständige Aktualisierung deiner Datenbank eine Herausforderung und kann die Wahrscheinlichkeit von inkonsistentem Wissen erhöhen. Diese Art von "Bugs" kann schwer aufzuspüren sein, weil sie schwer zu reproduzieren sind. In diesem Fall könntest du die Mappings als Regeln erstellen und sie wie jeden anderen Code verwalten. Sobald sie in die Datenbank geladen sind, würden deine Abfragen von den aktuellsten Zuordnungen profitieren.

Bevor wir mit den Daten arbeiten, müssen wir das TypeDB-Schema erstellen und laden, genau wie wir es in Kapitel 3 bei der Arbeit mit der UMLS getan haben. Beispiel 4-13 zeigt das Schema für Patient, das der Definition der Tabelle PATIENTS in MIMIC-III entspricht.

Beispiel 4-13. TypeDB-Schema für Patienten
define

  patient sub entity,
      plays prescription:patient,

      owns row_id,
      owns subject_id,

      owns gender,
      owns dob,
      owns dod,
      owns dod_hosp,
      owns dod_ssn,
      owns expire_flag;

  row_id sub attribute,
      value long;
  subject_id sub attribute,
      value long;
  gender sub attribute,
      value string;
  dob sub attribute,
      value string;
  dod sub attribute,
      value string;
  dod_hosp sub attribute,
      value string;
  dod_ssn sub attribute,
      value string;
  expire_flag sub attribute,
      value long;

Wir haben jede Spalte der Tabelle PATIENTS als Attribut in der TypeDB definiert, zusammen mit einem zugehörigen Datentyp. Ich habe alles als String definiert, um den Code und das Parsing einfach zu halten, obwohl ich die verschiedenen Felder von _id als lange Datentypen angelegt habe. Du kannst auch sehen, dass die Entität patient der TabellePATIENTS entspricht und die einzelnen Spalten über das Schlüsselwort owns verknüpft. Diese Syntax unterscheidet sich zwar von der anderer Datenbankdefinitionssprachen, ist aber konzeptionell gleichwertig. Der einzige wesentliche Unterschied ist die Zeile plays prescription:patient, die die Tatsache hervorhebt, dass ein Patient die Rolle eines Patienten innerhalb der Verordnungsbeziehung "spielt".8 Beispiel 4-14 zeigt einen weiteren Ausschnitt aus der Schemadefinition, der speziell für die Modellierung von Rezepten und Medikamenteninstanzen gilt.

Beispiel 4-14. TypeDB-Schema für Verschreibungen
prescription sub relation,
    relates patient,
    relates prescribed_drug,

    owns startdate,
    owns enddate;

druginstance sub entity,
    plays prescription:prescribed_drug,

    owns startdate,
    owns enddate,
    owns drug_type,
    owns drug,
    owns drug_name_poe,
    owns drug_name_generic,
    owns formulary_drug_cd,
    owns gsn,
    owns ndc,
    owns prod_strength,
    owns dose_val_rx,
    owns dose_unit_rx,
    owns form_val_disp,
    owns form_unit_disp,
    owns route;

Ich habe die ursprüngliche Definition der Tabelle PRESCRIPTIONS in eine Entität und eine Beziehung aufgeteilt. Die Entität druginstance enthält alle Informationen über das Medikament (z. B. Name, Dosis, Art des Medikaments usw.), während die Relation prescription die Details des Rezepts selbst enthält. Innerhalb der Relation definieren wir zweiRollen: patient und prescribed_drug. Rollen ermöglichen es TypeDB, eine zusätzliche Kontextebene zu erfassen und zu modellieren - die Rolle, die eine Entität innerhalb einer bestimmten Beziehung spielt.

Daten in TypeDB importieren

Wie du es sicher schon gewohnt bist, brauchen wir , um sicherzustellen, dass unser Docker-Container läuft. Du kannst die gleiche Konfiguration wie in Beispiel 3-14 aus dem vorherigen Kapitel verwenden.

Das Grundgerüst unseres Codes bleibt das gleiche wie bei den vorherigen Beispielen für SQLite und Neo4j. Das ist ziemlich einfach, obwohl TypeDB die explizite Verwendung von Transaktionen erfordert, um unsere Interaktionen zu verwalten. Bevor wir mit dem Importieren der eigentlichen Daten beginnen, müssen wir zunächst unser TypeDB-Schema laden, wie wir in Beispiel 3-16 gesehen haben.

Jetzt, da unsere Datenbank vorbereitet und geladen ist, müssen wir die einzelnen CSV-Dateien durchgehen und sie in TypeDB laden, wie wir es bereits getan haben. Leider gibt es noch keine Bibliothek, die das bequeme Laden von Datenrahmen in TypeDB ermöglicht. Außerdem müssen wir die Daten zunächst verarbeiten, um eindeutige Medikamenteninstanzen zu extrahieren, die wir dann mit Hilfe von Verordnungsbeziehungen verknüpfen können. Beispiel 4-15 enthält unseren Top-Level-Code, der durch die CSV-Dateien iteriert und sie lädt.

Beispiel 4-15. Laden der MIMIC-III-Daten in TypeDB
csv_files = [f for f in os.listdir(mimic_path) if f.endswith(".csv")]
csv_files.sort()

print(f"Found {len(csv_files)} files, expected 26")

db = "test2"

with TypeDB.core_client("localhost:1729") as client:
    if client.databases().contains(db):
        client.databases().get(db).delete()

    client.databases().create(db)

with TypeDB.core_client("localhost:1729") as client:
    with client.session(db, SessionType.SCHEMA) as session:
        with session.transaction(TransactionType.WRITE) as tx:
            with open("../data/mimic-schema.tql") as f:
                q = f.read()
                tx.query().define(q)
                tx.commit()

with TypeDB.core_client("localhost:1729") as client:
    with client.session(db, SessionType.DATA) as session:
        for f in csv_files:
            path = os.path.join(mimic_path, f)
            table_name = f.split(".")[0]
            print(f"Importing: {path} into table: {table_name}")
            data = pd.read_csv(path, dtype=str)
            data = data.replace({np.nan: None})

            queries = processors[table_name](data)

            with session.transaction(TransactionType.WRITE) as tx:
                for q in queries:
                    tx.query().insert(q)
                tx.commit()

Im Vergleich zu unseren vorherigen Beispielen haben wir den Großteil des Imports in Transaktionen und anderen TypeDB-spezifischen Code verpackt. Die anderen bemerkenswerten Unterschiede im Vergleich zu Beispiel 3-17 sind das Fehlen von Batching und die Verwendung von allgemeineren "Prozessoren". Jede unserer Prozessorfunktionen nimmt einen Datenrahmen als Eingabe und gibt eine Liste von Abfragezeichenfolgen zurück, die in der Datenbank ausgeführt werden. Bei einfachen Tabellen wie PATIENTS führt die Prozessorfunktion eine direkte Konvertierung durch. Für Rezepte müssen wir etwas mehr tun, wie wir in Beispiel 4-16 sehen können. Ich habe die Definitionen der Templating-Funktionen aus dem Beispiel weggelassen, da du einige inBeispiel 3-18 gesehen hast.

Beispiel 4-16. Funktion des Patientenprozessors
def process_patients(df):
    queries = [patient_template(x).strip()
               for x in data.itertuples()]
    return queries

def process_prescriptions(df):
    queries = []

    # Extract unique drug instances
    drugs = df[[x[0] for x in drug_fields]].drop_duplicates()

    queries.extend([druginstance_template(x).strip()
                    for x in drugs.itertuples()])

    queries.extend([prescription_template(x).strip()
                    for x in df.itertuples()])

    return queries

processors = {
    "PATIENTS": process_patients,
    ...
    "PRESCRIPTIONS": process_prescriptions
}

Da wir unsere Rezepte jedoch in eine druginstance Entität und eine prescription Relation aufteilen, müssen wir etwas mehr verarbeiten. Zuerst müssen wir nur die Spalten extrahieren, die für druginstance benötigt werden, und dann müssen wir zwei Abfragen erstellen - eine zum Einfügen von druginstances und eine zum Einfügen der prescription Beziehungen.Beispiel 4-17 zeigt die Prozessorfunktion sowie die zugehörigen Template-Funktionen, die die Abfrage-Strings erzeugen.

Beispiel 4-17. Funktion des Verordnungsgebers
drug_fields = [
        ("drug_type", str),
        ("drug", str),
        ("drug_name_poe", str),
        ("drug_name_generic", str),
        ("formulary_drug_cd", str),
        ("gsn", str),
        ("ndc", str),
        ("prod_strength", str),
        ("dose_val_rx", str),
        ("dose_unit_rx", str),
        ("form_val_disp", str),
        ("form_unit_disp", str),
        ("route", str)
    ]

def druginstance_template(p):
    return f"""
    insert $p isa druginstance,
        {", ".join(filter(None, [has_clause(p, f) for f in drug_fields]))}
        ;
    """

def prescription_template(p):
    return f"""
    match
        $pt isa patient, has subject_id {getattr(p, 'subject_id')};
        $drug isa druginstance,
        {", ".join(filter(None, [has_clause(p, f) for f in drug_fields]))};
    insert $prescription (patient: $pt, prescribed_drug: $drug) isa prescription,
        has row_id {getattr(p, 'row_id')},
        has startdate "{getattr(p, 'startdate')}",
        has enddate "{getattr(p, 'enddate')}";
    """

def process_prescriptions(df):
    queries = []

    # Extract unique drug instances
    drugs = df[[x[0] for x in drug_fields]].drop_duplicates()

    queries.extend([druginstance_template(x).strip()
                    for x in drugs.itertuples()])

    queries.extend([prescription_template(x).strip()
                    for x in df.itertuples()])

    return queries

Der wichtigste Unterschied ist die Match/Insert-Abfrage inprescription_template(). Da die Relationen davon ausgehen, dass die zugehörigen Entitäten bereits in den Graphen geladen sind, beginnen wir mit einer match Klausel, um den Patienten und das Medikament zu finden, die das Rezept bilden. Ausgehend von den MIMIC-III-Daten gleichen wir den Patienten über subject_id und das Medikament über alle Medikamentenfelder ab und erstellen dann die Beziehung, in der das Anfangs- und Enddatum des Rezepts erfasst wird.

Jetzt, wo wir unsere Daten in TypeDB geladen haben, können wir unter einige unsererStandardabfragen ausführen.

Daten in TypeDB abfragen

Zunächst wollen wir mit einem einfachen String-Matching herausfinden, welche Medikamente Heparin enthalten . Im Gegensatz zu den vorherigen Beispielen fragen wir nach unseren neu erstellten Medikamenteninstanzen und nicht nach Verschreibungen, wie wir es bei SQLite und Neo4j getan haben. Dann müssten wir nur eine zusätzliche Klausel hinzufügen, um Verschreibungsbeziehungen abzugleichen, wenn wir alle Verschreibungen zu unseren Medikamenten zurückgeben wollen, die die Zeichenfolge "Heparin" enthalten. Diese beiden Abfragen werden inBeispiel 4-18 gezeigt.

Beispiel 4-18. TypeQL-Abfragen für Heparin
# Query just the drug instances
match
    $drug isa druginstance;
    {$drug has drug contains "heparin";}
      or {$drug has drug_name_generic contains "heparin";}
      or {$drug has drug_name_poe contains "heparin";};
get $rx;

# Add clause for prescriptions
match
    $drug isa druginstance;
    {$drug has drug contains "heparin";}
      or {$drug has drug_name_generic contains "heparin";}
      or {$drug has drug_name_poe contains "heparin";};

    $rx (prescribed_drug: $drug) isa prescription;
get $rx;

Kommen wir nun zu der Frage, wie wir unsere Arzneimittelfälle mit unserem Heparin-Konzept in Einklang bringen können.

Daten in TypeDB harmonisieren

Nachdem wir unsere Daten in TypeDB geladen haben, besteht der nächste Schritt in unserem Heparin-Beispiel darin, ein neues Arzneimittelkonzept für die Untergruppe von Heparin zu erstellen, an der wir interessiert sind. Bei der Harmonisierung der Daten können wir zwei Ansätze verfolgen. Der erste(Beispiel 4-19) folgt demselben Muster wie bei den Eigenschaftsgraphen - wir aktualisieren einfach direkt die Datenbank.

Beispiel 4-19. Beispiel TypeDB-Regel
match
    $d isa druginstance, has ndc "63323026201";
    $c isa concept, has purl "http://some-ontology.org/12345";
insert
    (parent: $c, child: $d) isa hierarchy;

Der zweite Ansatz, den wir verfolgen, besteht darin, Regeln zu erstellen. Wie du in der folgenden Abfrage sehen kannst, sieht sie ähnlich aus wie die TypeQL-Abfrage zum direkten Einfügen in die Datenbank. Bei der Verwendung von Regeln fügt die TypeDB-Reasoning-Engine bei der Ausführung der Regel dynamisch neue Daten zur Datenbank hinzu, ohne sie zu persistieren. So können Abfragen mit den Daten interagieren, als wären sie bereits in der Datenbank. Im Grunde genommen handelt es sich um einen Mechanismus, der Daten einfügt, die Abfrage ausführt und dann die Daten wieder entfernt.

Beispiel 4-20 zeigt eine einzelne Regel, die eine bestimmtedruginstance (indiziert durch NDC) mit unserem Konzept "Heparin (non-flush)" abgleicht. Regeln können Beziehungen und Attribute ableiten, aber keine neuen Entitäten, also hätten wir dieses Konzept vorher in die Datenbank eingefügt. Sobald die Regel ausgelöst wird, ist der Graph derselbe, als hätten wir die neue Beziehung wie im vorherigen Beispiel eingefügt.

Beispiel 4-20. Beispiel TypeDB-Regel
define

rule heparin-non-flush-subset-ndc-63323026201:
when {
    $d isa druginstance, has ndc "63323026201";
    $c isa concept, has purl "http://some-ontology.org/12345";
} then {
    (parent: $c, child: $d) isa hierarchy;
};

Diese Regel stimmt mit allen Medikamenten überein, die den angegebenen NDC-Code und das Konzept haben, mit dem wir sie verknüpfen wollen, und erstellt eine hierarchische Beziehung. Wie du siehst, geben wir nur einen einzigen NDC an. Wir müssten für jede NDC, die wir abgleichen wollen, eine eigene Regel erstellen, da die Bedingungen der TypeDB-Regeln konjunktiv sind. Disjunkte Bedingungen können einfach als separate Regeln erstellt werden.

OR Versus AND Bedingungen

TypeDB-Bedingungsklauseln (der "wenn"-Abschnitt) sind konjunktiv - sie werden mit "UND" verknüpft, sodass alle Bedingungen erfüllt sein müssen, damit die Regel ausgelöst wird. Wenn du disjunktive Bedingungen (mit "OR") angeben musst, musst du für jedeBedingung eine eigene Regel erstellen.

Um die sieben Regeln zu erstellen, die wir für die verschiedenen NDCs benötigen, können wir die Regeln entweder manuell erstellen, wie wir es zuvor getan haben, oder wir können einen kleinen Codeblock schreiben, der die Regeln für uns generiert und sie auf die gleiche Weise lädt, wie wir die Schemas geladen haben. Wir verwenden den gleichen Template-Ansatz wie zuvor, sodass unser Code ziemlich einfach ist, wie wir in Beispiel 4-21 sehen.

Beispiel 4-21. TypeDB-Regeln automatisch generieren
def heparin_ndc_rule_template(ndc):
    return f"""
    define

    rule heparin-non-flush-subset-ndc-{ndc}:
    when {{
        $d isa druginstance, has ndc "{ndc}";
        $c isa concept, has purl "http://some-ontology.org/12345";
    }} then {{
        (parent: $c, child: $d) isa hierarchy;
    }};
    """

ndcs = [
    "63323026201",
    "00641040025",
    "00338055002",
    "00074779362",
    "00264958720",
    "00409779362",
    "63323054207"
]

with TypeDB.core_client("localhost:1729") as client:
    with client.session(db, SessionType.SCHEMA) as session:
        with session.transaction(TransactionType.WRITE) as tx:
            for rule in [heparin_ndc_rule_template(ndc) for ndc in ndcs]:
                tx.query().define(rule)
            tx.commit()

Nachdem wir nun unsere Regeln geladen haben, um bestimmte Medikamente mit unserem neuen Heparin-Konzept zu verbinden, können wir unsere Datenbank abfragen. Wie wir inBeispiel 4-22 sehen, ist die Syntax etwas komplizierter als bei Cypher (in Beispiel 4-11), aber der konzeptionelle Ansatz ist der gleiche.

Beispiel 4-22. Alle Patienten, die an Heparin angeschlossen sind (TypDB)
match
    # Find all drug instances linked to http://some-ontology.org/12345
    $p isa concept, has purl "http://some-ontology.org/12345";
    $c isa druginstance;
    (parent: $p, child: $c) isa hierarchy;

    # Find all prescriptions related to those drugs
    (prescribed_drug: $c, patient: $pt) isa prescription;
get $pt;

Der erste Block der match Anweisung findet unser neues Heparin-Konzept (mithilfe der PURL), findet alle verbundenen Medikamenteninstanzen und findet dann alle mit diesen Medikamenteninstanzen verbundenen Verschreibungen. Wie du siehst, erreichen wir die gleiche Funktionalität wie bei den Eigenschaftsgraphen. Allerdings wird die Verbindung zwischen unserem neuen Heparin-Konzept und den bestehenden Medikamenteninstanzen von der Reasoning Engine abgeleitet und nicht in der Datenbank gespeichert. Unsere Datenbank enthält also die Fakten aus der elektronischen Gesundheitsakte, aber unsere anwendungsspezifische Gruppierung der Medikamente wird in Echtzeit abgeleitet.

Der Hauptvorteil eines solchen Ansatzes ist, dass wir unsere Datenbank nicht mit Mappings überladen, die für eine Untergruppe von Anwendungsfällen spezifisch sind. Wir können Regeln laden und löschen, wenn sich unser Bedarf ändert, und die Datenbank auf die Speicherung unserer Fakten beschränken. Auch wenn das verlockend klingt, müssen wir natürlich abwägen, ob diese technische Lösung tatsächlich unseren Anforderungen entspricht. TypeDB ist inzwischen recht leistungsfähig (im Vergleich zu früheren Versionen, als sie noch Grakn waren), aber das Ziehen von Schlussfolgerungen in Echtzeit kann zu einem Engpass werden. Ob du Wissen als Regeln in den Reasoner oder direkt in den Graphen lädst, hängt letztlich davon ab, wie oft du die Schlussfolgerung nutzen willst, wie wahrscheinlich es ist, dass sie sich ändert, und wie leistungsfähig die jeweilige(n) Regel(n) sind.

Wie wir bei jeder vorherigen Lösung gesehen haben, kann das Problem der Medikamentennormalisierung mit jeder Datenbank gelöst werden. Die Wahl des richtigen Ansatzes hängt weitgehend von anderen Anforderungen ab, von denen viele zu Beginn eines Projekts schwer zu bestimmen sind. Es kann leicht passieren, dass man eine Lösung zu sehr ausreizt, vor allem wenn man versucht, unzureichend definierte Anforderungen zu berücksichtigen. Deshalb ist es wichtig, dass wir uns im Vorfeld die Zeit nehmen, um den Kontext, in dem wir unsere Lösung entwickeln, wirklich zu verstehen.

Arbeiten wir zum Beispiel mit Daten aus einem Krankheitsbereich, der häufig aktualisiert wird? Mit anderen Worten: Ändert sich unser Verständnis der zugrunde liegenden Wissenschaft in der Größenordnung von Monaten oder Jahren? Abgesehen von der Krankheit selbst müssen wir auch die damit verbundene klinische Praxis betrachten. Wir verstehen vielleicht die Pathophysiologie der Krankheit, aber das klinische Management kann sehr komplex oder nuanciert sein.

Generell gilt: Alles, was die Häufigkeit oder Komplexität des Mapping-Prozesses erhöht, erhöht die Wahrscheinlichkeit, dass wir die Daten irgendwann neu mappen müssen. Wir müssen dann abwägen, wie lange es dauern würde, die Daten neu zuzuordnen und diese Zuordnungen dann auf die zugrunde liegenden Daten anzuwenden. Dies ist ein gutes Beispiel für die Überschneidung von Daten und Technik.

Wir müssen ein Gleichgewicht zwischen der Arbeit mit den Daten (Erstellung und Pflege der Mappings) und technischen und betrieblichen Überlegungen (Anwendung der Mappings auf die Daten) finden. Es kann sein, dass der oben beschriebene regelbasierte Ansatz aus Sicht der Daten der beste ist. Was aber, wenn unsere Abfrage dadurch so langsam wird, dass sie unbrauchbar ist? Es kann sein, dass wir diese Zuordnungen beibehalten müssen, nur um die Abfragen nutzbar zu machen, was uns dazu zwingt, Wege zu finden, die Migration der Zuordnungen besser zu verwalten.

Damit ist unsere Rundreise durch unser Heparin-Beispiel im Kontext verschiedener Datenbanktypen abgeschlossen. Wie wir inKapitel 2 kurz besprochen haben, sind RDF-Triple-Stores eine Form von Graphdatenbanken, die auf den Standards des Semantic Web basieren. Wir sind nicht auf spezifische Implementierungsdetails von RDF-Triple-Stores und SPARQL eingegangen, weil der Modellierungsansatz konzeptionell der gleiche ist wie bei Eigenschaftsgraphen undHypergraphen. Im Gegensatz zu Property-Graph-Datenbanken behandeln RDF-Triple-Stores jedoch alles als ein Tripel aus Subjekt-Prädikat-Objekt. Daher können sie etwas komplexer erscheinen. Konkrete Implementierungsbeispiele findest du im GitLab Repo.

Bei RDF-Graphen gibt es ähnliche Vor- und Nachteile wie bei Eigenschaftsgraphen. Da RDF-Triple-Stores jedoch einem etablierten Standard folgen, kann dieser Ansatz es uns erleichtern, unser Wissen mit anderen Systemen zu teilen. Anstatt Triples zu erstellen, die die Beziehung zwischen unserem neuen Konzept und den Daten, wie sie von MIMIC dargestellt werden, direkt erfassen, könnten wir zum Beispiel standardisierte PURLs für die Medikamentenkonzepte verwenden. Diese Triples könnten wir dann an andere senden, um sie in ihre Systeme einzubinden.

Im nächsten Abschnitt erörtern wir kurz, wie wir die bisherigen Ansätze mit Terminologien wie der UMLS oder spezifischen Quellen wie SNOMED CT verbinden können.

Verbinden mit der UMLS

Wie du in den vorangegangenen Beispielen gesehen hast, konnten wir die Daten aus einer elektronischen Gesundheitsakte mit strukturierten Konzepten verbinden. Obwohl wir in unserem Beispiel ein neues Konzept erstellt haben, dem wir unsere eigene Definition und Semantik zugewiesen haben, könnte dies auch ein Konzept aus der UMLS oder einer anderen Terminologie/Ontologie sein.

Im SQL-Beispiel fügen wir dies in der Regel als neue Spalte hinzu, wenn wir noch keine strukturierten Codes oder Konzepte haben. Data Scientists erledigen das in der Regel in ihrem Code (z. B. in R oder Python), nachdem sie die Daten aus der Datenbank exportiert haben. Eine Alternative wäre, das Datenbankschema zu aktualisieren und die strukturierten Konzepte als neue Spalte in der Datenbank oder über komplexere Join-Tabellen (wie bei OMOP und der Integration der Athena-Vokabularien) zu speichern.

Bei der Arbeit mit Graphen hast du jedoch gesehen, wie schnell wir eine neue Verbindung zwischen dem Datenpunkt und einem strukturierten Konzept, das wir erstellt haben, herstellen können. Wenn wir Terminologien in denselben Graphen geladen haben wie unsere Daten, können wir die gleiche Verknüpfung mit bestehenden Konzepten aus der Terminologie vornehmen. Wenn wir mit UMLS arbeiten, können wir Konzepte über CUI statt über eine PURL nachschlagen. Wenn wir mit dem NCBO BioPortal arbeiten, können wir die PURL des BioPortals verwenden. Auch wenn wir direkt mit einer einzelnen Terminologie verlinken, würden wir einfach den eindeutigen Bezeichner der Quelle verwenden (z. B. RXCUI, LOINC-Code, SNOMED CT Identifier).

Um es etwas konkreter zu machen, können wir unser Beispiel in diesem Kapitel mit dem Heparin-Beispiel aus Kapitel 3 kombinieren, wo wir die Begriffe gefunden haben, die dem pharmakologischen Wirkstoff Heparin entsprechen. Außerdem haben wir das therapeutische oder präventive Verfahren "Heparintherapie" identifiziert. Mit den MIMIC-Daten könnten wir, anstatt ein neues Heparin-Konzept zu erstellen und die Medikamente zu verknüpfen, auch einfach den Patienten oder die Aufnahme direkt mit dem Konzept "Heparin-Therapie" verknüpfen. Das wäre nicht nur eine saubere Verknüpfung mit der UMLS, sondern auch semantisch korrekter. Schließlich ist nicht das Medikament selbst entscheidend, ob es therapeutisch ist oder nicht, sondern die Tatsache, dass es als Therapeutikum zur Behandlung des Patienten eingesetzt wurde.

Nachdem wir uns ein wenig in die Technik vertieft haben, wollen wir einen Schritt zurücktreten und im nächsten Abschnitt einige der Schwierigkeiten und Herausforderungen besprechen, die wir bei der Normalisierung strukturierter medizinischer Daten haben.

Schwierigkeiten bei der Normalisierung strukturierter medizinischer Daten

Das in diesem Kapitel besprochene Szenario der Medikamentennormalisierung ist eine relativ einfache Situation. Sicherlich gibt es einige Komplexitäten und potenzielle Unklarheiten bei der Zuordnung eines Medikaments wie Heparin, aber Medikamente sind im Allgemeinen objektive Daten. Viel schwieriger wird es, wenn du mit Daten arbeitest, die subjektiv sind oder für die es keinen klaren Konsens gibt.

Wie wir bereits in diesem Kapitel besprochen haben, ist zum Beispiel die Krebseinteilung ein komplexes Thema. Es gibt nicht nur verschiedene Ansätze für die Einstufung (Gruppeneinteilung oder TNM), sondern auch die AJCC-Richtlinien, die regelmäßig aktualisiert werden. Obwohl die Richtlinien ursprünglich für den menschlichen Gebrauch erstellt wurden (d.h. für Onkologen, die sie lesen, verstehen und in ihrer Praxis anwenden sollten), entwickeln sie sich auch weiter, um den Wandel hin zu einer daten- und rechnergestützten Zukunft widerzuspiegeln.

Wie wirkt sich das nun auf die Datenharmonisierung aus? Wenn wir mit onkologischen Daten arbeiten, für die es Leitlinien zur Stadieneinteilung gibt, müssen wir feststellen, welche Ausgabe/Version verwendet wurde und ob wir die Unterschiede zwischen den Versionen berücksichtigen müssen. Mit anderen Worten: Eine Krebserkrankung, die nach einer Version der AJCC-Richtlinien eingestuft wurde, kann nach einer späteren Version der Richtlinien ein anderes Stadium aufweisen. Wenn wir also Datensätze kombinieren oder Patienten über viele Jahre hinweg analysieren (in denen es eine Aktualisierung der Richtlinien gab), müssen wir darauf achten, wie die Daten kodiert wurden. Natürlich gibt es zu viele Beispiele, um sie alle aufzuzählen, aber ähnliche Nuancen gibt es in allen medizinischen Fachgebieten, daher ist es wichtig, dass wir geeignete Fachleute hinzuziehen, die uns helfen, solche Fallstricke zu erkennen.

Fazit

In diesem Kapitel haben wir uns mit der Arbeit mit elektronischen Gesundheitsdaten befasst und den MIMIC-Datensatz verwendet, um viele der Herausforderungen zu veranschaulichen. Außerdem haben wir uns Synthea angesehen, einen Generator für synthetische Daten. Der MIMIC-Datensatz ist ein echter Datensatz und daher in vielerlei Hinsicht synthetischen Daten vorzuziehen. Er konzentriert sich jedoch auf Daten aus der Intensivpflege und spiegelt daher nicht die Mehrheit der Daten wider, auf die du bei der Arbeit mit EHR-Daten wahrscheinlich stößt. Synthea bietet allgemeinere Daten, die allerdings synthetisch erzeugt werden. Mit Synthea können wir auch Daten in verschiedenen Formaten erhalten, so dass wir Datenumwandlungspipelines testen können.

Der offensichtlichste Kompromiss zwischen den beiden ist, dass wir die MIMIC-Daten sowohl für die Entwicklung von Datenpipelines als auch für die Gewinnung tatsächlicher Erkenntnisse nutzen können - solange unser Anwendungsfall mit den Intensivpflegedaten eines akademischen medizinischen Zentrums übereinstimmt. Andererseits können wir mit den Synthea-Daten zwar Datenpipelines erstellen und testen, sie aber nicht für eine tatsächliche Analyse verwenden.

Die MIMIC-Daten spiegeln auch echte Datenqualitätsprobleme wider, die sich in die EHR-Daten einschleichen, weil sie während der tatsächlichen Patientenversorgung erhoben wurden. Die Synthea-Daten erzeugen zwar Pseudozufallsdaten, aber die Algorithmen, die die Daten erzeugen, wurden in die zugrunde liegende Software eingebaut. Obwohl die Synthea-Daten viele der Probleme, die wir in der realen Welt sehen, annähernd lösen können, können sie immer noch viele Lücken aufweisen.

Im Zusammenhang mit EHR-Daten haben wir uns einige Methoden zur Speicherung und Abfrage der Daten im Rahmen eines Beispiels zur Medikamentenharmonisierung angesehen. So konnten wir SQL- und graphenbasierte Ansätze zur Darstellung von Daten auf Patientenebene vergleichen und gegenüberstellen. Tabelle 4-2 zeigt die Vor- und Nachteile der einzelnenDatenbanktypen.

Tabelle 4-2. Vor- und Nachteile der einzelnen Datenbanktypen
Datenbank Profis Nachteile

PostgreSQL

(SQL)

  • Lässt sich mit bestehenden Analyse- und Business Intelligence-Tools integrieren.

  • SQL ist allgegenwärtig und sowohl für geschäftliche als auch für technische Benutzer zugänglich.

  • Leistung und Skalierbarkeit werden gut verstanden.

  • Strenge Schemata können es schwierig machen, das Daten-/Informationsmodell zu aktualisieren.

  • Die Anpassung des Datenmodells an die unterschiedlichen Geschäftsanforderungen kann schwierig sein.

  • Die Abfrage von graphenartigen Daten kann zu sehr umständlichen und schwer lesbaren SQL-Abfragen führen.

Neo4j/ArangoDB

(Eigenschaftsdiagramm)

  • Natürlich im Einklang mit der Verwendung von Terminologien und Ontologien.

  • Das Schema kann aktualisiert werden, wenn sich das Verständnis der Daten ändert.

  • Graphenalgorithmen können verborgene Erkenntnisse auf der Grundlage von Verbindungen zwischen den Daten und den zugehörigen Terminologien freilegen.

  • NoSQL-Ansätze können zu schlecht verwalteten Schemata führen und fragile Systeme schaffen.

  • Die Leistungsmerkmale hängen stark von der Struktur der Daten ab (z. B. Supernode-Problem).a

GraphDB

(RDF Triple Store)

  • Die Verwendung von semantischen Webstandards macht das Wissen übertragbar (sowohl zwischen Organisationen als auch zwischen Datenbanken).

  • RDF-Graphen sind von Natur aus selbstbeschreibend und ermöglichen Werkzeuge für die Verwaltung des Graphen selbst.

  • Etabliertes Ökosystem von Tools und Bibliotheken für die Interaktion mit RDF-Graphen.

  • Sehr steile Lernkurve und der Eindruck, dass es nur für den akademischen Gebrauch geeignet ist.

  • Das kann zu einer übermäßig komplexen Lösung mit einem hohen Wartungsaufwand führen.

  • Die Leistung kann zu einem Problem werden.

TypDB

(stark typisierter Hypergraph)

  • Ein aussagekräftigeres Graphschema mit starker Typisierung ermöglicht eine umfassendere Modellierung von RWD.

  • Die integrierte Reasoning-Engine erleichtert die Abfrage und Interaktion mit transienten/inferenten Beziehungen und Knotenpunkten.

  • Sehr neue Technologie mit begrenztem Ökosystem an Tools und Support.

  • Integrationen mit Analyse- und Business Intelligence-Tools erfordern zusätzliche Softwareentwicklung.

a ArangoDB: Können Graphdatenbanken skalieren? Ja! Nein! Zeig's uns!

Inzwischen solltest du ein grundlegendes Verständnis für die vielen Probleme haben, die bei der Arbeit mit realen Daten auftreten. Es gibt keine Möglichkeit, alle Nuancen und Herausforderungen zu erfassen, auf die du stoßen könntest, aber ich habe die Herausforderung der Medikamentenharmonisierung gewählt, da sie für viele gängige Probleme repräsentativ ist.

Im nächsten Kapitel setzen wir unser tiefes Eintauchen in die RWD fort, aber wir schalten einen Gang zurück und schauen uns die Schadendaten an, die im Rahmen des Erstattungsprozesses generiert werden. Du denkst vielleicht, dass Anspruchsdaten nur in den Vereinigten Staaten vorkommen, da wir uns stark auf private Versicherer konzentrieren. Aber auch in Ländern mit Einheits- oder verstaatlichten Gesundheitssystemen werden Anspruchsdaten generiert - im Grunde genommen ist der Staat die einzige Versicherungsgesellschaft.

1 Arvind Narayanan und Vitaly Shmatikov, "Robust De-anonymization of Large Datasets (How To Break Anonymity of the Netflix Prize Dataset)", University of Texas at Austin, 2008. https://arxiv.org/abs/cs/0610105.

2 Siehe Open Data Stack Exchange, "MIMIC-III inputevents MV or CV".

3 ACDIS, "Frage und Antwort: Ist ein "Backward Mapping" von ICD-10-CM/PCS auf ICD-9-CM angemessen?"

4 "Asthma Data Analysis Guidance: ICD-9-CM to ICD-10-CM Conversion"," US Centers for Disease Control and Prevention.

5 OHDSI OMOP-Mitarbeiter.

6 Wikipedia, "Separation of Concerns".

7 Das DOI-System verwendet PURLs und bietet eine kurze Diskussion darüber, wie sie PURLs verwenden.

8 Weitere Informationen zu Entitäten, Attributen, Rollen und Beziehungen findest du in der TypeDB-Dokumentation.

Get Praktische Daten zum Gesundheitswesen now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.