ADTTE en R avec admiral : jeu de données ADaM time-to-event

Dérivez un jeu de données time-to-event conforme CDISC — PARAMCD, AVAL, CNSR — avec derive_param_tte() d’admiral, puis lancez une analyse de Kaplan-Meier dessus

Un tutoriel complet et exécutable pour le jeu de données ADaM time-to-event ADTTE. Apprenez la structure BDS time-to-event (PARAMCD/PARAM, AVAL comme le temps, la convention de censure CNSR 0/1, de STARTDT à ADT), dérivez un ADTTE conforme en R à partir d’un ADSL pré-construit et de sources d’événement/censure avec derive_param_tte() d’admiral, et analysez-le avec une courbe de Kaplan-Meier, la survie médiane et un test du log-rank sur des données pharmaverse publiques.

Date de publication

30 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • ADTTE est le jeu de données ADaM pour les critères de survie — survie globale, survie sans progression, temps jusqu’au premier événement indésirable. Il suit la structure de données de base (BDS) : une ligne par sujet et par paramètre.
  • Trois variables portent l’analyse. AVAL est le temps jusqu’à l’événement, CNSR est l’indicateur de censure (convention CDISC : CNSR = 0 = l’événement a eu lieu, un entier positif = censuré), et STARTDTADT sont les dates de départ et d’arrêt du chronomètre.
  • admiral le dérive de façon déclarative. Vous décrivez ce qui compte comme un événement avec event_source() et ce qui compte comme une censure avec censor_source(), puis derive_param_tte() retient le premier événement (ou la dernière censure) par sujet — aucune logique de dates case_when() écrite à la main.
  • derive_vars_duration() calcule AVAL de STARTDT à ADT en jours ; une fusion finale ramène le traitement et les covariables depuis ADSL. Le résultat a la forme attendue pour une soumission et est prêt à analyser.
  • Le gain final est une analyse de survie. Passez Surv(AVAL, 1 - CNSR) directement dans survfit() pour une courbe de Kaplan-Meier, le temps médian jusqu’à l’événement et un test du log-rank — le jeu de données est fait pour ça.

Introduction

Le résultat phare d’un essai est souvent un chiffre de type délai jusqu’à l’événement : survie globale médiane de 18.7 mois, un hazard ratio de 0.74 pour la survie sans progression. Avant que tout cela n’atteigne un tableau ou une courbe de Kaplan-Meier, quelqu’un doit construire le jeu de données ADTTE — le jeu de données ADaM (Analysis Data Model) prêt à analyser qui retient, pour chaque sujet, combien de délai jusqu’à l’événement et si l’événement a réellement eu lieu ou si le sujet a été censuré.

ADTTE déroute les débutants parce qu’il encode une question clinique sous la forme d’une structure de données précise. Quand le chronomètre démarre-t-il ? Quel événement l’arrête ? Que se passe-t-il si aucun événement ne survient jamais ? Cette leçon répond aux trois en construisant un ADTTE conforme en R avec admiral, le package pharmaverse pour la dérivation ADaM, puis en l’analysant avec l’écosystème de survie de R. Nous travaillons sur des données d’exemple publiques pharmaverse, donc chaque ligne s’exécute telle quelle.

Voici où nous allons — une courbe de Kaplan-Meier prête à publier, construite directement à partir de l’ADTTE que nous dérivons :

Kaplan-Meier curves of time to first adverse event for three treatment arms (Placebo, Xanomeline High Dose, Xanomeline Low Dose) built from the derived ADTTE dataset, with confidence bands, a risk table, and a log-rank p-value below 0.001 annotated on the plot. The placebo arm stays adverse-event-free markedly longer than either active arm.

Cette leçon suppose que vous avez déjà rencontré le pipeline CDISC (Clinical Data Interchange Standards Consortium) — si « ADaM », « BDS » ou « SDTM » sont nouveaux pour vous, commencez par les standards CDISC, qui situe où s’inscrit ADTTE. Vous venez de SAS ? La correspondance SAS-vers-R explique pourquoi admiral joue le rôle d’une bibliothèque de macros de dérivation validée.

