Democase Diagnosehäufigkeiten

Harald G. Grohganz, Visionsberatung GmbH, 24.01.2025 (zuletzt geändert am 30.04.2025)

Eine 1%-Stichprobe der Diagnosen aller gesetzlich Versicherten in Deutschland in den Jahren 2012, 2017 und 2019 – erstellt mithilfe des Public Use File des Forschungsdatenzentrum Gesundheit am Bundesinstitut für Arzneimittel und Medizinprodukte (BfArM).

Das Public Use File ist stark anonymisiert. Alle Spalten wurden separat zufällig permutiert, daher können echte Informationen nur durch Betrachtung einer Einzelspalte gewonnen werden. Wir lesen also pro Datei auch nur eine Spalte ein. Dadurch können wir nur die Diagnosehäufigkeiten auslesen – wir können nicht unterscheiden, ob eine Person eine Diagnose mehrfach bekommen hat oder ob es mehrere Personen waren.

Quellen

  • Public Use File: https://zenodo.org/records/14524120 (DM3/2019: https://zenodo.org/records/15057924)
  • Datensatzbeschreibung: https://fdz-gesundheit.github.io/datensatzbeschreibung_fdz_gesundheit/
  • ICD-Kataloge für die relevanten Jahre:
    https://www.bfarm.de/SharedDocs/Downloads/DE/Kodiersysteme/klassifikationen/icd-10-gm/vorgaenger-bis-2020/icd10gm2012_zip.html
    https://www.bfarm.de/SharedDocs/Downloads/DE/Kodiersysteme/klassifikationen/icd-10-gm/vorgaenger-bis-2020/icd10gm2017_zip.html
    https://www.bfarm.de/SharedDocs/Downloads/DE/Kodiersysteme/klassifikationen/icd-10-gm/vorgaenger-bis-2020/icd10gm2019_zip.html

Vorbereitungen

  1. CSV-Dateien des PUF herunterladen und entzippen.
  2. Lektüre der Datensatzbeschreibung, um die Struktur der Daten nachzuvollziehen.
  3. ICD-Kataloge herunterladen. Das sind sehr umfangreiche ZIP-Archive, uns interessieren nur die txt/csv-Dateien aus dem jew. Ordner beginnend mit x1gm (Metadaten zur ICD im csv-Format). Dort die drei txt-Dateien zu Kodes, Gruppen und Kapiteln entpacken.
In [1]:
import pandas as pd

Einlesen der Daten

Datenmodell 1 und 2 (Berichtsjahre 2012 und 2017)

In [2]:
# Wir brauchen aus der CSV nur die Spalte mit den ICD-Codes
# Daran spielen wir die Infos aus den Metadaten: Jahr & Typ (d.h. stationär oder ambulant)
def read_diagnoses(dm, sa):
    df_tmp = pd.read_csv(dm+"/"+sa+".csv", usecols=[sa+"_ICD_CODE"], dtype=str)
    df_tmp.columns = ["ICD"]
    df_tmp["Jahr"] = "2012" if dm == "DM1" else "2017"
    df_tmp["Typ"] = "stationär" if sa == "SA551" else "ambulant"
    return df_tmp
In [3]:
# Nun lesen wir die einzelnen Rohdatendateien ein und hängen sie in eine große Tabelle untereinander
df_dm1 = pd.concat([
    read_diagnoses("DM1", "SA551"),
    read_diagnoses("DM2", "SA551"),
    read_diagnoses("DM1", "SA651"),
    read_diagnoses("DM2", "SA651")
])
In [4]:
# Um die Dateigröße webtauglich (klein) zu halten, nutzen wir später nur die ersten 3 Stellen der ICD-Codes
df_dm1["ICD3"] = df_dm1["ICD"].str[0:3]
In [5]:
# Ein erster Blick...
df_dm1
Out[5]:
ICD Jahr Typ ICD3
0 Z922 2012 stationär Z92
1 G431 2012 stationär G43
2 Z867 2012 stationär Z86
3 I200 2012 stationär I20
4 C4131 2012 stationär C41
... ... ... ... ...
24335376 K219 2017 ambulant K21
24335377 E6699 2017 ambulant E66
24335378 K529 2017 ambulant K52
24335379 H520 2017 ambulant H52
24335380 R103 2017 ambulant R10

45017329 rows × 4 columns

Datenmodell 3 (Berichtsjahr 2019)

