
La figure de Kaplan-Meier en R : construire la courbe de survie clinique à partir d’un ADTTE
Construisez la figure de Kaplan-Meier de qualité publication qu’expédie un rapport d’étude clinique — courbes de survie par bras de traitement avec marques de censure, table des effectifs à risque, survie médiane et p-value du log-rank — en R à partir d’un ADTTE CDISC avec survminer
Un tutoriel complet et exécutable pour la figure de Kaplan-Meier clinique — la courbe de survie qui entre dans un rapport d’étude clinique. Découvrez ce que la figure doit montrer (courbes par bras de traitement, marques de censure, table des effectifs à risque, survie médiane, et la p-value du log-rank avec le hazard ratio), puis construisez-la à partir d’un jeu de données de délai jusqu’à l’événement ADTTE CDISC sur des données pharmaverse publiques avec le ggsurvplot(risk.table, conf.int, pval, surv.median.line) de survminer — l’appel unique qui assemble tout le panneau. Inclut la correspondance CNSR-vers-événement (Surv(AVAL, 1 - CNSR)), comment lire les médianes et le test du log-rank, et les pièges courants (sens de CNSR, l’axe d’unité de temps, l’alignement de la table des effectifs à risque).
- La figure de Kaplan-Meier (KM) est la figure d’efficacité phare de nombreux rapports d’étude clinique (CSR) — la courbe de survie par bras de traitement pour la survie globale (OS) ou la survie sans progression (PFS). Elle figure dans la section 14 du CSR et constitue souvent la sortie la plus scrutée de la soumission.
- Une figure KM de qualité soumission montre six choses : les courbes en marches par bras, les marques de censure, une table des effectifs à risque sous l’axe des x, la survie médiane par bras, la p-value du log-rank, et — couplée au modèle de délai jusqu’à l’événement — le hazard ratio (HR).
- Elle lit un ADTTE CDISC (Clinical Data Interchange Standards Consortium). Le jeu de données ADaM (Analysis Data Model) de délai jusqu’à l’événement vous donne
AVAL(le temps) etCNSR(l’indicateur de censure). La convention CDISC estCNSR = 0= événement, un entier positif = censuré, si bien que chaque appel de survie l’inverse :Surv(AVAL, 1 - CNSR). - Un seul appel construit tout le panneau. Le
ggsurvplot()de survminer assemble chaque élément que la convention demande — courbes + bandes de confiance + table des effectifs à risque + p-value du log-rank + lignes médianes + marques de censure — à partir de l’objetsurvfiten un seul appel de fonction, ce qui en fait la fonction de tracé de survie la plus utilisée en R. - La figure se couple à un modèle. La courbe et la p-value du log-rank décrivent et comparent ; le HR avec son intervalle de confiance (CI) provient du modèle de Cox ajusté sur le même ADTTE — rapportez les deux.
Introduction
Dans un essai en oncologie, une figure porte le résultat : la courbe de survie de Kaplan-Meier. C’est la première chose vers laquelle un évaluateur se tourne, la figure qui apparaît dans le communiqué de presse, et l’image derrière chaque « survie globale médiane de 18.7 mois, hazard ratio 0.74 ». La construire correctement — à partir d’un jeu de données CDISC (Clinical Data Interchange Standards Consortium) conforme, avec les marques de censure, la table des effectifs à risque et les statistiques qu’un régulateur attend sur le panneau — est une compétence centrale du reporting clinique.
Cette leçon construit cette figure en R à partir d’un ADTTE (le jeu de données d’analyse ADaM de délai jusqu’à l’événement) avec survminer — dont le ggsurvplot() est le moteur derrière des milliers de figures de survie publiées et assemble le panneau de soumission entier en un seul appel. Nous travaillons sur des données pharmaverse publiques pour que chaque ligne s’exécute telle quelle.
Voici la figure que nous construisons — courbes de survie par bras de traitement avec bandes de confiance, les guides médians, les marques de censure et la p-value du log-rank sur le panneau (la version complète ajoute la table des effectifs à risque ci-dessous) :
À la fin, vous produirez cette figure exacte et saurez lire chaque élément qu’elle porte. Si les mots « ADTTE », « ADaM » ou « CNSR » sont nouveaux, construisez d’abord le jeu de données dans Construire le jeu de données ADaM de délai jusqu’à l’événement (ADTTE) ; pour la méthode de survie derrière la courbe — l’estimateur, la mécanique du log-rank, le modèle de Cox — la leçon Estimation de Kaplan-Meier du pilier biostatistique va jusqu’au fond.
Ce que la figure doit montrer
Une figure KM n’est pas qu’un tracé de deux courbes. Un régulateur la lit comme un résumé autonome d’un critère de délai jusqu’à l’événement, si bien que la ligne directrice ICH E3 sur les rapports d’étude clinique (qui définit la section d’efficacité où se trouve la figure) et des décennies de convention fixent son anatomie. Chaque figure KM de soumission porte les mêmes six éléments :
| Élément | Ce que c’est | Pourquoi un évaluateur en a besoin |
|---|---|---|
| Courbes en marches par bras | Une courbe de survie par bras de traitement | La comparaison phare : quel bras fait mieux, et de combien |
| Marques de censure | Une petite marque là où un sujet est censuré | Montre le suivi, pas les événements — distingue « toujours sans événement » de « a eu l’événement » |
| Table des effectifs à risque | Le nombre encore à risque sous chaque temps | Vous dit combien de données soutiennent la queue (les courbes deviennent peu fiables à mesure qu’elle s’amincit) |
| Survie médiane | Le temps où chaque courbe croise 0.5 | Le résumé unique le plus rapporté d’une courbe de survie |
| P-value du log-rank | Le test de « les courbes diffèrent-elles ? » | La preuve formelle que la survie des bras n’est pas la même |
| Hazard ratio (HR) + CI | La taille d’effet du modèle de Cox | Une amplitude, pas juste un oui/non — le nombre qui porte la conclusion |
Les cinq premiers vivent sur la figure ; le HR provient du modèle de délai jusqu’à l’événement ajusté sur le même jeu de données et est généralement imprimé à côté. Nous construisons les six ci-dessous.
Les données : un ADTTE CDISC
La figure lit un ADTTE — le jeu de données de délai jusqu’à l’événement prêt pour l’analyse, au standard CDISC ADaM (Analysis Data Model). Il suit la Basic Data Structure (BDS), une ligne par sujet par paramètre, et porte les deux colonnes dont une courbe de survie a besoin : AVAL (la valeur d’analyse — le temps jusqu’à l’événement) et CNSR (l’indicateur de censure). Nous le dérivons ici en quelques appels admiral ; la dérivation complète et annotée est la leçon ADTTE — cette leçon la réutilise et se concentre sur la figure.
Le pilote CDISC public livré dans pharmaverseadam est une étude de sécurité à faible mortalité, si bien que sa courbe de survie globale est presque plate — convenable pour enseigner une dérivation, inutile comme figure. Pour obtenir une courbe interprétable et bien séparée, nous utilisons son critère de délai jusqu’au premier événement indésirable, qui a les événements dont une vraie figure a besoin. Le code de la figure est identique pour l’OS ou la PFS ; seul le PARAMCD (le code de paramètre qui nomme le critère) change.
library(admiral)
library(dplyr, warn.conflicts = FALSE)
library(pharmaversesdtm)
library(pharmaverseadam)
library(lubridate)
adsl <- pharmaverseadam::adsl
ae <- pharmaversesdtm::ae %>% convert_blanks_to_na()
# Treatment-emergent AEs: bring treatment dates, impute the onset date, flag emergence
adae <- ae %>%
derive_vars_merged(dataset_add = adsl, new_vars = exprs(TRTSDT, TRTEDT),
by_vars = exprs(STUDYID, USUBJID)) %>%
derive_vars_dt(new_vars_prefix = "AST", dtc = AESTDTC, highest_imputation = "M") %>%
mutate(TRTEMFL = if_else(ASTDT >= TRTSDT & ASTDT <= TRTEDT + days(30), "Y", NA_character_))
# Event = first AE; censor = end of treatment + 30 days for subjects with none
ttae <- event_source(dataset_name = "ae", date = ASTDT, order = exprs(AESEQ),
set_values_to = exprs(EVNTDESC = "ADVERSE EVENT", SRCDOM = "ADAE", SRCVAR = "ASTDT"))
eot <- censor_source(dataset_name = "adsl", date = TRTEDT + days(30),
set_values_to = exprs(EVNTDESC = "END OF TREATMENT", SRCDOM = "ADSL", SRCVAR = "TRTEDT"))
adtte <- derive_param_tte(
dataset_adsl = adsl, start_date = TRTSDT,
event_conditions = list(ttae), censor_conditions = list(eot),
source_datasets = list(adsl = adsl, ae = filter(adae, TRTEMFL == "Y")),
set_values_to = exprs(PARAMCD = "TTAE", PARAM = "Time to First Adverse Event")) %>%
derive_vars_duration(new_var = AVAL, start_date = STARTDT, end_date = ADT, out_unit = "days") %>%
derive_vars_merged(dataset_add = adsl, new_vars = exprs(TRT01A),
by_vars = exprs(STUDYID, USUBJID)) %>%
filter(TRT01A != "Screen Failure") %>%
# fix the arm order so the figure reads placebo first, then ascending dose
mutate(TRT01A = factor(TRT01A,
levels = c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")))
adtte %>%
select(USUBJID, PARAMCD, AVAL, CNSR, TRT01A) %>%
head(5)# A tibble: 5 × 5
USUBJID PARAMCD AVAL CNSR TRT01A
<chr> <chr> <dbl> <int> <fct>
1 01-701-1015 TTAE 2 0 Placebo
2 01-701-1023 TTAE 3 0 Placebo
3 01-701-1028 TTAE 3 0 Xanomeline High Dose
4 01-701-1033 TTAE 44 1 Xanomeline Low Dose
5 01-701-1034 TTAE 58 0 Xanomeline High Dose
Voilà un ADTTE conforme : une ligne par sujet, AVAL contenant les jours jusqu’à l’événement, CNSR indiquant événement-versus-censure, et le bras de traitement attaché pour la séparation. Lisez l’indicateur de censure avant de tracer :
adtte %>%
count(CNSR) %>%
mutate(meaning = if_else(CNSR == 0, "Event (had an AE)", "Censored (no AE)"))# A tibble: 2 × 3
CNSR n meaning
<int> <int> <chr>
1 0 217 Event (had an AE)
2 1 35 Censored (no AE)
C’est la convention qui fait trébucher chaque nouveau venu. Dans un ADTTE CDISC, CNSR = 0 signifie que l’événement est survenu et un entier positif signifie censuré — l’opposé du package survival, qui attend 1 = événement. CDISC recommande fortement cette convention CNSR = 0 pour les événements, et admiral l’impose. Ainsi, chaque appel de survie ci-dessous utilise Surv(AVAL, 1 - CNSR), qui réinverse CNSR vers le statut que veut le package survival. Trompez-vous sur cette seule expression et votre courbe se retrouve à l’envers.
Estimer les courbes : survfit()
La figure est une image d’un objet survfit. Construisez-le avec la formule standard — la réponse Surv() à gauche, la variable de regroupement à droite — sur le package survival :
library(survival)
fit <- survfit(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)
fitCall: survfit(formula = Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)
n events median 0.95LCL 0.95UCL
TRT01A=Placebo 85 65 33 26 60
TRT01A=Xanomeline Low Dose 95 84 18 15 27
TRT01A=Xanomeline High Dose 72 68 16 14 23
L’impression donne déjà les chiffres phares. Par bras, elle rapporte n, le nombre d’events, et la médiane de survie avec son intervalle de confiance à 95 % : le délai médian jusqu’au premier événement indésirable est de 33 jours sous placebo (95% CI 26–60), contre 18 jours sous Xanomeline Low Dose (95% CI 15–27) et 16 jours sous Xanomeline High Dose (95% CI 14–23). Les sujets sous placebo restent sans événement indésirable environ deux fois plus longtemps. Ce sont les médianes que les lignes pointillées de la figure marqueront.
Construisez-la à la façon survminer : ggsurvplot()
Le ggsurvplot() de survminer construit le panneau entier en un seul appel. Vous lui passez le fit et les données, puis activez chaque élément que la convention demande :
conf.int = TRUE— les bandes de confiance à 95 % ombrées ;risk.table = TRUE— la table des effectifs à risque sous le tracé ;pval = TRUE— la p-value du log-rank sur le panneau ;surv.median.line = "hv"— les guides pointillés de survie médiane ;censor = TRUE(le défaut) — les marques de censure ;palette = "jco"— une palette de revue adaptée au daltonisme.
library(survival)
library(survminer)
fit <- survfit(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)
ggsurvplot(
fit,
data = adtte,
risk.table = TRUE, # number at risk under the plot
conf.int = TRUE, # 95% confidence bands
pval = TRUE, # log-rank p-value on the panel
surv.median.line = "hv", # dashed median-survival guides
censor = TRUE, # censoring ticks (the default)
palette = "jco", # colourblind-safe journal palette
legend.labs = c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose"),
legend.title = "Treatment arm",
xlab = "Days from first dose",
ylab = "Probability of being adverse-event-free",
risk.table.height = 0.28,
ggtheme = theme_minimal()
)
Voilà une figure de forme soumission à partir d’une seule fonction. Les courbes se séparent nettement, le bras placebo se place au plus haut, les lignes pointillées descendent vers la médiane de chaque bras, la p-value du log-rank est sur le panneau, et la table des effectifs à risque montre les comptages s’amincir au fil du temps. ggsurvplot() est la route la plus rapide vers une figure KM complète et soignée, ce qui en fait la fonction de tracé de survie la plus utilisée en R.
Personnaliser la figure
Le ggsurvplot() vous donne le panneau fini en un seul appel, mais il ne vous verrouille pas hors de ggplot. L’objet qu’il retourne est une liste de ggplots — $plot est la courbe et $table est la bande des effectifs à risque — si bien que vous pouvez continuer à styliser après coup :
- Atteignez n’importe quelle couche ggplot.
p <- ggsurvplot(fit, data = adtte, ...), puisp$plot + labs(title = "Overall survival")ou+ theme(legend.position = "bottom")— la courbe est un ggplot normal, si bien que n’importe quelle échelle, annotation ou thème se compose dessus. ggpar()restyle tout le panneau (polices, texte d’axe, légende) sans plonger dans la liste.- Réglez l’anatomie via les arguments :
surv.median.line = "hv"pour les guides médians,break.time.by =pour les graduations d’axe (la table des effectifs à risque en hérite),risk.table.height =pour la part de la table dans le panneau, ettables.theme = theme_cleantable()pour un style de table des effectifs à risque minimal.
Tout ce que la convention demande est un argument de ggsurvplot() ; tout ce qui va au-delà n’est qu’à une couche ggplot de distance.
Lire la figure : médianes, séparation, p-value et hazard ratio
Une figure KM se lit en quatre mouvements.
Les médianes sont les lignes pointillées : 33 jours sous placebo contre 16–18 jours sur les bras actifs. La moitié des sujets sous placebo sont encore sans événement indésirable à 33 jours, contre la moitié des sujets sous Xanomeline à un peu plus de deux semaines.
La séparation est l’histoire visuelle — la courbe placebo se place au-dessus des deux courbes actives à chaque point du temps, jusqu’au bout. Une séparation constante dans une direction est ce qui rend la comparaison fiable (des courbes qui se croisent la briseraient).
La p-value du log-rank formalise cet écart. Calculez-la directement :
library(survival)
survdiff(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)Call:
survdiff(formula = Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)
N Observed Expected (O-E)^2/E (O-E)^2/V
TRT01A=Placebo 85 65 98.1 11.15 22.3
TRT01A=Xanomeline Low Dose 95 84 70.1 2.76 4.3
TRT01A=Xanomeline High Dose 72 68 48.8 7.51 10.2
Chisq= 23.4 on 2 degrees of freedom, p= 8e-06
La statistique du log-rank est χ² = 23.4 à 2 degrés de liberté, p ≈ 8 × 10⁻⁶ — bien en dessous de 0.001. Les trois courbes ne sont pas les mêmes ; la différence est hautement improbable sous l’hypothèse nulle d’une survie identique.
Le hazard ratio transforme « ils diffèrent » en « de combien ». Le test du log-rank ne donne aucune taille d’effet, alors ajustez le modèle de Cox à risques proportionnels sur le même ADTTE — le modèle que l’analyse de délai jusqu’à l’événement couple à la figure :
library(survival)
cox <- coxph(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte)
summary(cox)$conf.int exp(coef) exp(-coef) lower .95 upper .95
TRT01AXanomeline Low Dose 1.899640 0.5264155 1.362864 2.647831
TRT01AXanomeline High Dose 2.222727 0.4498977 1.568091 3.150657
Relativement au placebo (la référence), le risque d’un premier événement indésirable est de HR 1.90 (95% CI 1.36–2.65) sous Low Dose et de HR 2.22 (95% CI 1.57–3.15) sous High Dose. Clairement : les sujets sous Xanomeline atteignent leur premier AE environ deux fois plus vite que les sujets sous placebo, et les deux CI excluent 1, donc l’effet est significatif. Le HR est le nombre qui a sa place à côté de la figure dans le rapport.
Cet exemple travaillé est un bilan de sécurité (délai jusqu’au premier AE), donc « mieux » signifie plus haut — le bras placebo reste sans événement le plus longtemps. Pour un critère d’efficacité (OS, PFS), le code identique produit la figure, mais vous attendez que le bras actif se place plus haut et un HR protecteur en dessous de 1. La mécanique ne change jamais ; seule la direction clinique change.
Rapport
Le délai jusqu’au premier événement indésirable apparu sous traitement différait significativement entre les trois bras (log-rank χ²(2) = 23.4, p < 0.001). Le délai médian jusqu’au premier événement indésirable était de 33 jours (95% CI 26–60) sous placebo, de 18 jours (95% CI 15–27) sous Xanomeline Low Dose et de 16 jours (95% CI 14–23) sous Xanomeline High Dose. Relativement au placebo, le risque d’un premier événement indésirable était plus élevé sur les deux bras actifs (Low Dose HR 1.90, 95% CI 1.36–2.65 ; High Dose HR 2.22, 95% CI 1.57–3.15).
Ce paragraphe — estimation, intervalle, test, taille d’effet — est ce qui accompagne la figure dans le CSR.
Les deux comparent la survie entre les bras, et ils répondent à des questions différentes. Le test du log-rank (survdiff()) est un test non paramétrique de l’hypothèse nulle « les courbes de survie sont identiques » — il renvoie une statistique χ² et une p-value, mais aucune taille d’effet et aucun moyen d’ajuster sur des covariables. Le modèle de Cox à risques proportionnels (coxph()) estime un hazard ratio par bras — une amplitude avec un intervalle de confiance — et peut ajuster sur l’âge, le sexe, les facteurs de stratification, et plus encore dans le même ajustement. Dans un CSR, vous rapportez typiquement les deux : la p-value du log-rank comme test primaire de différence, et le HR de Cox (souvent stratifié, selon le plan d’analyse statistique) comme taille d’effet. Ils concordent ici — une p-value du log-rank minuscule et des HR bien au-dessus de 1 disent tous deux que les bras diffèrent. Les leçons test du log-rank et risques proportionnels de Cox couvrent chacun en profondeur.
Demandez à Prova « comment ajouter une annotation de hazard ratio stratifié à ma figure de Kaplan-Meier survminer pour un critère OS ? » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code survminer exécutable que vous pouvez essayer sur l’ADTTE pharmaverse d’exemple. The runtime is the judge. Ask Prova →
Problèmes fréquents
Votre courbe est à l’envers (elle grimpe vers 1.0 au lieu de descendre). Vous avez passé CNSR directement comme statut d’événement. L’ADTTE utilise CNSR = 0 = événement, mais survival veut 1 = événement. Utilisez toujours Surv(AVAL, 1 - CNSR). Une courbe qui monte est presque toujours cette convention inversée.
L’axe des x est dans la mauvaise unité de temps. AVAL est dans l’unité que l’ADTTE a stockée (ici, des jours — vérifiez AVALU). Si votre étude rapporte la survie en mois ou en années, la courbe, les lignes médianes et l’étiquette de l’axe des x doivent toutes utiliser cette unité. Convertissez AVAL une fois (AVAL / 30.4375 pour les mois, AVAL / 365.25 pour les années) avant survfit(), et réglez xlab en conséquence — ne réétiquetez jamais l’axe sans réajuster l’échelle des données, ou les médianes de la figure contrediront la table.
La table des effectifs à risque ne s’aligne pas sous la courbe. La table des effectifs à risque doit partager les graduations et les limites de l’axe des x de la courbe, sinon les comptages se placent sous les mauvais temps. Dans ggsurvplot(), réglez l’axe une fois avec break.time.by = et xlim = et la table en hérite — ne mettez jamais à l’échelle la courbe et la table séparément. Ajustez la hauteur de la table avec risk.table.height.
Questions fréquentes
Ajustez les courbes avec survfit(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte) sur votre ADTTE CDISC, puis dessinez la figure avec le ggsurvplot() de survminer — activez risk.table = TRUE, conf.int = TRUE, pval = TRUE et surv.median.line = "hv" et tout le panneau de soumission sort d’un seul appel. C’est travaillé ci-dessus sur des données pharmaverse publiques. Le 1 - CNSR inverse la convention de censure CDISC vers le statut d’événement que survival attend.
Parce que vous avez utilisé CNSR directement comme statut. Dans un ADTTE CDISC, CNSR = 0 est l’événement et un entier positif est censuré — l’inverse du package survival, qui traite 1 comme l’événement. Utilisez Surv(AVAL, 1 - CNSR) pour que les événements et les censures soient codés comme survfit() l’attend, et la courbe descend correctement depuis 1.0.
ggsurvplot() retourne une liste de ggplots — $plot est la courbe, $table est la bande des effectifs à risque — si bien que vous pouvez continuer à styliser après l’appel : p <- ggsurvplot(fit, data = adtte, ...), puis p$plot + theme(legend.position = "bottom") ou + labs(title = "Overall survival"). ggpar() restyle tout le panneau en une étape, et des arguments comme break.time.by, risk.table.height et tables.theme = theme_cleantable() règlent l’anatomie sans toucher à la liste.
Dans survminer, réglez risk.table = TRUE dans ggsurvplot() et ajustez sa taille avec risk.table.height. La table doit partager les graduations et les limites de l’axe des x de la courbe pour que les comptages s’alignent sous les bons temps — réglez l’axe une fois avec break.time.by = (et xlim = si vous le rognez) et la table en hérite.
Les deux. La p-value du log-rank (de survdiff(), montrée par pval = TRUE) teste si les courbes diffèrent mais ne donne aucune amplitude. Le hazard ratio avec son intervalle de confiance (du modèle de Cox coxph() sur le même ADTTE) est la taille d’effet — combien plus vite ou plus lentement l’événement survient dans un bras versus un autre. Un rapport d’étude clinique montre typiquement la courbe avec la p-value du log-rank sur le panneau et le HR à côté, dans la note de bas de figure ou la table d’accompagnement.
Oui — le code de la figure est identique. Pointez survfit() vers les lignes OS de votre ADTTE (PARAMCD == "OS") et reconstruisez ; seul le paramètre change. Nous utilisons le délai jusqu’au premier événement indésirable ici parce que le pilote CDISC public est une étude à faible mortalité dont la courbe de survie globale est presque plate — une figure d’enseignement médiocre. Pour un critère d’efficacité, vous attendez que le bras actif se place plus haut et un hazard ratio protecteur en dessous de 1.
Testez vos connaissances
En utilisant l’objet adtte construit dans cette leçon, redessinez la figure de Kaplan-Meier survminer avec l’axe des x en mois au lieu de jours, avec des graduations tous les 2 mois, et l’axe des y en pourcentage. Conservez la table des effectifs à risque, les bandes de confiance, la p-value du log-rank et les lignes médianes.
Ajoutez une colonne de mois avec mutate(AVAL_M = AVAL / 30.4375) et ajustez sur Surv(AVAL_M, 1 - CNSR). Dans ggsurvplot(), réglez break.time.by = 2, réétiquetez xlab = "Months from first dose", et passez surv.scale = "percent". Réajustez l’échelle des données, pas seulement l’étiquette d’axe.
library(dplyr)
library(survival)
library(survminer)
adtte_m <- adtte %>% mutate(AVAL_M = AVAL / 30.4375) # days -> months
fit_m <- survfit(Surv(AVAL_M, 1 - CNSR) ~ TRT01A, data = adtte_m)
ggsurvplot(
fit_m, data = adtte_m,
risk.table = TRUE, conf.int = TRUE, pval = TRUE,
surv.median.line = "hv", palette = "jco",
surv.scale = "percent", # y-axis as a percentage
break.time.by = 2, # an axis tick every 2 months
legend.labs = c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose"),
legend.title = "Treatment arm",
xlab = "Months from first dose",
ylab = "Adverse-event-free probability",
ggtheme = theme_minimal()
)Les médianes se lisent désormais en mois (environ 1.1 contre 0.5–0.6), et les comptages de la table des effectifs à risque s’alignent sous chaque graduation de 2 mois. La forme est inchangée — seule l’unité a bougé, parce que vous avez réajusté l’échelle de AVAL, pas seulement l’étiquette. C’est la discipline dont parle le piège de la « mauvaise unité de temps ».
Surv() dessine correctement une figure de Kaplan-Meier ADTTE CDISC ?
A. Surv(AVAL, CNSR) B. Surv(AVAL, 1 - CNSR) C. Surv(CNSR, AVAL)
B. Un ADTTE CDISC code CNSR = 0 pour un événement, mais Surv() attend 1 = événement, donc vous l’inversez avec 1 - CNSR. A inverse le statut de chaque sujet (les événements deviennent des censures), ce qui retourne la courbe à l’envers. C échange entièrement les arguments de temps et de statut. Toute la figure dépend de la justesse de cette seule expression.
Conclusion
La figure de Kaplan-Meier est le titre d’une analyse de délai jusqu’à l’événement, et la construire pour un rapport d’étude clinique est une recette fixe : lire l’ADTTE, inverser la convention de censure avec Surv(AVAL, 1 - CNSR), estimer les courbes avec survfit(), et dessiner le panneau avec tout ce dont un évaluateur a besoin — courbes par bras, marques de censure, table des effectifs à risque, lignes médianes et p-value du log-rank. Le ggsurvplot() de survminer construit tout le panneau en un seul appel, et parce qu’il retourne de simples ggplots vous pouvez le styliser aussi loin que n’importe quel style maison l’exige. Couplez la figure au hazard ratio du modèle de Cox sur le même jeu de données, réglez bien l’unité de temps et l’alignement de la table des effectifs à risque, et votre figure KM est prête pour la soumission.
Cette leçon est reproductible : chaque résultat de cette page a été produit par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.
Leçons connexes
- Construire le jeu de données ADaM de délai jusqu’à l’événement (ADTTE) — dérive le jeu de données
AVAL/CNSRque cette figure lit, avec le pipeline admiral complet. · Estimation de Kaplan-Meier — la méthode de survie derrière la courbe, à partir des premiers principes. · Le test du log-rank — le test dont la p-value se trouve sur la figure, en profondeur. · Risques proportionnels de Cox — le modèle derrière le hazard ratio que vous rapportez à côté de la figure. · Le tableau démographique rtables — le tableau qui ouvre le CSR que la figure KM titre. - Où cela s’inscrit : construire les ADaM avec admiral (ADSL, ADAE, ADTTE) → générer les tableaux, listings et figures (le tableau démographique → les tableaux de synthèse des AE → vous êtes ici : la figure de Kaplan-Meier) → empaqueter la soumission. La figure KM est la figure signature du flux TLF (tables, listings, and figures).
Réutilisation
Citation
@online{2026,
author = {},
title = {La figure de Kaplan-Meier en R : construire la courbe de
survie clinique à partir d’un ADTTE},
date = {2026-07-01},
url = {https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/kaplan-meier-figure-survminer},
langid = {fr}
}