Reproduire une soumission R à la FDA de bout en bout : le projet de synthèse pharmaverse

Faites passer un seul jeu de données à travers tout le pipeline de soumission que cette série a enseigné par morceaux — construisez un ADSL avec admiral, exportez un XPT conforme avec xportr, vérifiez-le par rapport à sa spécification avec metacore et metatools, et empaquetez les programmes avec pkglite — puis rencontrez les pilotes du R Consortium qui ont prouvé qu’une soumission FDA entièrement en R fonctionne

La série sur l’assemblage du dossier de soumission enseignait chaque étape isolément : dériver un jeu de données ADaM, exporter un XPT, lire et vérifier les métadonnées Define-XML, rédiger les guides de l’évaluateur, empaqueter les programmes. Ce projet de synthèse les enfile en un seul pipeline continu sur un unique jeu de données — construire un jeu de données d’analyse au niveau sujet (ADSL) à partir du SDTM CDISC avec admiral, l’exporter vers un fichier SAS Transport v5 conforme avec xportr, le vérifier par rapport à une spécification metacore avec metatools (un contrôle de terminologie contrôlée qui gagne sa place), et empaqueter les programmes de construction en un seul artefact texte évaluable avec pkglite. Puis il situe tout le fil dans les vrais pilotes FDA du R Consortium — les premières soumissions de test publiques entièrement en R — avec un compte rendu précis et sans surenchère de ce que la FDA a réellement fait et n’a pas fait. Chaque étape s’exécute sur des données pharmaverse publiques.

Date de publication

1 juillet 2026

Modifié

7 juillet 2026

AstucePoints clés
  • C’est tout le fil de la soumission sur un seul jeu de données. Les leçons précédentes enseignaient chaque étape isolément ; ici un unique jeu de données au niveau sujet traverse les quatre — construire → exporter → vérifier → empaqueter — de sorte que vous voyez le pipeline comme un seul travail continu, non cinq outils isolés.
  • Construisez avec admiral, exportez avec xportr, vérifiez avec metacore/metatools, empaquetez avec pkglite. Dérivez un ADSL (Subject-Level Analysis Dataset, une ligne par sujet) à partir d’une source SDTM (Study Data Tabulation Model), écrivez un fichier XPT (SAS Transport version 5) conforme, validez-le par rapport à une spécification, et empaquetez les programmes en un seul artefact texte évaluable.
  • Le contrôle des métadonnées gagne sa place. Vérifier le jeu de données exporté par rapport à sa spécification attrape un véritable écart — une valeur de traitement absente de la liste de codes de terminologie contrôlée de la spécification — ce qui est exactement ce pour quoi le contrôle existe, non une défaillance de l’outil.
  • C’est la forme d’une vraie soumission FDA entièrement en R. Les pilotes de soumission du R Consortium ont livré des soumissions de test publiques, entièrement fondées sur R, à la FDA en utilisant cette même chaîne d’outils pharmaverse.
  • La FDA est agnostique vis-à-vis du logiciel — les pilotes prouvent la faisabilité, non un aval. La FDA a examiné et achevé son examen d’une soumission entièrement en R ; cela démontre que le pipeline est acceptable et fournit un exemple public fonctionnel. Ce n’est pas « R est approuvé par la FDA ». La FDA examine des soumissions, elle ne certifie pas un langage.

Introduction

Vous avez appris chaque étape de l’assemblage isolément. Vous avez dérivé un jeu de données au niveau sujet, l’avez exporté vers un fichier de transport, avez lu et vérifié ses métadonnées, rédigé les guides de l’évaluateur, et empaqueté les programmes d’analyse. Chaque leçon se tenait seule. Une vraie soumission n’est pourtant pas cinq exercices distincts — c’est un seul jeu de données qui voyage à travers eux tous. Ce projet de synthèse les noue ensemble : nous prenons un unique ADSL (Subject-Level Analysis Dataset — une ligne par sujet, la colonne vertébrale à laquelle se joint tout autre jeu de données d’analyse) depuis une source SDTM (Study Data Tabulation Model) brute jusqu’à un artefact de soumission évaluable, en exécutant chaque étape pour de vrai.

