zum Blättern · Enter öffnet · Esc schließt

Kontakt aufnehmen.

Projekte

Tumoraggressivität aus PSA, Proteinen und Genen vorhersagen.

Demo20. August 2026

Bereich
Gesundheitswesen
Umsetzung
August 2026
Kompetenz
Machine Learning · Data Engineering
Stack
Python · scikit-learn · pandas · Streamlit · matplotlib · Git

Ergebnis

  • Modellfrage: aggressiver Tumor oder eher ruhig - nicht «liegt Krebs vor?»
  • Das Modell sieht das Pathologenurteil nicht (Gleason, ISUP, Stadium)
  • Nested CV: Alter und PSA geben AUC 0,66; plus Proteine 0,76; plus Gene wieder 0,76
  • Patientenkarte in Streamlit: Wahrscheinlichkeit und Beitrag einzelner Merkmale
  • Offener Code und Reproduktion von null

Kurz: Ich habe die offene Prostatakrebs-Kohorte TCGA genommen: 317 Männer nach der Operation, Alter, PSA, rund 227 Proteine aus dem Tumorgewebe und 25 vorab gewählte Gene. Daraus ist ein System aus drei Modellen derselben Familie geworden: zuerst nur Alter und PSA, dann plus Proteine, dann plus Gene. Das Modell sieht das Urteil des Pathologen nicht und soll sagen, ob der Tumor aggressiv ist (ISUP-Gruppe 3-5) oder eher ruhig (Gruppe 1-2). Ergebnis: Alter und PSA geben AUC 0,66; Proteine heben auf 0,76; Gene verbessern auf dieser Kohorte nichts mehr.

Das ist bewusst eine Demo, kein Kundenprojekt und kein klinischer Test. Kein Medizinprodukt, keine Diagnose. Die Daten sind öffentlich (The Cancer Genome Atlas, Studie prad_tcga). Code: prad-risk. Die Zahlen unten stammen aus der geschachtelten Kreuzvalidierung, nicht aus der Karte in der App.

Welche Frage wir stellen

In dieser Kohorte hat jeder bereits Krebs. Die Modellfrage «liegt Krebs vor?» ist sinnlos: die Antwort ist immer ja, und jede «Trefferquote» wäre ein unechter Sieg.

Nützlicher ist etwas anderes. Bei einem Teil der Männer wächst der Tumor langsam und muss lange nicht das Leben bedrohen. Bei einem Teil ist er aggressiver: er wächst schneller, kehrt häufiger zurück, braucht häufiger eine härtere Behandlung. Der Pathologe sieht das unter dem Mikroskop und setzt eine Note. Diese Note bekommt das Modell nicht. Es bekommt, was sich ohne dieses Urteil messen lässt: Alter, PSA, Proteine, Genaktivität. Die Aufgabe: trifft es den aggressiven Tumor, ohne ins Heft des Pathologen zu schauen?

Das ist absichtlich eine enge Frage. Sie ersetzt kein Screening in der Praxis, keinen Bluttest vor der Operation und kein MRT. Hier ist das Gewebe bereits bei der Operation entnommen. Die Demo zeigt, wie man mehrere Datenschichten zusammenführt, die Antwort nicht in den Eingang mischt, die Vorhersage an Menschen misst, die das Modell nicht gesehen hat, und sichtbar macht, wo eine Schicht hilft und wo nicht.

Woher die Daten kommen und was darin steckt

TCGA (The Cancer Genome Atlas) ist ein großes offenes Projekt: bei Patienten mit verschiedenen Krebsarten wurden eine klinische Akte und Labormessungen aus dem Tumor gesammelt. Ich habe das Adenokarzinom der Prostata genommen, den Satz Firehose Legacy, Kennung prad_tcga.

Drei Schichten, drei Quellen:

  1. Klinik (Portal cBioPortal): Alter und PSA.
  2. Proteine (UCSC Xena): RPPA-Array, rund 227 Antikörper.
  3. Gene (cBioPortal, RNA-seq V2): nicht alle ~20.000 Gene, sondern 25, deren Liste im Code festliegt, bevor man nachschaut, wer aggressiv ist.