Hier heißen die Tabellen und Spalten etwas anders. Um den Code übersichtlich zu halten, schreiben wir hierfür eine separate Einlesefunktion.
In [6]:
# Wir brauchen aus der CSV nur die Spalte mit den ICD-Codes
# Daran spielen wir die Infos aus den Metadaten: Jahr & Typ (d.h. stationär oder ambulant)
def read_diagnoses(dm, tablename):
    df_tmp = pd.read_csv(dm+"/"+tablename+".csv", usecols=["ICDKH_CODE" if tablename == "KHDIAG" else "ICDAMB_CODE"], dtype=str)
    df_tmp.columns = ["ICD"]
    df_tmp["Jahr"] = "2019"
    df_tmp["Typ"] = "stationär" if tablename == "KHDIAG" else "ambulant"
    return df_tmp
In [7]:
# Nun lesen wir die einzelnen Rohdatendateien ein und hängen sie an die bestehende Tabelle mit dran
df_dm3 = pd.concat([
    read_diagnoses("DM3", "KHDIAG"),
    read_diagnoses("DM3", "AMBDIAG"),
])
In [8]:
# Auch hier: später nutzen wir nur die ersten 3 Stellen der ICD:
df_dm3["ICD3"] = df_dm3["ICD"].str[0:3]
In [9]:
# Auch hier mal reinspicken:
df_dm3
Out[9]:
ICD Jahr Typ ICD3
0 G4738 2019 stationär G47
1 F331 2019 stationär F33
2 D1803 2019 stationär D18
3 D6980 2019 stationär D69
4 H252 2019 stationär H25
... ... ... ... ...
29999995 E713 2019 ambulant E71
29999996 Q210 2019 ambulant Q21
29999997 Z768 2019 ambulant Z76
29999998 M1997 2019 ambulant M19
29999999 E1490 2019 ambulant E14

31562867 rows × 4 columns

In [10]:
# Nun fassen wir die Daten für alle Berichtsjahre zusammen:
df = pd.concat([df_dm1, df_dm3])

Zusammenfassen der Daten

In [11]:
# Daten zusammenfassen - von 76M auf 10k
df_grp = df.groupby(["Typ", "Jahr", "ICD3"]).size().reset_index(name='Anzahl')
In [12]:
df_grp
Out[12]:
Typ Jahr ICD3 Anzahl
0 ambulant 2012 999 6
1 ambulant 2012 A00 43
2 ambulant 2012 A01 178
3 ambulant 2012 A02 1031
4 ambulant 2012 A03 78
... ... ... ... ...
9780 stationär 2019 Z95 24017
9781 stationär 2019 Z96 10146
9782 stationär 2019 Z97 1419
9783 stationär 2019 Z98 3520
9784 stationär 2019 Z99 4364

9785 rows × 4 columns

Die ICD-Informationen

In [13]:
def read_icd(files):
    icd_kodes = pd.read_csv(r"ICD/"+files[0]+".txt", sep=";", dtype=str, header=None)
    icd_grp = pd.read_csv(r"ICD/"+files[1]+".txt", sep=";", dtype=str, header=None)
    icd_kap = pd.read_csv(r"ICD/"+files[2]+".txt", sep=";", dtype=str, header=None)
    
    icd_grp["Gruppe"] = icd_grp[0] + "-" + icd_grp[1]
    icd = pd.merge(pd.merge(icd_kodes[[3,4,7,8]], 
                            icd_grp[[0,3,"Gruppe"]], 
                            how="left", left_on=4, right_on=0),
                   icd_kap, 
                   how="left", left_on="3_x", right_on=0)
    
    icd = icd[[7, 8, "Gruppe", "3_y", "3_x", 1]]
    icd.columns = ["ICD3", "ICD3_Name", "ICD2", "ICD2_Name", "ICD1", "ICD1_Name"]
    
    return icd[icd["ICD3"].str.len() == 3]
In [14]:
icd_files_2019 = ["icd10gm2019syst_kodes", "icd10gm2019syst_gruppen", "icd10gm2019syst_kapitel"]
icd_files_2017 = ["icd10gm2017syst_kodes", "icd10gm2017syst_gruppen", "icd10gm2017syst_kapitel"]
icd_files_2012 = ["icd10gmsyst_kodes2012", "icd10gmsyst_gruppen2012", "icd10gmsyst_kapitel2012"]

icd_2012 = read_icd(icd_files_2012)
icd_2017 = read_icd(icd_files_2017)
icd_2019 = read_icd(icd_files_2019)