Ce qu’est ADTTE

ADTTE est un jeu de données à structure de données de base (BDS), la même forme verticale, une-ligne-par-paramètre, qu’utilisent ADLB et ADVS — mais spécialisée pour l’analyse time-to-event. Chaque ligne répond à une question pour un sujet et un critère : à partir de la date d’origine, combien de délai jusqu’à l’événement, et a-t-il eu lieu ? Le standard CDISC ADaM définit la structure (son document Basic Data Structure for Time-to-Event Analyses en est la référence normative), et admiral l’implémente.

Une poignée de variables font le travail. Voici les colonnes ADTTE porteuses :

Variable Libellé Ce qu’elle contient
PARAMCD Code du paramètre L’identifiant du critère — OS, PFS, TTAE. Une valeur par analyse.
PARAM Paramètre Le critère lisible, p. ex. Time to First Adverse Event.
STARTDT Date d’origine du temps-événement Le départ du chronomètre — généralement la première dose (TRTSDT) ou la randomisation.
ADT Date d’analyse L’arrêt du chronomètre — la date de l’événement, ou la date de censure si aucun événement.
AVAL Valeur d’analyse Le délai jusqu’à l’événement : la durée de STARTDT à ADT.
AVALU Unité de la valeur d’analyse L’unité d’AVALDAYS, MONTHS, YEARS.
CNSR Censure 0 = l’événement a eu lieu ; un entier positif = censuré (aucun événement observé).
EVNTDESC Description de l’événement ou de la censure Pourquoi cette ligne est un événement ou une censure (traçabilité).
SRCDOM / SRCVAR Domaine / variable source D’où vient ADT — la piste d’audit jusqu’aux données brutes.

Deux conventions piègent les gens, alors réglons-les tout de suite :

  • CNSR est « à l’envers » par rapport à l’intuition. CNSR = 0 signifie que l’événement a eu lieu ; une valeur positive signifie censuré. CDISC recommande fortement CNSR = 0 pour les événements et des entiers positifs pour les censures, et admiral l’impose. C’est pourquoi chaque appel de survie ci-dessous utilise Surv(AVAL, 1 - CNSR) — qui rebascule CNSR vers le statut standard « 1 = événement » attendu par le package survival.
  • AVAL est une durée, pas une date. STARTDT et ADT sont des dates ; AVAL est le nombre d’unités de temps entre elles. Vous la dérivez à partir des deux dates, vous n’y stockez pas une date.

Le scénario clinique de cette leçon est le temps jusqu’au premier événement indésirable — pour chaque sujet, combien de jours s’écoulent entre la première dose et son premier événement indésirable apparu sous traitement, censuré à la fin du traitement pour ceux qui n’en ont eu aucun. Les mécanismes sont identiques pour la survie globale (événement = décès) ou la survie sans progression (événement = progression ou décès) ; nous montrons cette variante à la fin.

Les données sources

admiral construit ADTTE à partir d’un ADSL pré-construit (une ligne par sujet — l’ossature démographie et traitement) plus les sources d’événement et de censure que vous choisissez. Nous utilisons les données d’exemple du pilote CDISC livrées dans pharmaverseadam et pharmaversesdtm — des données synthétiques, libres de licence, conçues exactement pour cela.

library(admiral)
library(dplyr)
library(pharmaversesdtm)
library(pharmaverseadam)
library(lubridate)

# ADSL — one row per subject, the demographics + treatment backbone
adsl <- pharmaverseadam::adsl

# AE — the raw adverse-event domain that will supply our event dates.
# convert_blanks_to_na() turns SDTM's empty strings "" into proper NA.
ae <- pharmaversesdtm::ae %>% convert_blanks_to_na()

adsl %>%
  select(USUBJID, TRT01A, TRTSDT, TRTEDT, AGE, SEX) %>%
  head(5)
# A tibble: 5 × 6
  USUBJID     TRT01A               TRTSDT     TRTEDT       AGE SEX  
  <chr>       <chr>                <date>     <date>     <dbl> <chr>
1 01-701-1015 Placebo              2014-01-02 2014-07-02    63 F    
2 01-701-1023 Placebo              2012-08-05 2012-09-01    64 M    
3 01-701-1028 Xanomeline High Dose 2013-07-19 2014-01-14    71 M    
4 01-701-1033 Xanomeline Low Dose  2014-03-18 2014-03-31    74 M    
5 01-701-1034 Xanomeline High Dose 2014-07-01 2014-12-30    77 F    

ADSL nous donne TRTSDT (date de première dose — notre départ du chronomètre) et TRTEDT (date de dernière dose — la base de la censure). Le domaine AE nous donne les dates de survenue des événements indésirables dans AESTDTC. Tout le reste est dérivé.

Préparer la source d’événement

L’événement est le « premier événement indésirable apparu sous traitement », donc les dates des AE ont besoin de deux choses : une vraie date R (AESTDTC est une chaîne de caractères ISO, éventuellement partielle), et un indicateur d’apparition sous traitement pour ne compter que les AE ayant commencé à la première dose ou après. La fonction derive_vars_dt() d’admiral impute et convertit la date ; un simple indicateur gère l’apparition sous traitement.

adae <- ae %>%
  # bring the treatment dates from ADSL onto each AE record
  derive_vars_merged(
    dataset_add = adsl,
    new_vars = exprs(TRTSDT, TRTEDT),
    by_vars = exprs(STUDYID, USUBJID)
  ) %>%
  # convert AESTDTC -> ASTDT, imputing a missing day/month to the first ("M")
  derive_vars_dt(new_vars_prefix = "AST", dtc = AESTDTC, highest_imputation = "M") %>%
  # treatment-emergent: started on/after first dose, up to 30 days after last dose
  mutate(TRTEMFL = if_else(ASTDT >= TRTSDT & ASTDT <= TRTEDT + days(30), "Y", NA_character_))

adae %>%
  select(USUBJID, AESTDTC, ASTDT, TRTSDT, TRTEMFL) %>%
  head(5)
# A tibble: 5 × 5
  USUBJID     AESTDTC    ASTDT      TRTSDT     TRTEMFL
  <chr>       <chr>      <date>     <date>     <chr>  
1 01-701-1015 2014-01-03 2014-01-03 2014-01-02 Y      
2 01-701-1015 2014-01-03 2014-01-03 2014-01-02 Y      
3 01-701-1015 2014-01-09 2014-01-09 2014-01-02 Y      
4 01-701-1023 2012-08-26 2012-08-26 2012-08-05 Y      
5 01-701-1023 2012-08-07 2012-08-07 2012-08-05 Y      

derive_vars_dt() fait le travail peu glorieux mais essentiel : une date partielle comme 2014-01 devient 2014-01-01 (imputée), et le résultat atterrit dans la nouvelle colonne ASTDT. TRTEMFL = "Y" marque les AE qui comptent. Nous ne fournirons que ceux-là à la dérivation.

Décrire les événements et la censure

C’est l’idée centrale d’admiral, et là où elle s’écarte de la logique de dates écrite à la main. Vous ne calculez pas le temps vous-même. Vous déclarez ce qu’est un événement et ce qu’est une censure, sous forme d’objets, et vous laissez derive_param_tte() les résoudre.

# EVENT: the first adverse event. order = AESEQ breaks ties on the same date.
ttae <- event_source(
  dataset_name = "ae",
  date = ASTDT,
  order = exprs(AESEQ),
  set_values_to = exprs(
    EVNTDESC = "ADVERSE EVENT",
    SRCDOM   = "ADAE",
    SRCVAR   = "ASTDT"
  )
)

# CENSOR: subjects with no AE are censored at end of treatment + 30 days.
eot <- censor_source(
  dataset_name = "adsl",
  date = TRTEDT + days(30),
  set_values_to = exprs(
    EVNTDESC = "END OF TREATMENT",
    SRCDOM   = "ADSL",
    SRCVAR   = "TRTEDT"
  )
)

Lisez-les comme des phrases. L’événement est la plus précoce ASTDT dans les données AE ; on enregistre qu’il s’agissait d’un événement indésirable issu d’ADAE. La censure intervient à la dernière dose plus 30 jours, depuis ADSL. Les blocs set_values_to écrivent les variables de traçabilité (EVNTDESC, SRCDOM, SRCVAR) afin que le jeu de données final documente pourquoi chaque ligne est un événement ou une censure — exactement ce qu’un évaluateur veut auditer. censor_source() fixe par défaut son CNSR à 1 ; event_source() fixe toujours CNSR = 0.

Dériver le paramètre ADTTE

Maintenant, passez les sources à derive_param_tte(). Pour chaque sujet, elle trouve le premier événement qualifiant ; s’il n’y en a aucun, elle prend la dernière censure. Elle écrit une ligne par sujet avec STARTDT, ADT, CNSR et les variables descriptives — mais pas encore AVAL.

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")   # only treatment-emergent AEs
  ),
  set_values_to = exprs(PARAMCD = "TTAE", PARAM = "Time to First Adverse Event")
)

adtte %>%
  select(USUBJID, PARAMCD, STARTDT, ADT, CNSR, EVNTDESC) %>%
  head(5)
# A tibble: 5 × 6
  USUBJID     PARAMCD STARTDT    ADT         CNSR EVNTDESC        
  <chr>       <chr>   <date>     <date>     <int> <chr>           
1 01-701-1015 TTAE    2014-01-02 2014-01-03     0 ADVERSE EVENT   
2 01-701-1023 TTAE    2012-08-05 2012-08-07     0 ADVERSE EVENT   
3 01-701-1028 TTAE    2013-07-19 2013-07-21     0 ADVERSE EVENT   
4 01-701-1033 TTAE    2014-03-18 2014-04-30     1 END OF TREATMENT
5 01-701-1034 TTAE    2014-07-01 2014-08-27     0 ADVERSE EVENT   

Une ligne par sujet, chacune étiquetée TTAE. STARTDT est la première dose, ADT est soit la date du premier AE (CNSR = 0), soit la date de censure de fin de traitement (CNSR = 1), et EVNTDESC indique laquelle. Le départ et l’arrêt du chronomètre sont fixés — ce qui manque, c’est le temps écoulé lui-même.

Calculer AVAL et ajouter les covariables

AVAL est la durée de STARTDT à ADT. Ne soustrayez pas les dates à la main — la fonction derive_vars_duration() d’admiral le fait avec la bonne unité et le bon signe. Ensuite, derive_vars_merged() ramène le traitement et les covariables depuis ADSL pour que le jeu de données soit prêt à analyser et à découper par groupe.

adtte <- adtte %>%
  # AVAL = time from STARTDT to ADT, in days
  derive_vars_duration(
    new_var = AVAL,
    start_date = STARTDT,
    end_date = ADT,
    out_unit = "days"
  ) %>%
  mutate(AVALU = "DAYS") %>%
  # bring treatment + covariates from ADSL for analysis
  derive_vars_merged(
    dataset_add = adsl,
    new_vars = exprs(TRT01A, AGE, SEX),
    by_vars = exprs(STUDYID, USUBJID)
  )

adtte %>%
  select(USUBJID, PARAMCD, AVAL, AVALU, CNSR, TRT01A) %>%
  head(5)
# A tibble: 5 × 6
  USUBJID     PARAMCD  AVAL AVALU  CNSR TRT01A              
  <chr>       <chr>   <dbl> <chr> <int> <chr>               
1 01-701-1015 TTAE        2 DAYS      0 Placebo             
2 01-701-1023 TTAE        3 DAYS      0 Placebo             
3 01-701-1028 TTAE        3 DAYS      0 Xanomeline High Dose
4 01-701-1033 TTAE       44 DAYS      1 Xanomeline Low Dose 
5 01-701-1034 TTAE       58 DAYS      0 Xanomeline High Dose

Voilà un ADTTE conforme : PARAMCD/PARAM nomment le critère, AVAL/AVALU donnent le temps et son unité, CNSR signale événement-contre-censure, et les variables de traçabilité et de traitement sont attachées. Avant d’analyser, relisez-vous le jeu de données.

# event vs censored counts
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) 
# time-to-event distribution, overall
summary(adtte$AVAL)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00   10.75   22.00   42.36   47.25  218.00 

Le découpage est le premier contrôle de cohérence : la plupart des sujets ont un événement, une minorité est censurée. Si chaque sujet était censuré, ou si AVAL contenait une valeur aberrante, la définition de l’événement ou de la censure serait fausse — détectez-le ici, pas dans la courbe de Kaplan-Meier.

Analyser : Kaplan-Meier, médiane et le test du log-rank

C’est le gain en matière de survie. Parce qu’ADTTE a été construit pour l’analyse time-to-event, le jeu de données s’insère directement dans le package survival — le seul ajustement est de rebasculer CNSR vers le statut d’événement standard avec 1 - CNSR. Nous comparons les trois bras de traitement (en écartant les échecs de sélection, qui n’ont jamais reçu de dose).

library(survival)

adtte_arms <- adtte %>% filter(TRT01A != "Screen Failure")

fit <- survfit(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte_arms)
fit
Call: survfit(formula = Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte_arms)

                             n events median 0.95LCL 0.95UCL
TRT01A=Placebo              85     65     33      26      60
TRT01A=Xanomeline High Dose 72     68     16      14      23
TRT01A=Xanomeline Low Dose  95     84     18      15      27

survfit() affiche la médiane du délai jusqu’à l’événement par bras avec son intervalle de confiance à 95 % — la médiane est le jour où la moitié du bras a connu l’événement. Le contraste est immédiat : le temps médian jusqu’au premier événement indésirable est de 33 jours sous placebo contre environ 16 à 18 jours dans les bras actifs. Les sujets sous Xanomeline atteignent leur premier AE plus tôt.

Formalisez cela avec un test du log-rank, le test standard pour « ces courbes de survie diffèrent-elles ? »

survdiff(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte_arms)
Call:
survdiff(formula = Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte_arms)

                             N Observed Expected (O-E)^2/E (O-E)^2/V
TRT01A=Placebo              85       65     98.1     11.15      22.3
TRT01A=Xanomeline High Dose 72       68     48.8      7.51      10.2
TRT01A=Xanomeline Low Dose  95       84     70.1      2.76       4.3

 Chisq= 23.4  on 2 degrees of freedom, p= 8e-06 

La statistique du log-rank est grande et p est bien en dessous de 0.001, donc la différence entre les bras est hautement improbable sous l’hypothèse nulle de courbes identiques. Lisez-le simplement : le temps jusqu’au premier événement indésirable diffère significativement entre les trois bras, les sujets sous placebo restant sans événement indésirable le plus longtemps — une lecture de sécurité sensée pour un médicament actif.

Voici maintenant la figure qu’un rapport clinique livre, construite avec ggsurvfit pour une courbe de qualité publication avec une table des effectifs à risque et le p du log-rank sur le graphique.

library(ggsurvfit)

survfit2(Surv(AVAL, 1 - CNSR) ~ TRT01A, data = adtte_arms) %>%
  ggsurvfit(linewidth = 0.9) +
  add_confidence_interval() +
  add_risktable(risktable_stats = "n.risk") +
  add_pvalue("annotation", size = 4) +
  scale_color_manual(values = c("#3a86d4", "#e07b39", "#5fa052")) +
  scale_fill_manual(values  = c("#3a86d4", "#e07b39", "#5fa052")) +
  labs(
    x = "Days from first dose",
    y = "Probability of being adverse-event-free"
  ) +
  theme_minimal()

Publication-ready Kaplan-Meier curves of time to first adverse event by treatment arm, derived from the ADTTE dataset. Three step curves (Placebo in azure, Xanomeline High Dose in orange, Xanomeline Low Dose in green) fall over 200 days from first dose, each with a shaded 95% confidence band. The placebo curve stays highest. A risk table runs beneath the x-axis and a log-rank p-value below 0.001 is annotated top-right.

La courbe porte tout ce dont un évaluateur a besoin : les fonctions en escalier avec leurs bandes de confiance, les effectifs à risque en dessous, et le p du log-rank sur le panneau. Pour les méthodes de survie plus approfondies — construire la courbe à la main, la mécanique du log-rank, les modèles de Cox et le peaufinage de la figure — les leçons Estimation de Kaplan-Meier, Test du log-rank et Modèle de Cox à risques proportionnels du pilier biostatistique vont jusqu’au bout. Ici, le point se situe en amont : un ADTTE correctement dérivé est ce qui fait de cette analyse un appel de deux lignes.

Rapport

Dans cette analyse des données du pilote CDISC, le temps jusqu’au premier événement indésirable apparu sous traitement différait significativement entre les bras de traitement (log-rank p < 0.001). Le temps médian jusqu’au premier événement indésirable était de 33 jours (IC à 95 % : 26–60) sous placebo, contre 16 jours (IC à 95 % : 14–23) et 18 jours (IC à 95 % : 15–27) sous Xanomeline forte et faible dose, respectivement.

Cette phrase — estimation, intervalle, test, sens — est le type d’énoncé qui figure dans un rapport d’étude clinique, produit à partir d’un jeu de données que vous pouvez remettre à un évaluateur avec une traçabilité complète jusqu’aux enregistrements AE bruts.

La même recette construit la survie globale

Rien de ce qui précède n’est spécifique aux événements indésirables. La survie globale (OS) est le même appel derive_param_tte() avec un événement et une censure différents : l’événement est le décès, la censure est la dernière date à laquelle le sujet était connu vivant. ADSL porte déjà DTHFL/DTHDT (décès) et LSTALVDT (dernière fois connu vivant), donc OS ne nécessite aucune préparation des AE.

death <- event_source(
  dataset_name = "adsl",
  filter = DTHFL == "Y",
  date = DTHDT,
  set_values_to = exprs(EVNTDESC = "DEATH", SRCDOM = "ADSL", SRCVAR = "DTHDT")
)
lstalv <- censor_source(
  dataset_name = "adsl",
  date = LSTALVDT,
  set_values_to = exprs(EVNTDESC = "LAST KNOWN ALIVE DATE", SRCDOM = "ADSL", SRCVAR = "LSTALVDT")
)

adtte_os <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl),
  start_date = TRTSDT,
  event_conditions  = list(death),
  censor_conditions = list(lstalv),
  set_values_to = exprs(PARAMCD = "OS", PARAM = "Overall Survival")
) %>%
  derive_vars_duration(new_var = AVAL, start_date = STARTDT, end_date = ADT, out_unit = "days")

adtte_os %>%
  count(CNSR) %>%
  mutate(meaning = if_else(CNSR == 0, "Death", "Censored (alive)"))
# A tibble: 2 × 3
   CNSR     n meaning         
  <int> <int> <chr>           
1     0     3 Death           
2     1   251 Censored (alive)

Même structure, même forme de code — seule la définition clinique a changé. La survie sans progression l’étend une fois de plus : deux sources d’événement (progression ou décès) et une censure à la dernière évaluation tumorale, toutes passées sous forme de listes à la même fonction. Cette composabilité est la raison pour laquelle les dérivations admiral restent lisibles à mesure que les critères se complexifient.

Une note honnête sur ce jeu de données d’exemple : le pilote CDISC est une étude à faible mortalité, donc son OS n’a qu’une poignée de décès et sa courbe de Kaplan-Meier est presque plate — bien pour montrer la dérivation, mais ce n’est pas une courbe informative. C’est exactement pourquoi cette leçon analyse le temps jusqu’au premier événement indésirable, qui a les événements nécessaires à une comparaison de survie interprétable. Choisir un critère avec assez d’événements fait partie du travail.

🟢 Avec un agent IA

Demandez à Prova « comment changer ma dérivation ADTTE du temps jusqu’au premier événement indésirable vers la survie sans progression, avec la progression ou le décès comme événement ? » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code admiral exécutable que vous pouvez essayer sur les données d’exemple pharmaverse. The runtime is the judge. Demandez à Prova →

Problèmes fréquents

Votre courbe de Kaplan-Meier est à l’envers (elle monte au lieu de descendre). Vous avez passé CNSR directement comme statut d’événement. Le package survival veut 1 = événement, mais le CNSR d’ADTTE vaut 0 = événement. Utilisez toujours Surv(AVAL, 1 - CNSR), qui inverse la convention. Si la courbe grimpe vers 1.0, c’est presque toujours la cause.