PSA (prostataspezifisches Antigen) ist ein Protein, das im Blut gemessen wird. Es ist bei Prostataerkrankungen oft höher, nicht nur bei aggressivem Krebs. In dieser Tabelle heißt das Feld PSA_MOST_RECENT_RESULTS: der «aktuellste» PSA, kein gesondert markierter präoperativer Screening-Test. Für die Demo reicht das, für die Klinik wäre ein solches Feld zu wenig.

RPPA (reverse-phase protein array) ist eine Laborplatte: auf winzigen Gewebe-Spots werden viele Proteine gleichzeitig gemessen. Das ist kein «Protein aus dem Blutröhrchen», sondern Protein im Tumor selbst.

RNA-seq zeigt, wie stark ein Gen «an» ist: wie viel RNA davon abgelesen wird. Ich habe dem Modell nicht das ganze Genom gegeben, damit es selbst sucht, was mit der Antwort korreliert. Bei 317 Menschen findet das fast immer Zufallsrauschen und verkauft es als Entdeckung. Die Liste aus 25 Genen (AR, KLK3, PTEN, TP53 und andere, die beim Prostatakrebs bekannt sind) ist in genes.py eingefroren.

Zuerst stehen in der klinischen Tabelle 500 Patienten, in der Protein-Tabelle 352. Nach der Forderung «Alter da, PSA da, Gleason-Paar da, Proteine da» bleiben 317 Menschen: 194 mit aggressivem Label, 123 mit ruhigem. Das sind keine «Big Data» mehr. Das ist eine kompakte, schmutzige, echte Tabelle, an der man sieht, ob die Methode hält.

Von drei Tabellen zur zitierbaren Zahl Klinik cBioPortal, 500 Akten Alter und PSA PSA «aktuellster» Proteine Xena RPPA, 352 Patienten 227 Antikörper im Gewebe nicht Blut, der Tumor Gene RNA-seq, 25 aus der Liste vor den Labels fixiert nicht alle 20.000 Gene Join und Mauer 317 Personen in der Analyse 194 aggressiv, 123 ruhig Gleason, ISUP, Stadium raus sonst liest das Modell die Antwort Drei Stufen 1. Alter + PSA 2. plus Proteine 3. plus 25 Gene eine Modellfamilie Bewertung nested 5 × 3 AUC 0,66 dann 0,76 Karte getrennt Rechts: Vorhersagen für ungesehene Patienten. Die Streamlit-Karte lernt auf allen 317 und zählt nicht mit.
Der echte Datenweg in diesem Repository: drei öffentliche Tabellen, Filter «alle Schichten da», kein Blick in den Gleason-Score, drei Merkmalsstufen, geschachtelte Prüfung.

Wie das Label «aggressiv» entsteht

Der Pathologe betrachtet die Tumorzellen und setzt einen Gleason-Score. Zwei Zahlen: wie das häufigste Zellmuster aussieht, und wie das zweithäufigste. Jede von 3 bis 5. Zusammen zum Beispiel 3+4 oder 4+3. Die Summe ist dieselbe (7), der Inhalt nicht: bei 4+3 nimmt das schlechtere Muster mehr Fläche ein.

Aus diesem Paar macht die internationale Skala ISUP eine Gruppe von 1 bis 5. Gruppe 1 ist das ruhigste Bild (Klassiker: 3+3). Gruppe 5 ist das schlechteste. Ich nehme die Regel, die in Risikostudien oft verwendet wird: Gruppen 3, 4 und 5 = aggressiv, Gruppen 1 und 2 = eher ruhig.

Warum der Schnitt genau hier? Gruppe 2 ist 3+4. Gruppe 3 ist 4+3. Für den Arzt sind das verschiedene Geschichten. Für das Modell ist das genau das «ja / nein», das es ohne Hinweis vom Mikroskop lernen soll.

def isup_grade_group(primary, secondary):
    """ISUP 1-5 from the two Gleason patterns the pathologist wrote down."""
    score = int(primary) + int(secondary)
    if score <= 6:
        return 1
    if score == 7:
        return 2 if int(primary) == 3 else 3
    if score == 8:
        return 4
    if score >= 9:
        return 5


def is_aggressive(primary, secondary):
    """True when ISUP is 3, 4 or 5."""
    return isup_grade_group(primary, secondary) >= 3

Das ist nicht «ein Netz hat selbst verstanden, was Aggressivität ist». Das ist eine ausdrückliche Abmachung, die ein Test prüfen kann. Wenn sich die Regel einmal ändert (zum Beispiel Gruppe 2 ebenfalls als aggressiv zu zählen), ändert sich das Label, und alle Modellzahlen müssen neu gerechnet werden. So soll es sein: zuerst das Label, dann das Modell.

Was das Modell nicht sieht

Wenn in die Tabelle zufällig eine Spalte GLEASON_SCORE oder ISUP gerät, bekommt das Modell die Antwort im Eingang. Dann wird die AUC schön und der Nutzen null: es liest einfach das Heft des Pathologen.

Das heißt Ziel-Leakage (target leakage): in den «Hinweisen» sitzt bereits, was wir angeblich vorhersagen.

Deshalb fliegt vor dem Training aus der Merkmalsmatrix alles raus, in dessen Namen Gleason, ISUP, Grade, pathologisches Stadium oder klinisches Stadium vorkommt. Es gibt einen Autotest: sobald so eine Spalte trotzdem nach X durchsickert, fällt der Build.

Das Modell sieht Alter, PSA, Proteine, ausgewählte Gene. Es sieht nicht das Mikroskop-Urteil und nicht, wie weit der Tumor nach der Stadienklassifikation schon im Körper ist.

Drei Stufen, nicht ein Merkmalshaufen

Ein großes Modell «alles auf einmal» würde mit einer Zahl antworten. Man sähe nicht, was genau geholfen hat.

Deshalb drei Stufen, dieselbe Modellfamilie:

  1. Alter + PSA.
  2. Alter + PSA + Proteine.
  3. Alter + PSA + Proteine + 25 Gene.

Die erste Stufe ist das, was grob «in der Akte steht». Die zweite fügt das Gewebelabor hinzu. Die dritte fügt Gene hinzu. Wenn Proteine nichts bringen, sieht man das. Wenn Gene über den Proteinen nichts bringen, sieht man das ebenfalls, und das muss man nicht mit schönem Text zudecken.

Das Modell auf jeder Stufe: logistische Regression. Sie «denkt» nicht wie ein Mensch. Sie sucht Gewichte: welche Merkmale die Antwort nach «aggressiv» schieben, welche nach «ruhig», und sammelt sie zu einer Wahrscheinlichkeit von 0 bis 1.

Proteine sind über zweihundert, Menschen 317. Gibt man einer gewöhnlichen Regression so viele Hebel, merkt sie sich die Tabelle, nicht die Regel. Deshalb Elastic-Net: ein Teil der Gewichte wird auf null gedrückt, das Modell muss sparsam sein. Das ist kein Beweis, dass genau diese Proteine in die Klinik gehören. Das ist ein Weg, an der Breite der Tabelle nicht zu fallen.

Vor der Regression werden Lücken mit dem Median gefüllt (typischer Wert der Spalte), Zahlen auf eine gemeinsame Skala gebracht. Sonst zögen Alter in Jahren und ein Protein in Laboreinheiten gegeneinander.

So prüfen wir, ohne uns selbst zu benoten

Der einfache Weg: auf allen 317 trainieren und fragen «na, wie?». Fast immer wird das zu gut. Das Modell hat diese Menschen schon gesehen.

Ein etwas besserer Weg: 20 % «für später» zurücklegen. Besser, aber eine einzige Teilung ist Glück oder Pech, und die Suche nach Einstellungen an denselben Menschen schaut wieder vor.

Hier geschachtelte Kreuzvalidierung (nested cross-validation).

Stellen Sie sich ein Kartenspiel mit 317 Karten vor. Wir teilen in 5 Stapel, sodass in jedem der Anteil Aggressiver und Ruhiger ähnlich ist. Das ist der äußere Kreis. Ein Stapel ist gerade «Prüfung». Auf dem Rest suchen wir, wie stark die Gewichte gedrückt werden (Parameter C: 0,1, 1 oder 10). Diese Suche teilt den Lernteil selbst noch in 3 Teile: der innere Kreis. Wenn die Einstellung steht, setzt das Modell Vorhersagen nur auf den Prüfungsstapel. Dann wird der nächste Stapel zur Prüfung.

Am Ende hat jeder Patient eine Vorhersage von einem Modell, das ihn nicht gesehen hat. Aus diesen Vorhersagen werden alle Zahlen des Artikels gerechnet. Das ist out-of-fold: «außerhalb der eigenen Lernfalte».

Unten dasselbe Ritual im Code. Danach die Zeilen: welches Werkzeug das ist und wozu es hier steht.

# Each patient is scored by a model that did not train on him.
outer = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
inner = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)

for train_idx, test_idx in outer.split(X, y):
    for stage, columns in stage_columns.items():
        search = GridSearchCV(
            make_pipeline(),
            {"clf__C": [0.1, 1.0, 10.0]},
            cv=inner,
            scoring="roc_auc",
        )
        search.fit(X.iloc[train_idx][columns], y[train_idx])
        oof[stage][test_idx] = search.predict_proba(
            X.iloc[test_idx][columns]
        )[:, 1]

StratifiedKFold teilt Menschen in Stapel für die Prüfung. KFold heißt: nicht eine Teilung «Lernen / Prüfung», sondern mehrere (n_splits=5 außen, 3 innen). Jeder Stapel wird der Reihe nach zur Prüfung. Stratified (stratifiziert) fügt die Regel hinzu: in jedem Stapel soll der Anteil Aggressiver und Ruhiger wie in der ganzen Kohorte sein. Ohne das kann zufälliges Mischen in eine Prüfung fast nur Aggressive legen, und die Note springt vom Glück. shuffle=True mischt vor der Teilung, damit die Stapel nicht «so wie in der Tabelle» kommen. random_state=42 hält das Mischen fest: derselbe Code gibt morgen dieselben Stapel.

Die Schleife for train_idx, test_idx in outer.split(X, y) verteilt genau die Zeilennummern: wen jetzt lernen, wen fragen. Die innere Schleife über stage wiederholt das für drei Spaltensätze: nur Alter und PSA, plus Proteine, plus Gene.

make_pipeline() baut eine Kette aus drei Schritten, die immer zusammenlaufen: Lücken füllen, Zahlen auf eine Skala bringen, logistische Regression setzen. Ohne Kette vergisst man leicht die Skala in der Prüfung oder rechnet den Lücken-Median, indem man bei den Prüfungsleuten spickt. Hier kommt der Prüfungsteil in diese Kette nicht hinein, bevor sie auf train_idx gelernt hat.

GridSearchCV (Grid Search Cross-Validation) sucht Einstellungen. Grid ist das Raster der Varianten, die wir selbst aufgezählt haben: wie stark die Gewichte drücken (C = 0,1, 1 oder 10). Search heißt: versuche alle, rate nicht. CV im Namen ist derselbe innere StratifiedKFold auf drei Stapel. Für jedes C lernt er auf zwei inneren Stapeln, prüft auf dem dritten, dreht das dreimal und behält das C, bei dem Aggressive am stabilsten eine höhere Note bekommen als Ruhige. scoring="roc_auc" (Area Under the Receiver Operating Characteristic Curve) sagt: «am besten» ist nicht «wie viel Prozent getroffen», sondern genau dieses Ordnen. Im nächsten Abschnitt hat diese Maßzahl den Namen AUC.

search.fit(...) ist das Lernen. Rein gehen nur Lernzeilen und nur die Spalten dieser Stufe. Hier sieht die Kette Merkmale X und Labels y, probiert drei Werte C über die inneren Stapel und merkt sich den Sieger. Nach fit kann das Objekt search schon vorhersagen, aber die Menschen aus test_idx hat es beim Lernen nicht gesehen.

search.predict_proba(...) fragt nicht «ja oder nein», sondern die Wahrscheinlichkeit. Für jeden Prüfungspatienten gibt das Modell zwei Anteile: Wahrscheinlichkeit «ruhig» und Wahrscheinlichkeit «aggressiv», zusammen 1. Die Schreibweise [:, 1] nimmt die zweite Spalte, also das Risiko eines aggressiven Tumors. Diese Zahlen landen in der Tabelle oof: später werden genau daraus AUC und Grafiken gerechnet, nicht aus der Karte in der App.

Die Karte in Streamlit lernt danach auf allen 317, damit man einen Patienten anklicken und den Merkmalsbeitrag sehen kann. Zitate von der Karte gehen nicht in die Güte-Tabelle. Die Karte ist Erklärung, nested CV ist Bewertung.

Was die Zahlen bedeuten

Das Modell gibt eine Wahrscheinlichkeit, kein Urteil. Das Urteil erscheint erst, wenn wir eine Schwelle setzen: «liegt das Risiko über dieser Marke, zählen wir den Tumor als aggressiv». Von der Schwelle hängt ab, wen wir «fangen» und wen wir unnötig erschrecken. ROC sammelt genau alle Schwellen in ein Bild. AUC drückt das Bild in eine Zahl.

Zuerst vier Zellen. «Positiv» heißt hier aggressiv, weil genau das gesucht wird.

Pathologe: aggressiv Pathologe: ruhig
Modell: aggressiv TP, gefunden FP, Fehlalarm
Modell: ruhig FN, verpasst TN, zu Recht beruhigt

Zwei Anteile aus dieser Tabelle:

  • TPR (true positive rate, Anteil gefundener Aggressiver) = TP / (TP + FN). Von allen, deren Tumor wirklich aggressiv ist, welchen Teil hat das Modell über die Schwelle gehoben. Das ist Sensitivität. 1 heißt: keinen Aggressiven verpasst.
  • FPR (false positive rate, Anteil Fehlalarme) = FP / (FP + TN). Von allen, deren Tumor ruhig ist, welchen Teil hat das Modell trotzdem aggressiv genannt. 0 heißt: keinen Ruhigen unnötig erschreckt.

Die Physik der Schwelle ist einfach. Stellen Sie sich ein Lineal von 0 bis 1 vor, darauf stehen 317 Männer. Jeder steht auf seiner Wahrscheinlichkeit. Der Finger auf dem Lineal ist die Schwelle: alle rechts vom Finger gehen nach «aggressiv».

  • Finger nahe null: «aggressiv» fast alle. TPR nahe 1 (Aggressive gefunden), FPR ebenfalls nahe 1 (Ruhige auch getroffen). Punkt im Bild nahe der rechten oberen Ecke.
  • Finger nahe eins: fast niemanden anfassen. Wenige Fehlalarme und viele Verpasste. Punkt nahe der linken unteren Ecke.
  • Den Finger bewegen und für jede Lage einen Punkt (FPR, TPR) setzen. Die Punkte verbinden. Das ist die ROC-Kurve. Der Name ROC (Receiver Operating Characteristic) kommt aus der Radarortung: wie gut ein Empfänger Signal von Rauschen trennt, bei verschiedenen Einstellungen. Hier ist das Signal der aggressive Tumor.
ROC lesen, vor unseren drei Kurven Strenge Schwelle wenige Alarme, viele Lücken Punkt nahe unten links Weiche Schwelle fast alle heißen aggressiv Punkt nahe oben rechts FPR, Fehlalarme → TPR, gefundene Aggressive Diagonale: Münze, AUC 0,50 besser nach oben links Ideal: alle gefunden, niemand zu viel
Schema, nicht unsere Daten. Die echten drei Kurven stehen in der Grafik darunter. Je näher der Bogen an der linken oberen Ecke, desto besser trennt das Modell Aggressive von Ruhigen, wenn die Schwelle wandert.

AUC (area under the curve, Fläche unter diesem Bogen) faltet die ganze Kurve in eine Zahl von 0 bis 1. Der Alltagssinn ist derselbe wie bei scoring="roc_auc": man nimmt zufällig einen Mann mit aggressivem Tumor und einen mit ruhigem. Mit welcher Wahrscheinlichkeit setzt das Modell dem Aggressiven die höhere Note? 0,50 ist Münzwurf (Diagonale im Schema). 1,00 ist ideale Trennung (Fläche des ganzen Quadrats). AUC fragt nicht, welche Schwelle man wählt: ihr zählt nur die Reihenfolge der Menschen auf dem Lineal. Deshalb geben zwei Modelle mit verschiedenen Punkt-Skalen dieselbe AUC, wenn sie die Patienten gleich ordnen.

Auf den Out-of-Fold-Vorhersagen dieses Projekts:

  • Alter + PSA: AUC 0,66 (95%-Intervall etwa 0,60-0,72)
  • plus Proteine: 0,76 (0,71-0,81)
  • plus Gene: 0,76 (0,70-0,81)