Quatre étapes, un jeu de données :

  1. Construire — dérivez l’ADSL depuis les domaines SDTM DM et EX avec admiral.
  2. Exporter — écrivez-le dans un fichier XPT (SAS Transport version 5) conforme avec xportr.
  3. Vérifier — validez-le par rapport à une spécification avec metacore et metatools.
  4. Empaqueter — regroupez les programmes de construction en un seul artefact texte avec pkglite.

Nous plaçons ensuite tout le fil dans son contexte réel : les pilotes de soumission du R Consortium, les premières soumissions de test entièrement en R publiques à la FDA, qui ont utilisé exactement cette chaîne d’outils. Chaque étape ci-dessous renvoie à la leçon qui l’a enseignée en profondeur — ici nous gardons chaque étape compacte pour que le fil soit le propos.

A left-to-right flow schematic of an all-R FDA submission pipeline in six boxes joined by arrows, showing one dataset moving through every stage. Box 1 (light blue): SDTM, the source data DM plus EX. Box 2 (mid blue): admiral, build the ADSL for 306 subjects. Box 3 (mid blue): xportr, export a compliant XPT v5 with strict_checks. Box 4 (dark navy, the centrepiece): metacore and metatools, check the data against its spec and controlled terminology. Box 5 (mid blue): pkglite, bundle the programs into one ASCII text file. Box 6 (brand azure): eCTD, the assembled submission package. The visual point is that a single dataset is built, exported, checked, and packaged into the deliverable a regulator receives — the shape of an all-R submission.

Étape 1 — Construire l’ADSL avec admiral

Le pipeline commence avec des données qu’un régulateur collecte réellement : les domaines SDTM. Nous dérivons l’ADSL à partir de deux d’entre eux — DM (Demographics) et EX (Exposure) — en utilisant admiral, le package pharmaverse pour construire les jeux de données ADaM (Analysis Data Model). Nous gardons la dérivation délibérément compacte : reportez les bras de traitement planifié et réel, dérivez la date de première dose TRTSDT, et posez le SAFFL (Safety Population Flag). La dérivation ADSL complète — chaque indicateur de population, chaque variable de traitement, chaque date — est sa propre leçon ; ici nous construisons juste assez pour disposer d’un authentique jeu de données d’analyse à une-ligne-par-sujet à emporter.

library(admiral)
library(pharmaversesdtm)

data("dm", package = "pharmaversesdtm")
data("ex", package = "pharmaversesdtm")
dm <- convert_blanks_to_na(dm)
ex <- convert_blanks_to_na(ex)

adsl <- dm |>
  dplyr::select(-DOMAIN) |>
  dplyr::mutate(TRT01P = ARM, TRT01A = ACTARM) |>
  # First treatment start date from EX (first dosed record).
  derive_vars_merged(
    dataset_add = derive_vars_dtm(ex, dtc = EXSTDTC, new_vars_prefix = "EXST"),
    filter_add  = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & !is.na(EXSTDTM),
    new_vars    = exprs(TRTSDTM = EXSTDTM),
    order       = exprs(EXSTDTM, EXSEQ), mode = "first",
    by_vars     = exprs(STUDYID, USUBJID)
  ) |>
  derive_vars_dtm_to_dt(source_vars = exprs(TRTSDTM)) |>
  # Safety flag: dosed subjects are "Y", everyone else "N" (a Y/N flag is never NA).
  derive_var_merged_exist_flag(
    dataset_add = ex, by_vars = exprs(STUDYID, USUBJID),
    new_var = SAFFL, condition = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))),
    false_value = "N", missing_value = "N"
  )

# Keep the submission-relevant subject-level columns.
sub <- adsl[, c("STUDYID", "USUBJID", "SUBJID", "SITEID", "AGE", "SEX",
                "RACE", "TRT01P", "TRT01A", "TRTSDT", "SAFFL")]

cat("ADSL rows:", nrow(sub),
    "| one row per subject:", nrow(sub) == length(unique(sub$USUBJID)), "\n")
ADSL rows: 306 | one row per subject: TRUE 

Une ligne par sujet, 306 sujets. Regardez les trois variables qui portent la dérivation — l’indicateur de sécurité, le traitement planifié et la date dérivée :

table(sub$SAFFL)                 # Y = dosed (safety population), N = not dosed

  N   Y 
 52 254 
table(sub$TRT01P)                # planned treatment arm, straight from DM

             Placebo       Screen Failure Xanomeline High Dose 
                  86                   52                   84 
 Xanomeline Low Dose 
                  84 
class(sub$TRTSDT)                # a real Date object
[1] "Date"

Lisez ceci comme le ferait un évaluateur. SAFFL vaut Y pour les 254 sujets dosés et N pour les 52 qui ne l’ont pas été — et parce que nous avons passé false_value = "N", missing_value = "N", il n’est jamais NA, ce qu’un indicateur de population Y/N doit être. TRT01P (Planned Treatment for Period 01) montre les trois bras d’analyse plus Screen Failure — la valeur de bras brute issue de DM. Gardez cela à l’esprit : c’est l’écart que l’étape 3 attrapera. TRTSDT est un authentique Date, ce qui compte dès l’instant où nous l’exportons.

Étape 2 — Exporter un XPT conforme avec xportr

L’ADSL est prêt pour l’analyse mais il ne peut pas être livré sous forme de .rds. Une soumission transporte les jeux de données sous forme de fichiers XPT (SAS Transport version 5). Nous utilisons xportr pour appliquer une spécification de métadonnées — types, longueurs, libellés, formats, ordre — et écrire le fichier de transport, avec strict_checks = TRUE de sorte qu’une violation de conformité interrompe l’écriture au lieu de livrer un avertissement. Le seul piège à retenir de la leçon complète sur l’export XPT : une date ADaM est exportée comme type = "numeric" avec un format d’affichage DATE9., jamais type = "date" — c’est ainsi que TRTSDT fait l’aller-retour comme une vraie date.

library(xportr)
library(haven)

# The variable-level spec — the same information a Define-XML carries.
var_spec <- data.frame(
  dataset  = "ADSL",
  variable = c("STUDYID", "USUBJID", "SUBJID", "SITEID", "AGE", "SEX",
               "RACE", "TRT01P", "TRT01A", "TRTSDT", "SAFFL"),
  type     = c("character", "character", "character", "character", "numeric",
               "character", "character", "character", "character", "numeric", "character"),
  length   = c(12, 40, 10, 10, 8, 2, 40, 40, 40, 8, 2),
  label    = c("Study Identifier", "Unique Subject Identifier", "Subject Identifier",
               "Study Site Identifier", "Age", "Sex", "Race",
               "Planned Treatment for Period 01", "Actual Treatment for Period 01",
               "Date of First Exposure", "Safety Population Flag"),
  format   = c("", "", "", "", "", "", "", "", "", "DATE9.", ""),  # a numeric date -> DATE9.
  order    = 1:11,
  stringsAsFactors = FALSE
)
df_spec <- data.frame(dataset = "ADSL", label = "Subject-Level Analysis Dataset")

xpt_path <- file.path(tempdir(), "adsl.xpt")   # member name must be <= 8 characters
sub |>
  xportr_metadata(var_spec, "ADSL") |>
  xportr_type()                     |>
  xportr_length(length_source = "metadata") |>
  xportr_label()                    |>
  xportr_order()                    |>
  xportr_format()                   |>
  xportr_df_label(df_spec)          |>
  xportr_write(xpt_path, strict_checks = TRUE)