Chaque sujet ressort censuré (CNSR = 1). La source d’événement n’a rien apparié. Les coupables habituels : le jeu de données d’événement a été filtré jusqu’au vide avant d’atteindre source_datasets (p. ex. TRTEMFL == "Y" a tout retiré parce que l’indicateur a été mal construit), la variable date de event_source() est encore NA (une date partielle que derive_vars_dt() n’a pas imputée), ou la chaîne dataset_name de la source ne correspond à aucun nom dans source_datasets. Vérifiez que le jeu de données d’événement filtré contient bien des lignes, et que sa colonne de date est une vraie Date.

AVAL semble décalé d’un jour, ou est négatif. AVAL est une durée de STARTDT à ADT, donc une valeur négative signifie qu’une date d’événement précède le départ du chronomètre — généralement un AE enregistré avant la première dose qui aurait dû être exclu par l’indicateur d’apparition sous traitement. admiral compte la durée de façon inclusive ; les promoteurs diffèrent sur la convention du « +1 jour », alors confirmez AVAL par rapport à votre plan d’analyse statistique plutôt que de supposer une valeur par défaut.

Questions fréquentes

ADTTE est le jeu de données d’analyse time-to-event dans le standard ADaM — le jeu de données derrière les critères de survie tels que la survie globale, la survie sans progression et le temps jusqu’au premier événement indésirable. Il utilise la structure de données de base (BDS), une ligne par sujet et par paramètre, et stocke le temps dans AVAL, l’indicateur de censure dans CNSR, et les dates d’origine et d’analyse dans STARTDT et ADT.

CNSR = 0 signifie que l’événement a eu lieu (p. ex. décès, progression, l’événement indésirable) ; un entier positif signifie que le sujet a été censuré — suivi pendant un certain temps sans l’événement. C’est la convention CDISC, qu’admiral impose. C’est l’opposé du statut « 1 = événement » du package survival, c’est pourquoi le code d’analyse utilise Surv(AVAL, 1 - CNSR).

Partez d’un ADSL pré-construit, déclarez l’événement et la censure avec event_source() et censor_source(), puis appelez derive_param_tte() pour obtenir une ligne par sujet avec STARTDT, ADT et CNSR. Ajoutez le temps avec derive_vars_duration() (qui remplit AVAL) et fusionnez le traitement et les covariables depuis ADSL avec derive_vars_merged(). La séquence complète est déroulée ci-dessus.

STARTDT et ADT sont des dates — le départ du chronomètre (souvent la première dose) et son arrêt (la date de l’événement, ou la date de censure quand aucun événement ne survient). AVAL est la durée entre elles, un nombre dans l’unité AVALU (généralement des jours). Vous dérivez AVAL à partir des deux dates avec derive_vars_duration() ; vous ne stockez jamais une date dans AVAL.

Oui — c’est ce pour quoi ADTTE est construit. Passez Surv(AVAL, 1 - CNSR) à survfit() pour la courbe et la survie médiane, et à survdiff() pour le test du log-rank. Le 1 - CNSR convertit la convention d’événement CNSR = 0 d’ADTTE vers le statut d’événement attendu par le package survival. Les bibliothèques de tracé comme ggsurvfit et survminer prennent le même objet Surv().

derive_param_tte() résout pour vous la logique « premier événement, sinon dernière censure », impose la convention CNSR, gère plusieurs sources d’événement et de censure de façon composable (p. ex. progression ou décès pour la PFS), et écrit les variables de traçabilité (EVNTDESC, SRCDOM, SRCVAR) dont un évaluateur a besoin. La logique de dates case_when() écrite à la main est facile à se tromper subtilement et difficile à auditer — admiral rend la dérivation déclarative et standard.

Testez vos connaissances

À partir des objets adsl et adae construits dans cette leçon, dérivez un paramètre ADTTE pour le temps jusqu’au premier événement indésirable grave (PARAMCD = "TTSAE"). Les AE graves sont signalés par AESER == "Y" dans les données AE. Calculez AVAL, puis comptez les événements contre les censurés.