icd_2012["ICD_Jahr"] = "2012"
icd_2017["ICD_Jahr"] = "2017"
icd_2019["ICD_Jahr"] = "2019"

df_icd = pd.concat([icd_2012, icd_2017, icd_2019])
df_icd
Out[14]:
ICD3 ICD3_Name ICD2 ICD2_Name ICD1 ICD1_Name ICD_Jahr
0 A00 Cholera A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
4 A01 Typhus abdominalis und Paratyphus A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
10 A02 Sonstige Salmonelleninfektionen A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
16 A03 Shigellose [Bakterielle Ruhr] A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
23 A04 Sonstige bakterielle Darminfektionen A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
... ... ... ... ... ... ... ...
16108 U82 Mykobakterien mit Resistenz gegen Antituberkul... U80-U85 Infektionserreger mit Resistenzen gegen bestim... 22 Schlüsselnummern für besondere Zwecke 2019
16112 U83 Candida mit Resistenz gegen Fluconazol oder Vo... U80-U85 Infektionserreger mit Resistenzen gegen bestim... 22 Schlüsselnummern für besondere Zwecke 2019
16113 U84 Herpesviren mit Resistenz gegen Virustatika U80-U85 Infektionserreger mit Resistenzen gegen bestim... 22 Schlüsselnummern für besondere Zwecke 2019
16114 U85 Humanes Immundefizienz-Virus mit Resistenz geg... U80-U85 Infektionserreger mit Resistenzen gegen bestim... 22 Schlüsselnummern für besondere Zwecke 2019
16115 U99 Nicht belegte Schlüsselnummer U99 U99-U99 Nicht belegte Schlüsselnummern 22 Schlüsselnummern für besondere Zwecke 2019

5129 rows × 7 columns

Abschluss: Daten mit ICD-Hierarchie anreichern

In [15]:
df_final = df_grp.merge(df_icd, left_on=["ICD3","Jahr"], right_on=["ICD3", "ICD_Jahr"], how="left")

df_final
Out[15]:
Typ Jahr ICD3 Anzahl ICD3_Name ICD2 ICD2_Name ICD1 ICD1_Name ICD_Jahr
0 ambulant 2012 999 6 NaN NaN NaN NaN NaN NaN
1 ambulant 2012 A00 43 Cholera A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
2 ambulant 2012 A01 178 Typhus abdominalis und Paratyphus A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
3 ambulant 2012 A02 1031 Sonstige Salmonelleninfektionen A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
4 ambulant 2012 A03 78 Shigellose [Bakterielle Ruhr] A00-A09 Infektiöse Darmkrankheiten 01 Bestimmte infektiöse und parasitäre Krankheiten 2012
... ... ... ... ... ... ... ... ... ... ...
9780 stationär 2019 Z95 24017 Vorhandensein von kardialen oder vaskulären Im... Z80-Z99 Personen mit potentiellen Gesundheitsrisiken a... 21 Faktoren, die den Gesundheitszustand beeinflus... 2019
9781 stationär 2019 Z96 10146 Vorhandensein von anderen funktionellen Implan... Z80-Z99 Personen mit potentiellen Gesundheitsrisiken a... 21 Faktoren, die den Gesundheitszustand beeinflus... 2019
9782 stationär 2019 Z97 1419 Vorhandensein anderer medizinischer Geräte ode... Z80-Z99 Personen mit potentiellen Gesundheitsrisiken a... 21 Faktoren, die den Gesundheitszustand beeinflus... 2019
9783 stationär 2019 Z98 3520 Sonstige Zustände nach chirurgischem Eingriff Z80-Z99 Personen mit potentiellen Gesundheitsrisiken a... 21 Faktoren, die den Gesundheitszustand beeinflus... 2019
9784 stationär 2019 Z99 4364 Abhängigkeit (langzeitig) von unterstützenden ... Z80-Z99 Personen mit potentiellen Gesundheitsrisiken a... 21 Faktoren, die den Gesundheitszustand beeinflus... 2019

9785 rows × 10 columns

In [16]:
# Spalten umsortieren und abspeichern
df_final = df_final[["ICD1", "ICD1_Name", "ICD2", "ICD2_Name", "ICD3", "ICD3_Name", "Typ", "Jahr", "Anzahl"]]
df_final.to_csv("diagnosen_PUF.csv", index=False, encoding='utf8')
In [ ]: