Überlebensdaten für Kaplan-Meier-Plots in Python simulieren
Bibliotheken wie lifelines bieten eine Fülle von Beispieldatensätzen, mit denen man arbeiten kann. Für viele Aufgaben musst du jedoch spezifisches Verhalten in Überlebenskurven simulieren.
In diesem Post demonstrieren wir einen einfachen Algorithmus zum Generieren von Überlebensdaten in einem Format, das mit dem in den lifelines-Beispieldatensätzen wie load_leukemia() verwendeten vergleichbar ist.
Der Generierungsalgorithmus basiert auf den folgenden Annahmen:
- Es gibt ein striktes Überlebensplateau mit einer gegebenen Überlebenswahrscheinlichkeit ab einem gegebenen Zeitpunkt
- Der Verlauf von 100% Überleben, t=0 zum Überlebensplateau ist näherungsweise linear (d.h. wenn man eine unendliche Anzahl von Datenpunkten generieren würde, wäre die Überlebenskurve linear)
- Keine Zensierungsereignisse sollen generiert werden, außer der Zensierung aller überlebenden Teilnehmer am Endpunkt der Zeitleiste.
Code:
import numpy as np
import random
from lifelines import KaplanMeierFitter
def simulate_survival_data_linear(N, survival_plateau, t_plateau, t_end):
"""
Generiert zufällige simulierte Überlebensdaten mit einem linearen Modell
Schlüsselparameter
------------------
N : integer
Anzahl der zu generierenden Einträge
survival_plateau : float
Die Überlebenswahrscheinlichkeit des Überlebensplateaus
t_plateau : float
Der Zeitpunkt, an dem das Überlebensplateau beginnt
t_end : float
Der Zeitpunkt, an dem alle überlebenden Teilnehmer zensiert werden.
Returns
-------
Ein Dict mit "Time"- und "Event"-NumPy-Arrays: 0 ist zensiert, 1 ist Ereignis
"""
data = {"Time": np.zeros(N), "Event": np.zeros(N)}
for i in range(N):
r = random.random()
if r <= survival_plateau:
# Ereignis ist Zensierung am Ende des Zeitraums
data["Time"][i] = t_end
data["Event"][i] = 0
else: # Ereignis tritt ein
# Normalisieren, wo wir uns zwischen 100% und dem Überlebensplateau befinden
p = (r - survival_plateau) / (1 - survival_plateau)
# Lineares Modell: Zeitpunkt des Ereignisses hängt linear von der gleichmäßig & zufällig gewählten Position
# im Bereich (0...tplateau) ab
t = p * t_plateau
data["Time"][i] = t
data["Event"][i] = 1
return data
# Beispielverwendung
data1 = simulate_survival_data_linear(250, 0.2, 18, 24)
data2 = simulate_survival_data_linear(250, 0.4, 17.2, 24)Gegeben data1 und data2 (siehe das Verwendungsbeispiel am Ende des Codes) kannst du sie plotten mit
# Schlechte Untergruppe plotten
kmf1 = KaplanMeierFitter()
kmf1.fit(data1["Time"], event_observed=data1["Event"], label="Bad subgroup")
ax = kmf1.plot()
# Gute Untergruppe plotten
kmf2 = KaplanMeierFitter()
kmf2.fit(data2["Time"], event_observed=data2["Event"], label="Good subgroup")
ax = kmf2.plot(ax=ax)
# Y-Achse auf feste Skalierung setzen
ax.set_ylim([0.0, 1.0])Kein Überlebensplateau gewünscht?
Setze einfach t_end = t_survival:
# Beispielverwendung
data1 = simulate_survival_data_linear(250, 0.2, 24, 24)
data2 = simulate_survival_data_linear(250, 0.4, 24, 24)
# Code zum Plotten: Siehe obenWas passiert bei einer geringen Teilnehmerzahl?
Verwenden wir 25 statt 250 wie oben:
# Beispielverwendung
data1 = simulate_survival_data_linear(25, 0.2, 24, 24)
data2 = simulate_survival_data_linear(25, 0.4, 24, 24)
# Plot-Code: Siehe obenObwohl wir die Daten mit denselben Daten generiert haben, ist der Unterschied in diesem Beispiel viel weniger deutlich, insbesondere gegen Ende der Zeitleiste (beachte jedoch, dass die Daten zufällig generiert werden, sodass du ein anderes Ergebnis sehen könntest). Man sieht einen großen Teil der überlappenden Konfidenzintervalle nahe t=24. Mit anderen Worten, basierend auf diesen Daten ist es nicht klar, dass sich die beiden Patientengruppen signifikant unterscheiden (mit anderen Worten, $P \geq 0.05$)