Vous n’avez qu’à changer la source d’événement : ajoutez un filter = AESER == "Y" à event_source() pour qu’elle n’apparie que les AE graves. Réutilisez le même censor_source() de fin de traitement, le même appel derive_param_tte() (avec un nouveau PARAMCD/PARAM), et le même derive_vars_duration().

ttsae <- event_source(
  dataset_name = "ae",
  filter = AESER == "Y",            # serious AEs only
  date = ASTDT,
  order = exprs(AESEQ),
  set_values_to = exprs(EVNTDESC = "SERIOUS 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_sae <- derive_param_tte(
  dataset_adsl = adsl,
  start_date = TRTSDT,
  event_conditions  = list(ttsae),
  censor_conditions = list(eot),
  source_datasets = list(adsl = adsl, ae = filter(adae, TRTEMFL == "Y")),
  set_values_to = exprs(PARAMCD = "TTSAE", PARAM = "Time to First Serious Adverse Event")
) %>%
  derive_vars_duration(new_var = AVAL, start_date = STARTDT, end_date = ADT, out_unit = "days")

adtte_sae %>%
  count(CNSR) %>%
  mutate(meaning = if_else(CNSR == 0, "Serious AE", "Censored"))

Les AE graves sont plus rares que les AE en général, donc attendez-vous à bien plus de sujets censurés (CNSR = 1) que dans le paramètre tous-AE — un bon rappel que la définition de l’événement pilote le taux d’événements.

A. Surv(AVAL, CNSR) B. Surv(AVAL, 1 - CNSR) C. Surv(CNSR, AVAL)

B. ADTTE utilise 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 de Kaplan-Meier à l’envers. C échange entièrement les arguments de temps et de statut.

Conclusion

ADTTE paraît intimidant jusqu’à ce que vous le voyiez comme trois décisions encodées sous forme de données : quand le chronomètre démarre (STARTDT), ce qui l’arrête (un événement avec CNSR = 0, ou une censure avec CNSR > 0 à ADT), et combien de temps cela a pris (AVAL). admiral transforme ces décisions en une dérivation déclarative — event_source(), censor_source(), derive_param_tte(), derive_vars_duration() — qui est lisible, traçable et facile à étendre du temps-jusqu’au-premier-AE à la survie globale puis à la survie sans progression. Et parce que le jeu de données est conçu à cet effet, l’analyse de survie par-dessus est un appel de deux lignes : Surv(AVAL, 1 - CNSR) dans survfit() et vous avez une courbe de Kaplan-Meier, une médiane et un test du log-rank. Construisez bien le jeu de données, et l’analyse se fait toute seule.

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

  • Les standards CDISC : CDASH, SDTM, ADaM, Define-XML — où ADTTE et la BDS s’inscrivent dans le modèle ADaM. · Le flux de données des essais cliniques — comment les données AE et DM brutes deviennent les jeux de données ADaM que cette leçon dérive. · Estimation de Kaplan-Meier — la méthode de survie derrière la courbe, à partir des premiers principes. · Le test du log-rank — le test comparant les bras, en profondeur. · Modèle de Cox à risques proportionnels — le modèle de hazard ratio que vous ajusteriez sur ce même ADTTE.
  • Où cela s’inscrit : les fondations réglementaires et CDISC → construire l’ADaM avec admiral (ADSL, BDS, ADAE, ADTTE — vous êtes ici) → produire des tableaux, des listings et des figures à partir de ces jeux de données. ADTTE est le pont entre la programmation clinique et l’analyse de survie que le pilier biostatistique couvre de bout en bout.
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 = {ADTTE en R avec admiral : jeu de données ADaM time-to-event},
  date = {2026-06-30},
  url = {https://www.datanovia.com/learn/pharma-clinical/03-adam-admiral/adtte-time-to-event},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“ADTTE en R avec admiral : jeu de données ADaM time-to-event.” 2026. June 30. https://www.datanovia.com/learn/pharma-clinical/03-adam-admiral/adtte-time-to-event.