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).

Date de publication

1 juillet 2026

Modifié

11 juillet 2026

AstucePoints clés
  • 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) et CNSR (l’indicateur de censure). La convention CDISC est CNSR = 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’objet survfit en 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) :

A survminer Kaplan-Meier figure of time to first adverse event for three treatment arms (Placebo in azure, Xanomeline Low Dose in green, Xanomeline High Dose in orange), built from a CDISC ADTTE with ggsurvplot(). Three step curves fall from 1.0 over about 220 days, each with a shaded 95% confidence band and small censoring ticks; the placebo curve stays clearly highest. Dashed horizontal-and-vertical guides mark the median survival per arm and a log-rank p-value below 0.001 is printed on the panel. The placebo arm stays adverse-event-free markedly longer than either active arm.

À 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)
fit
Call: 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()
)

A survminer Kaplan-Meier figure of time to first adverse event by treatment arm on the CDISC pilot data. Three step-function survival curves descend from 1.0 over about 220 days — Placebo highest, the two Xanomeline arms below it — each with a shaded 95% confidence band and small censoring ticks. Dashed median lines mark 33 days (Placebo), 18 days (Low Dose), and 16 days (High Dose). The log-rank p-value is printed on the panel and a number-at-risk table runs beneath the x-axis.

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, ...), puis p$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, et tables.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.

Note

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.

🟢 Avec un agent IA

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 ».

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.

Note

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/CNSR que 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).
Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Réutilisation

Citation

BibTeX
@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}
}
Veuillez citer ce travail comme suit :
“La figure de Kaplan-Meier en R : construire la courbe de survie clinique à partir d’un ADTTE.” 2026. July 1. https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/kaplan-meier-figure-survminer.