cat("XPT written:", file.exists(xpt_path),
    "| size:", file.info(xpt_path)$size, "bytes\n")
XPT written: TRUE | size: 67200 bytes

Les règles informatives confirment que chaque variable a été trouvée dans la spécification et que les colonnes ont été ordonnées. Parce que nous avons passé strict_checks = TRUE, le fichier n’a été écrit que parce qu’il est conforme — une barrière de soumission, non une politesse. Maintenant relisez-le et prouvez que le transport a survécu à l’aller-retour, en particulier la date :

back <- read_xpt(xpt_path)

dim(back)                          # 306 x 11, restored from the transport file
[1] 306  11
attr(back, "label")                # the dataset label survived
[1] "Subject-Level Analysis Dataset"
class(back$TRTSDT)                 # "Date" -> the numeric + DATE9. spec worked
[1] "Date"
back$TRTSDT[1]                     # a real calendar date
[1] "2014-01-02"

Le jeu de données revient à pleine taille, le libellé de jeu de données "Subject-Level Analysis Dataset" est attaché, et TRTSDT est un authentique Date — la preuve que le motif numeric + DATE9. a bien fait l’aller-retour. Ce .xpt est l’objet qui atterrit dans analysis/adam/datasets/.

Étape 3 — Vérifier les données par rapport à leur spécification avec metacore et metatools

Un fichier exporté n’est pas un fichier vérifié. Un jeu de données de soumission doit se conformer à ses métadonnées Define-XML — la spécification lisible par machine qui liste chaque variable, sa liste de codes et sa terminologie contrôlée. Nous chargeons une véritable spécification de pilote CDISC comme objet metacore et utilisons metatools pour vérifier les données exportées par rapport à elle. La terminologie contrôlée (CT) est l’ensemble fixe de valeurs autorisées qu’une variable peut prendre — et check_ct_col() vérifie que les valeurs d’une colonne vivent toutes dans sa liste de codes. Lire et vérifier les métadonnées en profondeur, c’est la leçon Define-XML ; ici nous exécutons deux contrôles et lisons le résultat honnêtement.

library(metacore)
library(metatools)

# Load the pilot ADaM spec (an .rda holding a `metacore` object).
e <- new.env()
load(metacore_example("pilot_ADaM.rda"), envir = e)
mc <- get("metacore", envir = e)
adsl_mc <- select_dataset(mc, "ADSL", verbose = "silent")

# SEX: every value is in the codelist -> the check passes, nothing to report.
get_bad_ct(back, adsl_mc, SEX)
character(0)

character(0)SEX est propre : chaque valeur (M/F) figure dans la liste de codes de la spécification, il n’y a donc rien à signaler. Exécutez maintenant le même contrôle sur le traitement planifié. Celui-ci n’est pas propre, et c’est là le propos :

# TRT01P: the check compares each value to the spec's CL.ARM codelist.
bad <- check_ct_col(back, adsl_mc, TRT01P)
Warning: ✖ Invalid controlled terminology detected
ℹ Variable: TRT01P | Codelist: CL.ARM
ℹ Values not permitted 'Screen Failure'
get_bad_ct(back, adsl_mc, TRT01P)   # the value(s) not permitted by the codelist
[1] "Screen Failure"

Le contrôle signale "Screen Failure" comme une valeur non autorisée par la liste de codes CL.ARM — et c’est le contrôle qui gagne sa place, non un bug. Notre construction compacte de l’étape 1 a reporté la valeur de bras brute de DM directement dans TRT01P ; le Define-XML du pilote ne liste que les trois traitements d’analyse dans CL.ARM. check_ct_col() a correctement attrapé une valeur de données absente de la spécification — exactement l’écart qu’un contrôle de conformité existe pour trouver. Dans une vraie soumission, un ADSL met TRT01P à NULL pour les sujets en échec de sélection (ils n’ont pas de traitement d’analyse planifié), ce qui effacerait le signalement. Voir SEX passer silencieusement et TRT01P attraper l’écart, c’est toute la valeur d’un contrôle piloté par spécification : il vous dit précisément où données et métadonnées divergent, avant qu’un évaluateur ne le fasse.

Étape 4 — Empaqueter les programmes avec pkglite

Une soumission livre les jeux de données et les métadonnées — et les programmes qui les ont produits. L’eCTD (electronic Common Technical Document, le format de dossier standard qu’un régulateur reçoit) veut ces programmes sous forme d’un unique fichier texte ASCII plat et évaluable, non un dossier de scripts .R épars. Nous utilisons pkglite pour regrouper les programmes de construction de l’ADSL en un seul artefact — bouclant la boucle, car c’est le même code qui a construit le jeu de données à l’étape 1 qui est empaqueté ici. L’ aller-retour pkglite complet — le format d’échange, le contrôle ASCII, unpack() — est sa propre leçon ; ici nous exécutons les trois verbes de bout en bout.

library(pkglite)

# A small package holding the ADSL build program (stands in for your analysis package).
pkg <- file.path(tempdir(), "adslbuild")
dir.create(file.path(pkg, "R"), recursive = TRUE, showWarnings = FALSE)
writeLines(c(
  "Package: adslbuild", "Type: Package",
  "Title: ADSL Build Programs for Submission", "Version: 0.1.0",
  "Description: Analysis programs that derive ADSL.", "License: MIT"
), file.path(pkg, "DESCRIPTION"))
writeLines(c(
  "#' Build ADSL from SDTM DM and EX",
  "build_adsl <- function(dm, ex) {",
  "  # derive treatment vars, first-dose date, and SAFFL",
  "  dm",
  "}"
), file.path(pkg, "R", "build_adsl.R"))

# collate -> pack -> verify_ascii (the eCTD gate) -> unpack.
fc      <- collate(pkg, file_ectd())          # file_ectd() takes NO arguments
out_txt <- file.path(tempdir(), "pkglite.txt")
pack(fc, output = out_txt, quiet = TRUE)

cat("pkglite.txt written:", file.exists(out_txt),
    "| size:", file.info(out_txt)$size, "bytes",
    "| ASCII:", verify_ascii(out_txt, quiet = TRUE), "\n")
pkglite.txt written: TRUE | size: 518 bytes | ASCII: TRUE 

Un seul fichier texte, et verify_ascii() renvoie TRUE — il est prêt pour la soumission. Prouvez que l’aller-retour restaure les programmes qu’un évaluateur peut lire et exécuter :

restore <- file.path(tempdir(), "restored")
unpack(out_txt, output = restore, quiet = TRUE)

list.files(restore, recursive = TRUE)   # the package tree, reconstructed
[1] "adslbuild/DESCRIPTION"    "adslbuild/R/build_adsl.R"

Le package adslbuild est de retour — DESCRIPTION et R/build_adsl.R — reconstitué depuis l’unique fichier texte. Cet artefact est ce qui se trouve physiquement dans l’emplacement des programmes d’analyse m5 de l’eCTD, et ce que l’ADRG (Analysis Data Reviewer’s Guide) inventorie dans sa section Submission of Programs.

Vous venez de construire une soumission, en miniature

Prenez du recul et regardez ce que les quatre étapes ont fait avec un seul jeu de données. Vous avez dérivé un jeu de données d’analyse à partir du SDTM CDISC, l’avez exporté comme fichier de transport conforme, l’avez vérifié par rapport à sa spécification de métadonnées, et empaqueté les programmes en un seul artefact texte évaluable. C’est la forme d’une soumission entièrement en R — admiralxportrmetacore/metatoolspkglite, les mêmes outils que cette série a enseignés un par un, exécutés ici comme un seul fil. La seule chose qui sépare cela d’un vrai livrable, c’est l’échelle : plus de jeux de données (ADAE, ADTTE, ADLBC), les tableaux et figures, le Define-XML, et les guides de l’évaluateur — chacun d’eux suivant ce même motif construire → exporter → vérifier → empaqueter.

Les pilotes du R Consortium : une soumission entièrement en R qu’a examinée la FDA

Ce pipeline n’est pas une abstraction pédagogique. Le R Consortium — un organisme sectoriel neutre et à but non lucratif — a mené une série de pilotes de soumission : des soumissions de test publiques, entièrement fondées sur R, à la FDA, construites pour prouver le workflow de bout en bout et donner à l’industrie un exemple fonctionnel à suivre.

La soumission du Pilote 3 couvrait un dossier d’étude complet — les jeux de données d’analyse ADaM (ADSL, ADAE, ADTTE, ADLBC, ADADAS) et les TLF (tables, listings, and figures) — assemblés à partir du SDTM, empaquetés au format eCTD, avec chaque artefact rendu public. Sa chronologie est consignée dans l’ annonce du R Consortium :

Jalon Date
Soumission initiale via la passerelle eCTD de la FDA 28 août 2023
Nouvelle soumission mise à jour 19 avril 2024
Lettre de réponse finale de la FDA 8 août 2024

Les pilotes ont été construits sur la chaîne d’outils pharmaverse — les mêmes admiral, metacore/metatools, xportr, et pkglite que vous venez d’utiliser (le R Consortium décrit le package regroupé avec pkglite et « divers packages R open source » ; le code de travail complet est public dans le dépôt du pilote 3 et le dépôt de soumission à la FDA).

Ce que cela signifie — et ne signifie pas —

Soyez précis ici, car c’est toute la crédibilité de l’exercice qui est en jeu :

  • Ce qui est vrai. La FDA a examiné et achevé avec succès son examen d’une soumission entièrement fondée sur R, et a émis une réponse finale favorable. Cela démontre que le pipeline est faisable et acceptable, et laisse un exemple public fonctionnel que les futures soumissions peuvent suivre. C’était une collaboration FDA–industrie à travers le R Consortium neutre.
  • Ce qui n’est pas vrai. Ce n’est pas « R est approuvé par la FDA », « avalisé » ou « certifié », et la FDA ne recommande pas R. La FDA est agnostique vis-à-vis du logiciel : elle examine des soumissions, et n’adoube pas un langage de programmation. Les pilotes prouvent qu’une soumission fondée sur R peut être préparée, livrée et examinée avec succès — faisabilité, non aval.

Cette distinction n’est pas de la pédanterie. Surenchérir ici est le moyen le plus courant par lequel le contenu R clinique perd sa crédibilité auprès des personnes qui mènent réellement des soumissions.

🟢 Avec un agent IA

Demandez à Prova « Guidez-moi à travers une soumission FDA entièrement en R de bout en bout — construisez un ADSL avec admiral, exportez un XPT conforme avec xportr, vérifiez-le par rapport à une spécification metacore avec metatools, et empaquetez les programmes avec pkglite — et expliquez ce que les pilotes du R Consortium ont réellement prouvé. » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code pharmaverse exécutable que vous pouvez essayer sur des données publiques. The runtime is the judge. Demandez à Prova →

Problèmes fréquents

La date se relit comme un nombre, non comme un Date. Vous avez posé type = "date" dans la spécification d’export. xportr traite type = "date" comme un type caractère et corrompt une vraie colonne Date à l’écriture. Exportez toute date ADaM comme type = "numeric" avec format = "DATE9." (et exécutez xportr_format() dans le pipeline) — la valeur reste numérique sur le disque et read_xpt() la restaure comme un Date en bonne et due forme, comme le fait TRTSDT à l’étape 2.

SAFFL ressort NA pour les sujets non dosés. derive_var_merged_exist_flag() pose l’indicateur à sa true_value là où la condition correspond, mais les sujets sans enregistrement correspondant reçoivent missing_value et les sujets présents-mais-en-échec reçoivent false_value — les deux valent NA par défaut. Un indicateur de population Y/N ne doit jamais valoir NA, alors passez false_value = "N", missing_value = "N" explicitement, comme le fait l’étape 1. Alors chaque sujet est Y ou N.