0,66 ist keine Münze mehr, aber schwach. 0,76 ist spürbar besser und trotzdem weit von «kann man ohne Menschen entlassen». In der Medizin ist oft wichtiger, Aggressives nicht zu verpassen (TPR hoch zu halten), auch wenn FPR etwas steigt. ROC zeigt das als Form der Kurve. Welche Schwelle die Klinik nehmen soll, löst diese Demo-Aufgabe nicht: dafür braucht man den Preis eines Verpassens und den Preis eines Fehlalarms, die in der offenen TCGA-Tabelle nicht stehen.

Drei Out-of-Fold-ROC-Kurven: Alter und PSA, plus Proteine, plus Gene
Unsere drei Kurven auf Vorhersagen für Menschen, die das Modell nicht gesehen hat. Der Strich ist die Münze. Die Protein-Kurve liegt über «nur Alter und PSA» und näher an der linken oberen Ecke. Die Kurve mit Genen legt sich fast auf die Protein-Kurve: die dritte Schicht fügt Fläche nicht hinzu.

Das 95%-Intervall kommt hier aus dem Bootstrap: wir werfen die 317 Vorhersagen viele Male neu, rechnen jedes Mal AUC, und schauen, wo die mittleren 95 % dieser Zahlen sitzen. Das Intervall des Protein-Modells reicht nicht an 0,50: das ist kein Zufallsrauschen. Es ist noch breit (rund zehn Hundertstel): Signal ist da, die Genauigkeit der Schätzung selbst mittel. Bei 317 Menschen wird es nicht enger.

Die Intervalle von Proteinen und Genen fallen fast zusammen. Die dritte Schicht verbessert auf dieser Kohorte die Vorhersage nicht. Das ist ein Ergebnis, kein Misserfolg, den man zudecken müsste. Entweder sitzt die Gen-Information schon teilweise in den Proteinen, oder 25 Gene und 317 Menschen tragen kein zusätzliches Signal, oder beides.

Eine eigene Regel für breite Tabellen: Ereignisse pro Variable (EPV). Orientierung für die logistische Regression: etwa 10 aggressive Fälle auf ein Eingangsmerkmal. Bei uns 194 Aggressive und 227 Proteine. Nach dieser Regel ist die Tabelle zu breit. Genau deshalb steht Elastic-Net in der Pipeline. Es lässt das Modell leben, es macht das Protein-Panel nicht zum fertigen medizinischen Test.

AUC sagt, ob das Modell Menschen ordnen kann. Für die Entscheidung «was tun mit diesem Menschen» braucht man noch etwas: ob die Wahrscheinlichkeit glaubwürdig ist. Ein Modell kann die Schlange gut legen und trotzdem mit der Zahl lügen (allen 0,90 schreiben, obwohl 61 % aggressiv sind).

Kalibrierung: wenn das Modell einer Gruppe etwa 70 % setzt, sind dann wirklich etwa 7 von 10 in der Gruppe aggressiv? Die Grafik vergleicht Gesagtes mit Eingetretenem. Je näher an der Diagonalen, desto weniger lügt das Modell mit der Zahl.

Brier ist das mittlere Quadrat des Wahrscheinlichkeitsfehlers. Kleiner ist besser. Proteine verbessern auch das (etwa 0,24 bei der Klinik gegen 0,20 bei den Proteinen).

Kalibrierungskurven der drei Modellstufen
Kalibrierung: waagerecht «was das Modell gesagt hat», senkrecht «welcher Anteil wirklich aggressiv ist». Die Diagonale ist das Ideal.

Die Entscheidungskurve (decision curve) fragt schon nach der Handlung. Stellen Sie sich eine Schwelle vor: «liegt das Risiko über 30 %, handeln wir, als wäre der Tumor aggressiv». Man kann so für alle handeln, für niemanden, oder nur dort, wo das Modell über der Schwelle liegt. Der Nettonutzen bestraft überflüssige Auslösungen: ein Fehlalarm kostet umso mehr, je höher die Schwelle. Liegt die Modellkurve über «alle» und «niemand» auf einem vernünftigen Schwellenbereich, hat das Modell Sinn. Wenn nicht, ist eine einfache Regel ohne es besser.