Le contrôle de terminologie contrôlée « échoue » et vous supposez que l’outil est cassé. Un avertissement de check_ct_col() est le contrôle qui fonctionne — il a trouvé une valeur de données absente de la liste de codes de la spécification ("Screen Failure" dans TRT01P, à l’étape 3). Le correctif est dans les données ou la spécification, non dans l’outil : mettez à NULL la variable de traitement d’analyse pour les échecs de sélection, ou confirmez que la valeur appartient et ajoutez-la à la liste de codes. Un contrôle de conformité qui ne signale jamais rien ne vérifie rien.

Questions fréquentes

Non — et la formulation compte. La FDA est agnostique vis-à-vis du logiciel : elle examine des soumissions, non des langages de programmation, donc il n’existe pas de langage « approuvé par la FDA ». Ce que les pilotes du R Consortium ont montré, c’est qu’une soumission entièrement fondée sur R peut être préparée, livrée au format eCTD, et examinée avec succès par la FDA — c’est la faisabilité et l’acceptabilité, un exemple public fonctionnel, non un aval ou une certification.

Une soumission de test publique et complète émulant un dossier d’étude entier : les jeux de données d’analyse ADaM (ADSL, ADAE, ADTTE, ADLBC, ADADAS) et les TLF (tables, listings, and figures), dérivés du SDTM et empaquetés au format eCTD. Elle a été soumise via la passerelle eCTD de la FDA en août 2023, resoumise en avril 2024, et a reçu une réponse finale de la FDA en août 2024. Tout le code est public dans le dépôt du pilote 3.

Non. XPT (SAS Transport version 5) est un format d’échange ouvert et publié — vous n’avez pas besoin de SAS pour en écrire un. En R, xportr applique une spécification de métadonnées et écrit un fichier v5 conforme (en appelant haven::write_xpt() sous le capot), et le relit avec haven::read_xpt(). L’étape 2 ci-dessus écrit et relit un authentique .xpt v5 sans aucune intervention de SAS.

Pas avec les packages de ce pipeline. metacore lit un Define-XML dans un objet structuré et metatools vérifie les données par rapport à lui, mais aucun n’écrit de define.xml. Générer le fichier revient à un autre outillage — Pinnacle 21 ou le package open source defineR — comme le couvre la leçon Define-XML. Soyez précis sur cette frontière : metacore est un lecteur et un vérificateur, non un générateur.

Le Submissions Working Group du R Consortium publie tout sur GitHub. Commencez par le hub du Submissions Working Group, puis le dépôt ADaM du pilote 3 et le dépôt de soumission à la FDA ; le dépôt du pilote 1 montre la lignée antérieure. Tous les jeux de données, programmes et l’empaquetage sont publics.

Testez vos connaissances

Étendez le pipeline. Prenez le jeu de données exporté de l’étape 2, ajoutez RACE à la spécification de variables (c’est déjà une colonne dans sub), relancez l’export xportr avec strict_checks = TRUE, puis exécutez check_ct_col() sur RACE par rapport à la spécification metacore. RACE passe-t-il son contrôle de terminologie contrôlée, et qu’avez-vous ainsi prouvé sur un pipeline piloté par spécification ?

RACE est déjà dans sub et dans la spécification de l’étape 2, donc l’export n’a besoin d’aucune nouvelle colonne — l’exercice consiste à exécuter le contrôle dessus. Chargez la spécification metacore exactement comme le fait l’étape 3 (load() le .rda, select_dataset(..., verbose = "silent")), puis appelez get_bad_ct(back, adsl_mc, RACE). character(0) signifie que chaque valeur figure dans la liste de codes.

library(xportr); library(haven); library(metacore); library(metatools)