Nettonutzen-Kurven: alle behandeln, niemanden, oder nach dem Modell
Entscheidungskurve auf denselben Out-of-Fold-Vorhersagen. Das Protein-Modell liegt auf den meisten Schwellen über «nur Alter und PSA».

Bewertung der Stufe: die klinische Schicht ist schwach, aber nicht leer. Die Protein-Schicht gibt den Hauptzuwachs. Die Gen-Schicht ist hier für die Güte überflüssig. Bei 317 chirurgischen Fällen würde ich «Multi-Omics als Pflicht-dritte Stufe» nicht verkaufen. Ich würde sagen: Proteine im Gewebe tragen Signal über PSA hinaus; Gene muss man getrennt prüfen, und in diesem Satz haben sie nicht gewonnen.

Die Patientenkarte: wie das an einem Menschen aussieht

Die Streamlit-App zeigt einen Patienten: Alter, PSA, ISUP-Gruppe (schon als Fakt aus den Daten, nicht als Modelleingang), Wahrscheinlichkeit der Aggressivität auf der gewählten Stufe, und Spalten zum Merkmalsbeitrag. Beitrag heißt: «was hat die Note für diesen Menschen nach oben oder unten geschoben». Technisch ist das SHAP, wo die Bibliothek greift; sonst dieselben Gewichte des linearen Modells. Für den Leser zählt der Unterschied nicht: Grün/Rot ist hier Richtung, keine Diagnose.

Zuerst ein Mann mit ruhigem Tumor (ISUP 1). Auf Alter und PSA setzt das Modell 33 %. Nach den Proteinen fällt es auf 17 %: das Gewebelabor zieht nach unten, passend zum Label.

Patientenkarte TCGA-2A-A8VL: Stufe Alter und PSA, Wahrscheinlichkeit 33 Prozent
TCGA-2A-A8VL, ISUP 1. Nur Alter und PSA: 33 %.
Derselbe Patient nach dem Hinzufügen der Proteine: Wahrscheinlichkeit 17 Prozent
Derselbe Patient, Stufe mit Proteinen: 17 %. Die Proteine haben die Risikoschätzung gesenkt.

Dann ein Mann mit aggressivem Tumor (ISUP 5), PSA 37,36. Mit Proteinen setzt das Modell 97 %. Nach den Genen 96 %: die dritte Schicht ändert fast nichts, wie in der ganzen Kohorte.

Patientenkarte TCGA-CH-5754: Stufe mit Proteinen, Wahrscheinlichkeit 97 Prozent
TCGA-CH-5754, ISUP 5. Alter, PSA und Proteine: 97 %.
Derselbe Patient nach dem Hinzufügen der Gene: Wahrscheinlichkeit 96 Prozent
Derselbe Patient plus Gene: 96 %. An diesem Menschen fügt die dritte Schicht ebenfalls nichts hinzu.

Zwei Karten sind Illustration, kein Beweis. Der Beweis steht oben: 317 Out-of-Fold-Vorhersagen und die Grafiken.

Was dieses Projekt nicht kann - und wozu es trotzdem da ist

Kein Screening gesunder Männer. Kein Test aus dem Röhrchen statt Biopsie. Kein MRT. Keine Heidelberger Daten und keine Serummarker aus einem fremden Paper. Das Gewebe ist chirurgisch, der PSA «aktuellster», die Menschen sind zu wenige für 227 Proteine.

Warum dann veröffentlichen. Damit der Arbeitszyklus sichtbar ist, der in so einer Aufgabe wichtiger ist als die Wahl des «modischsten» Algorithmus:

  1. Die Frage so stellen, dass Daten sie überhaupt beantworten können.
  2. Klinik, Proteine und Gene verbinden, ohne die Antwort in den Eingang zu tauschen.
  3. Schichten vergleichen, statt alles in einem Modell zu verstecken.
  4. Eine Zahl auf Menschen setzen, die das Modell nicht gesehen hat.
  5. Einen Menschen so zeigen, dass klar ist, was die Note verschoben hat.

Reproduktion: Python 3.11, pip install -e ".[dev]", python scripts/run_all.py, danach streamlit run app/streamlit_app.py. Tests prüfen das ISUP-Mapping und die Mauer gegen Leakage. Repository: github.com/KyryloY/prad-risk.

← Alle Projekte