# `sub` and `var_spec` already include RACE (Stage 2). Re-export with strict checks:
xpt_path <- file.path(tempdir(), "adsl.xpt")
sub |>
  xportr_metadata(var_spec, "ADSL") |> xportr_type() |>
  xportr_length(length_source = "metadata") |> xportr_label() |>
  xportr_order() |> xportr_format() |> xportr_df_label(df_spec) |>
  xportr_write(xpt_path, strict_checks = TRUE)
back <- read_xpt(xpt_path)

# Load the spec and check RACE against its controlled terminology.
e <- new.env(); load(metacore_example("pilot_ADaM.rda"), envir = e)
adsl_mc <- select_dataset(get("metacore", envir = e), "ADSL", verbose = "silent")

get_bad_ct(back, adsl_mc, RACE)   # character(0) -> RACE is clean

RACE passe — get_bad_ct() renvoie character(0), donc chaque valeur de race figure dans la liste de codes de la spécification, tout comme SEX. C’est le bénéfice d’un pipeline piloté par spécification : une seule spécification metacore vérifie chaque variable de la même façon, séparant proprement les colonnes qui se conforment (SEX, RACE) de celle qui a dérivé (TRT01P), de sorte que vous sachiez exactement quoi corriger avant qu’un évaluateur ne le voie.

A. Cela rend xportr_write() plus rapide en sautant l’application des métadonnées. B. Cela promeut toute violation de conformité v5 (un nom, un libellé ou une valeur trop longs) en une erreur bloquante qui interrompt l’écriture, de sorte qu’un .xpt non conforme ne puisse pas être livré — tandis que la valeur par défaut FALSE se contente d’avertir et écrit le fichier quand même. C. Cela génère automatiquement les métadonnées Define-XML aux côtés du fichier de transport.

B. strict_checks = TRUE transforme chaque constat de conformité en une erreur bloquante et refuse d’écrire, ce qui est exactement ce dont une soumission a besoin — une violation devrait arrêter le pipeline, non le décorer d’un avertissement que vous pourriez faire défiler sans le voir. A est faux : les contrôles s’exécutent quand même (c’est le propos), et les métadonnées sont quand même appliquées. C est faux : xportr n’écrit jamais de define.xml — c’est le domaine de metacore que de le lire, et Pinnacle 21 / defineR que de le générer.

Conclusion

C’est ici que la série sur l’assemblage du dossier de soumission se rassemble. Un jeu de données, quatre étapes : construisez l’ADSL à partir du SDTM avec admiral, exportez-le vers un XPT v5 conforme avec xportr et strict_checks = TRUE, vérifiez-le par rapport à une spécification metacore avec metatools — où le contrôle de terminologie contrôlée a gagné sa place en attrapant un véritable écart — et empaquetez les programmes de construction en un seul artefact texte ASCII avec pkglite. Mis à l’échelle sur chaque jeu de données d’analyse, les tableaux et figures, le Define-XML, et les guides de l’évaluateur, ce même motif est une soumission entièrement en R — le workflow exact que les pilotes du R Consortium ont livré à la FDA et rendu public. Gardez la formulation avec exactitude : les pilotes prouvent que le pipeline est faisable et acceptable, un exemple fonctionnel que quiconque peut suivre — jamais qu’un langage est « approuvé ». Construisez-le correctement, étape par étape, et le dossier qu’un régulateur reçoit en est un que vous pouvez assumer.

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.

Références

Leçons connexes

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 = {Reproduire une soumission R à la FDA de bout en bout : le
    projet de synthèse pharmaverse},
  date = {2026-07-01},
  url = {https://www.datanovia.com/learn/pharma-clinical/07-submission-packaging/reproduce-fda-r-pilot},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Reproduire une soumission R à la FDA de bout en bout : le projet de synthèse pharmaverse.” 2026. July 1. https://www.datanovia.com/learn/pharma-clinical/07-submission-packaging/reproduce-fda-r-pilot.