
ADVS en R avec admiral : le jeu de données d’analyse des signes vitaux et les valeurs initiales par point temporel
Dérivez un jeu de données d’analyse des signes vitaux (ADVS) conforme au CDISC avec admiral — la structure BDS plus la machinerie du point temporel d’analyse (ATPT/ATPTN) et de BASETYPE dont la pression artérielle orthostatique a besoin, de sorte qu’une valeur initiale en position couchée et une valeur initiale en position debout soient chacune calculées par rapport à la bonne position
Un tutoriel complet et exécutable pour l’ADVS, le jeu de données d’analyse des signes vitaux, et la machinerie par point temporel dont un ADaM Findings a besoin lorsqu’un paramètre est mesuré dans plus d’une position. Récapitulez la BDS (structure de données de base) vue avec ADLB, puis construisez un ADVS conforme en R à partir du domaine SDTM VS avec admiral : PARAMCD/AVAL depuis VSTESTCD/VSSTRESN, le point temporel d’analyse (ATPT/ATPTN) depuis VSTPT/VSTPTNUM, derive_basetype_records() pour définir un BASETYPE propre à chaque position pour la pression artérielle orthostatique, puis derive_var_base()/derive_var_chg() au sein de chaque BASETYPE, plus derive_var_anrind() et les marqueurs de la valeur initiale (ABLFL) et d’analyse (ANL01FL) — le tout sur des données pharmaverse publiques, de sorte que chaque ligne s’exécute.
- L’ADVS (le jeu de données d’analyse des signes vitaux) est le deuxième jeu de données Findings BDS. Il partage la BDS (structure de données de base) d’ADLB, le jeu de données d’analyse de laboratoire — une ligne par sujet, par paramètre, par point temporel, organisée autour de
PARAMCDetAVAL— mais ajoute une dimension qu’ADLB n’a pas : le point temporel d’analyse. - Les signes vitaux sont mesurés à plusieurs positions. La pression artérielle orthostatique est recueillie en position couchée (allongé) et debout à la même visite. Ce sont des points temporels d’analyse (
ATPT/ATPTN) différents, et chacun a besoin de sa propre valeur initiale — la pression artérielle debout d’un sujet doit être comparée à sa valeur initiale debout, pas à celle en position couchée. derive_basetype_records()est la nouvelle machinerie. Elle appose unBASETYPEsur chaque ligne de sorte que la valeur initiale, la variation par rapport à la valeur initiale et le marqueur d’analyse soient tous calculés au sein d’une position, et non entre positions.- Tout le reste est le schéma d’ADLB, calé sur BASETYPE.
derive_var_extreme_flag()pour le marqueur de la valeur initiale (ABLFL),derive_var_base()/derive_var_chg()pour la variation par rapport à la valeur initiale, etderive_var_anrind()pour l’indicateur de plage de référence — chaque listeby_varsgagne simplementBASETYPE. - Réussissez BASETYPE et l’analyse par position est automatique. Ratez-le et chaque valeur debout est silencieusement comparée à une valeur initiale en position couchée — le bug d’ADVS le plus courant.
Introduction
La pression artérielle n’est pas mesurée une seule fois. Dans un essai qui surveille les effets orthostatiques — une chute de la pression artérielle au passage debout qu’un médicament peut provoquer ou aggraver — les signes vitaux sont recueillis dans plus d’une position à la même visite : d’abord en position couchée (allongé), puis debout après une minute et après trois. Le même sujet, la même visite, le même paramètre, trois mesures. Et chaque position a son propre niveau normal : la pression artérielle debout est généralement plus basse qu’en position couchée, de sorte que la valeur debout d’un sujet ne peut être jugée que par rapport à sa valeur initiale debout.
C’est la subtilité que l’ADVS — le jeu de données d’analyse des signes vitaux — ajoute à l’ADaM (Analysis Data Model) Findings que vous connaissez déjà. Dans la leçon ADLB, vous avez construit la BDS (structure de données de base) — la forme verticale que définit le standard CDISC ADaM : une ligne par sujet, par paramètre, par point temporel, avec la valeur dans AVAL et l’analyte nommé par PARAMCD. ADLB n’avait aucune position à gérer — un prélèvement de laboratoire est un prélèvement de laboratoire — de sorte qu’une seule valeur initiale par couple sujet-paramètre était correcte. L’ADVS porte le point temporel d’analyse, et avec lui le besoin d’une valeur initiale par position. Cette leçon enseigne cette machinerie : le derive_basetype_records() d’admiral et la variable BASETYPE qu’il crée.
Nous construisons un ADVS conforme en R avec admiral, le paquet pharmaverse pour la dérivation ADaM, sur des données d’exemple pharmaverse publiques, en suivant la vignette Creating a BDS Finding ADaM d’admiral — qui est bâtie sur les signes vitaux précisément parce que c’est là que vit la dimension du point temporel.
Voici où nous allons — la variation moyenne par rapport à la valeur initiale de la pression artérielle systolique (PAS) au cours de l’étude, par bras de traitement, ventilée par position, calculée directement à partir de l’ADVS que nous dérivons :
Cette figure se lit sur quatre colonnes de l’ADVS — PARAMCD, AVISIT, CHG et la position derrière chaque BASETYPE — et elle se ventile par position parce que la variation est calculée au sein d’une position. À la fin de cette leçon, vous aurez dérivé tout cela. Si BDS, ADaM ou SDTM (le Study Data Tabulation Model — les données collectées standardisées à partir desquelles l’ADaM est construit) vous sont nouveaux, commencez par la leçon ADLB et la structure BDS ; l’ADVS réutilise chaque mouvement qu’elle enseigne et en ajoute un.
Ce que l’ADVS ajoute à la BDS : la dimension du point temporel
L’ADVS est un jeu de données BDS, donc la colonne vertébrale est la même que celle d’ADLB — une ligne par sujet, par paramètre, par point temporel, avec PARAMCD nommant le paramètre et AVAL contenant sa valeur. Ce qui est nouveau, c’est qu’un paramètre de signes vitaux peut être mesuré plusieurs fois à une même visite, dans différentes positions du corps, de sorte que le « point temporel » n’est plus seulement la visite. Deux variables portent cela, et une variable dérivée le rend exploitable :
| Variable | Libellé | Ce qu’elle contient |
|---|---|---|
PARAMCD / PARAM |
Code du paramètre / Paramètre | Le signe vital — SYSBP, DIABP, PULSE, TEMP. (Comme dans ADLB.) |
AVAL |
Valeur d’analyse | Le résultat numérique pour ce paramètre à ce point temporel. (Comme dans ADLB.) |
AVISIT / AVISITN |
Visite d’analyse | Le libellé de la visite d’analyse et son ordre de tri. (Comme dans ADLB.) |
ATPT / ATPTN |
Point temporel d’analyse / (N) | Nouveau. Le point temporel au sein de la visite — “AFTER LYING DOWN FOR 5 MINUTES” (position couchée) vs “AFTER STANDING FOR 1 MINUTE” — et son code numérique. |
ABLFL |
Marqueur d’enregistrement de la valeur initiale | "Y" sur l’unique ligne de la valeur initiale par position pour ce couple sujet-paramètre. |
BASE / CHG / PCHG |
Valeur initiale / Variation / % de variation | La valeur initiale et la variation par rapport à elle — calculées au sein de la position. |
ANRIND |
Indicateur de plage de référence d’analyse | LOW / NORMAL / HIGH pour cette valeur par rapport à sa plage de référence. (Comme dans ADLB.) |
ANL01FL |
Marqueur d’analyse 01 | "Y" sur l’unique enregistrement qui alimente une analyse donnée, par position par visite. |
BASETYPE |
Type de valeur initiale | La nouvelle variable clé. Nomme le groupe d’enregistrements qui partagent une valeur initiale — un par position. Chaque dérivation dépendante de la valeur initiale se cale dessus. |
Toute la différence entre ADLB et ADVS tient en un mot : position. ADLB marque une valeur initiale par couple sujet-paramètre. L’ADVS doit marquer une valeur initiale par couple sujet-paramètre-par-position, parce qu’une mesure en position couchée et une mesure debout prises à quelques minutes d’intervalle ne sont pas comparables. Le mécanisme qu’admiral vous donne pour cela est BASETYPE — une étiquette que vous attachez à chaque enregistrement, nommant le groupe de valeur initiale auquel il appartient. Une fois que chaque ligne porte un BASETYPE, les mêmes fonctions d’admiral que vous avez utilisées pour ADLB font le travail par position automatiquement, simplement en ajoutant BASETYPE à leurs by_vars.
L’essai que nous utilisons est l’étude pilote CDISC (Clinical Data Interchange Standards Consortium) livrée dans pharmaversesdtm — une étude synthétique, libre de licence, à bras parallèles comparant un placebo à deux doses de Xanomeline, dont les signes vitaux sont recueillis exactement à ces points temporels orthostatiques.
Les données sources
L’ADVS est construit à partir du domaine SDTM VS (Vital Signs, signes vitaux) plus l’ADSL (Subject-Level Analysis Dataset, jeu de données d’analyse au niveau du sujet) — la colonne vertébrale une-ligne-par-sujet que vous avez dérivée en premier, depuis laquelle chaque ADaM Findings lit ses variables de traitement. VS est déjà vertical, une ligne par résultat, et il porte le point temporel (VSTPT/VSTPTNUM) et la position (VSPOS) qui rendent l’ADVS intéressant.
library(admiral)
library(dplyr, warn.conflicts = FALSE)
library(pharmaversesdtm)
library(pharmaverseadam)
library(lubridate)
library(stringr)
library(tibble)
# VS — the raw vital-signs domain, one row per result. convert_blanks_to_na()
# turns SDTM's empty strings "" into proper NA so admiral's functions work.
vs <- pharmaversesdtm::vs %>% convert_blanks_to_na()
# ADSL — one row per subject, the treatment + demographics backbone we merge from.
adsl <- pharmaverseadam::adsl
# The timepoint/position structure that makes ADVS different from ADLB:
vs %>% count(VSTPTNUM, VSTPT, VSPOS)# A tibble: 4 × 4
VSTPTNUM VSTPT VSPOS n
<dbl> <chr> <chr> <int>
1 815 AFTER LYING DOWN FOR 5 MINUTES SUPINE 8208
2 816 AFTER STANDING FOR 1 MINUTE STANDING 8204
3 817 AFTER STANDING FOR 3 MINUTES STANDING 8207
4 NA <NA> <NA> 5024
Lisez ce tableau : chaque mesure de pression artérielle et de pouls est prise à l’un de trois points temporels — après s’être allongé 5 minutes (VSTPTNUM == 815, la position couchée), après s’être tenu debout 1 minute (816) et après s’être tenu debout 3 minutes (817) — tandis que la taille, le poids et la température n’ont pas de point temporel (NA). Ces codes numériques de point temporel sont ce que nous allons transformer en positions et, à partir d’eux, en valeurs initiales par position.
Fusionner ADSL et dériver la date d’analyse
Chaque ADaM Findings commence par apporter les variables de traitement d’ADSL sur chaque enregistrement, parce que la logique de la valeur initiale se cale sur TRTSDT (la date de première dose). derive_vars_dt() convertit ensuite le caractère VSDTC en une véritable date d’analyse ADT.
adsl_vars <- exprs(TRTSDT, TRTEDT, TRT01A, TRT01P)
advs <- vs %>%
# bring treatment dates + arms from ADSL onto each vital-signs record
derive_vars_merged(
dataset_add = adsl,
new_vars = adsl_vars,
by_vars = exprs(STUDYID, USUBJID)
) %>%
# ADT: the analysis date, parsed from the ISO character VSDTC
derive_vars_dt(new_vars_prefix = "A", dtc = VSDTC) %>%
# ADY: study day of the measurement, relative to first dose (TRTSDT)
derive_vars_dy(reference_date = TRTSDT, source_vars = exprs(ADT))
advs %>%
filter(VSTESTCD == "SYSBP", USUBJID == "01-701-1015") %>%
select(USUBJID, VISIT, VSTPT, ADT, ADY, TRTSDT) %>%
head(4)# A tibble: 4 × 6
USUBJID VISIT VSTPT ADT ADY TRTSDT
<chr> <chr> <chr> <date> <dbl> <date>
1 01-701-1015 SCREENING 1 AFTER LYING DOWN FOR 5 MI… 2013-12-26 -7 2014-01-02
2 01-701-1015 SCREENING 1 AFTER STANDING FOR 1 MINU… 2013-12-26 -7 2014-01-02
3 01-701-1015 SCREENING 1 AFTER STANDING FOR 3 MINU… 2013-12-26 -7 2014-01-02
4 01-701-1015 SCREENING 2 AFTER LYING DOWN FOR 5 MI… 2013-12-31 -2 2014-01-02
ADT est la date que les dérivations ci-dessous comparent à TRTSDT ; ADY est le jour d’étude familier (le jour 1 est la première dose, les jours négatifs sont la sélection pré-dose), qui est la façon dont les évaluateurs lisent la chronologie.
Poser la colonne vertébrale BDS : PARAMCD, PARAM et AVAL
Nommez maintenant le paramètre de chaque ligne et hissez sa valeur dans AVAL. Nous mappons les codes des tests de signes vitaux vers des paramètres d’analyse via une petite table de correspondance — derive_vars_merged_lookup() la joint sur VSTESTCD, donnant un PARAMCD gouverné et un PARAM lisible par un humain — puis nous fixons AVAL à partir du résultat numérique standardisé VSSTRESN.
# One controlled place that maps each SDTM test code to its analysis parameter.
param_lookup <- tibble::tribble(
~VSTESTCD, ~PARAMCD, ~PARAM,
"HEIGHT", "HEIGHT", "Height (cm)",
"WEIGHT", "WEIGHT", "Weight (kg)",
"DIABP", "DIABP", "Diastolic Blood Pressure (mmHg)",
"PULSE", "PULSE", "Pulse Rate (beats/min)",
"SYSBP", "SYSBP", "Systolic Blood Pressure (mmHg)",
"TEMP", "TEMP", "Temperature (C)"
)
advs <- advs %>%
derive_vars_merged_lookup(
dataset_add = param_lookup,
new_vars = exprs(PARAMCD, PARAM),
by_vars = exprs(VSTESTCD)
) %>%
mutate(AVAL = VSSTRESN) # the numeric value for THIS parameter
advs %>%
filter(USUBJID == "01-701-1015", VISIT == "WEEK 2", VSTPTNUM == 815) %>%
select(USUBJID, PARAMCD, PARAM, VSTPT, AVAL) %>%
head(6)# A tibble: 3 × 5
USUBJID PARAMCD PARAM VSTPT AVAL
<chr> <chr> <chr> <chr> <dbl>
1 01-701-1015 DIABP Diastolic Blood Pressure (mmHg) AFTER LYING DOWN FO… 56
2 01-701-1015 PULSE Pulse Rate (beats/min) AFTER LYING DOWN FO… 58
3 01-701-1015 SYSBP Systolic Blood Pressure (mmHg) AFTER LYING DOWN FO… 114
Utiliser une table de correspondance plutôt qu’un simple PARAMCD = VSTESTCD est la façon dont un ADVS de production garde ses paramètres gouvernés par des métadonnées — le même code, le même libellé et le même ordonnancement que chaque table utilise. AVAL est désormais agnostique au paramètre : ne le lisez jamais qu’avec PARAMCD, exactement comme dans ADLB.
Dériver la visite d’analyse et le point temporel d’analyse
Voici l’étape qu’ADLB n’avait pas. En plus de la visite d’analyse (AVISIT/AVISITN), nous dérivons le point temporel d’analyse (ATPT/ATPTN) directement depuis le VSTPT/VSTPTNUM SDTM. Ce point temporel est ce qui distingue une mesure en position couchée d’une mesure debout à la même visite.
advs <- advs %>%
mutate(
AVISIT = case_when(
str_detect(VISIT, "SCREEN|UNSCHED|RETRIEVAL|AMBUL") ~ NA_character_,
!is.na(VISIT) ~ str_to_title(VISIT)
),
AVISITN = as.numeric(case_when(
VISIT == "BASELINE" ~ "0",
str_detect(VISIT, "WEEK") ~ str_trim(str_replace(VISIT, "WEEK", ""))
)),
ATPT = VSTPT, # the within-visit timepoint label (position, here)
ATPTN = VSTPTNUM # its numeric code, for sorting and BASETYPE
)
advs %>% count(ATPTN, ATPT)# A tibble: 4 × 3
ATPTN ATPT n
<dbl> <chr> <int>
1 815 AFTER LYING DOWN FOR 5 MINUTES 8208
2 816 AFTER STANDING FOR 1 MINUTE 8204
3 817 AFTER STANDING FOR 3 MINUTES 8207
4 NA <NA> 5024
AVISIT réduit à NA les visites non planifiées et de sélection, exactement comme dans ADLB, de sorte qu’elles ne polluent pas les résumés par visite. ATPT/ATPTN portent la structure orthostatique à travers le jeu de données : trois points temporels réels pour la pression artérielle et le pouls, NA pour les mesures prises une fois par visite. Tout, à partir d’ici, se cale sur ATPTN.
Classer chaque valeur par rapport à sa plage de référence (ANRIND)
Un signe vital est jugé par rapport à une plage de référence, tout comme une valeur de laboratoire. ADaM stocke cela dans ANRIND (l’indicateur de plage de référence d’analyse) : LOW, NORMAL ou HIGH. derive_var_anrind() le calcule, en lisant la plage depuis les colonnes d’analyse ANRLO/ANRHI, donc nous fusionnons d’abord une petite table de plages par paramètre.
# Per-parameter reference ranges (the study's analysis ranges).
range_lookup <- tibble::tribble(
~PARAMCD, ~ANRLO, ~ANRHI,
"SYSBP", 90, 130,
"DIABP", 60, 80,
"PULSE", 60, 100,
"TEMP", 36.5, 37.5
)
advs <- advs %>%
derive_vars_merged(dataset_add = range_lookup, by_vars = exprs(PARAMCD)) %>%
derive_var_anrind()
advs %>%
filter(PARAMCD == "SYSBP", USUBJID == "01-701-1015", VISIT == "WEEK 8") %>%
select(USUBJID, PARAMCD, ATPT, AVAL, ANRLO, ANRHI, ANRIND) %>%
head(3)# A tibble: 3 × 7
USUBJID PARAMCD ATPT AVAL ANRLO ANRHI ANRIND
<chr> <chr> <chr> <dbl> <dbl> <dbl> <chr>
1 01-701-1015 SYSBP AFTER LYING DOWN FOR 5 MINUTES 138 90 130 HIGH
2 01-701-1015 SYSBP AFTER STANDING FOR 1 MINUTE 146 90 130 HIGH
3 01-701-1015 SYSBP AFTER STANDING FOR 3 MINUTES 140 90 130 HIGH
derive_var_anrind() lit la règle sur les données : en dessous d’ANRLO c’est LOW, au-dessus d’ANRHI c’est HIGH, entre les deux c’est NORMAL. La même plage s’applique ici à toutes les positions, mais l’ indicateur est calculé ligne par ligne — de sorte qu’une valeur debout peut être HIGH alors que la valeur en position couchée à la même visite est NORMAL.
Définir le type de valeur initiale par position (BASETYPE) — l’étape clé
C’est la dérivation qui fait de l’ADVS plus qu’un ADLB. Parce qu’un sujet a une valeur initiale en position couchée et une valeur initiale debout pour le même paramètre, nous ne pouvons pas marquer « la » valeur initiale — nous devons en marquer une par position. Le derive_basetype_records() d’admiral rend cela possible : il assigne une étiquette BASETYPE à chaque ligne, nommant le groupe d’enregistrements qui partagent une valeur initiale. Nous définissons un BASETYPE par code de point temporel, plus un fourre-tout "LAST" pour les paramètres qui n’ont pas de point temporel.
advs <- advs %>%
derive_basetype_records(
basetypes = exprs(
"LAST: AFTER LYING DOWN FOR 5 MINUTES" = ATPTN == 815, # supine
"LAST: AFTER STANDING FOR 1 MINUTE" = ATPTN == 816, # standing, 1 min
"LAST: AFTER STANDING FOR 3 MINUTES" = ATPTN == 817, # standing, 3 min
"LAST" = is.na(ATPTN) # height/weight/temp
)
)
advs %>% count(ATPT, ATPTN, BASETYPE)# A tibble: 4 × 4
ATPT ATPTN BASETYPE n
<chr> <dbl> <chr> <int>
1 AFTER LYING DOWN FOR 5 MINUTES 815 LAST: AFTER LYING DOWN FOR 5 MINUT… 8208
2 AFTER STANDING FOR 1 MINUTE 816 LAST: AFTER STANDING FOR 1 MINUTE 8204
3 AFTER STANDING FOR 3 MINUTES 817 LAST: AFTER STANDING FOR 3 MINUTES 8207
4 <NA> NA LAST 5024
Lisez la liste basetypes comme la définition de ce qui partage une valeur initiale : chaque condition sélectionne un ensemble d’enregistrements, et chaque enregistrement correspondant reçoit ce BASETYPE. Les enregistrements en position couchée recevront une valeur initiale en position couchée, les enregistrements debout-à-1-minute une valeur initiale debout-à-1-minute, et ainsi de suite. Les paramètres pris une fois par visite (taille, poids, température) n’ont pas de point temporel, donc ils tombent dans le groupe "LAST" simple. Rien n’est encore comparé — BASETYPE ne fait qu’étiqueter les groupes ; les trois étapes suivantes font le travail par groupe simplement en incluant BASETYPE dans leurs by_vars.
Marquer l’enregistrement de la valeur initiale par position (ABLFL)
Marquez maintenant la valeur initiale de la même façon qu’ADLB — la dernière valeur non manquante le jour de la première dose ou avant — mais au sein de chaque BASETYPE. Ajouter BASETYPE à by_vars est tout le changement : cela transforme « une valeur initiale par couple sujet-paramètre » en « une valeur initiale par couple sujet-paramètre-par-position ».
advs <- advs %>%
restrict_derivation(
derivation = derive_var_extreme_flag,
args = params(
by_vars = exprs(STUDYID, USUBJID, BASETYPE, PARAMCD), # ... now PER position
order = exprs(ADT, ATPTN, VISITNUM),
new_var = ABLFL,
mode = "last"
),
filter = (!is.na(AVAL) & ADT <= TRTSDT & !is.na(BASETYPE))
)
advs %>% count(ABLFL)# A tibble: 2 × 2
ABLFL n
<chr> <int>
1 Y 3048
2 <NA> 26595
by_vars se lit désormais : une valeur initiale par sujet, par BASETYPE, par paramètre — de sorte que la PAS en position couchée et la PAS debout d’un sujet reçoivent chacune leur propre enregistrement ABLFL == "Y". Le filter (valeur non manquante, le jour de la première dose ou avant) et mode = "last" sont inchangés par rapport à ADLB. Cette seule entrée by_vars ajoutée est la différence entre un ADVS correct et un où chaque valeur debout est comparée à une valeur initiale en position couchée.
Calculer la variation par rapport à la valeur initiale au sein de la position (BASE, CHG, PCHG)
Une fois la valeur initiale marquée par position, la variation par rapport à la valeur initiale est la séquence d’ADLB avec BASETYPE ajouté à derive_var_base(). derive_var_base() copie la valeur initiale marquée de chaque BASETYPE sur chaque ligne de cette position ; puis derive_var_chg() et derive_var_pchg() calculent la variation absolue et en pourcentage.
advs <- advs %>%
derive_var_base(
by_vars = exprs(STUDYID, USUBJID, PARAMCD, BASETYPE), # copy the per-position baseline
source_var = AVAL,
new_var = BASE
) %>%
derive_var_chg() %>% # CHG = AVAL - BASE
derive_var_pchg() # PCHG = 100 * (AVAL - BASE) / BASE
advs %>%
filter(PARAMCD == "SYSBP", USUBJID == "01-701-1015",
AVISIT %in% c("Baseline", "Week 8"), ATPTN %in% c(815, 816)) %>%
arrange(ATPTN, AVISITN) %>%
select(AVISIT, ATPT, BASE, AVAL, CHG, ANRIND) %>%
head(4)# A tibble: 4 × 6
AVISIT ATPT BASE AVAL CHG ANRIND
<chr> <chr> <dbl> <dbl> <dbl> <chr>
1 Baseline AFTER LYING DOWN FOR 5 MINUTES 130 130 0 NORMAL
2 Week 8 AFTER LYING DOWN FOR 5 MINUTES 130 138 8 HIGH
3 Baseline AFTER STANDING FOR 1 MINUTE 121 121 0 NORMAL
4 Week 8 AFTER STANDING FOR 1 MINUTE 121 146 25 HIGH
Parce que derive_var_base() s’est calé sur BASETYPE, les lignes en position couchée de ce sujet portent un BASE en position couchée et les lignes debout portent un BASE debout — des nombres différents, comme il se doit. La variation en position couchée à la semaine 8 est mesurée par rapport à la valeur initiale en position couchée, la variation debout à la semaine 8 par rapport à la valeur initiale debout. C’est le bénéfice de BASETYPE : la variation par rapport à la valeur initiale est honnête par position, sans écriture comptable supplémentaire.
Marquer les enregistrements d’analyse par position (ANL01FL)
Une table de sécurité veut habituellement un enregistrement par sujet, par paramètre, par visite — mais maintenant par position aussi. ANL01FL le marque, et comme ABLFL il gagne simplement BASETYPE dans ses by_vars.
advs <- advs %>%
restrict_derivation(
derivation = derive_var_extreme_flag,
args = params(
by_vars = exprs(STUDYID, USUBJID, BASETYPE, PARAMCD, AVISIT), # per position, per visit
order = exprs(ADT, ATPTN, AVAL),
new_var = ANL01FL,
mode = "last"
),
filter = !is.na(AVISITN) # scheduled analysis visits
)
advs %>% count(ANL01FL)# A tibble: 2 × 2
ANL01FL n
<chr> <int>
1 Y 19783
2 <NA> 9860
En aval, une table de signes vitaux par visite filtre sur ANL01FL == "Y" et se voit garantir une ligne par sujet-paramètre-visite-position — les mesures en position couchée et debout gardées séparées, sans double comptage dû à un prélèvement répété.
Lire l’histoire par position d’un sujet
Le résultat est un ADVS conforme : vertical, en forme de BDS, avec une valeur initiale par position attachée à chaque ligne. Lisez la pression artérielle systolique d’un seul sujet à travers les positions et les visites et tout le sens de BASETYPE est visible d’un coup.
advs %>%
filter(PARAMCD == "SYSBP", USUBJID == "01-701-1015",
AVISIT %in% c("Baseline", "Week 2", "Week 8"), !is.na(ATPTN)) %>%
arrange(ATPTN, AVISITN) %>%
select(AVISIT, ATPT, BASE, AVAL, CHG, ANRIND, ABLFL) %>%
as.data.frame() AVISIT ATPT BASE AVAL CHG ANRIND ABLFL
1 Baseline AFTER LYING DOWN FOR 5 MINUTES 130 130 0 NORMAL Y
2 Week 2 AFTER LYING DOWN FOR 5 MINUTES 130 114 -16 NORMAL <NA>
3 Week 8 AFTER LYING DOWN FOR 5 MINUTES 130 138 8 HIGH <NA>
4 Baseline AFTER STANDING FOR 1 MINUTE 121 121 0 NORMAL Y
5 Week 2 AFTER STANDING FOR 1 MINUTE 121 121 0 NORMAL <NA>
6 Week 8 AFTER STANDING FOR 1 MINUTE 121 146 25 HIGH <NA>
7 Baseline AFTER STANDING FOR 3 MINUTES 131 131 0 HIGH Y
8 Week 2 AFTER STANDING FOR 3 MINUTES 131 132 1 HIGH <NA>
9 Week 8 AFTER STANDING FOR 3 MINUTES 131 140 9 HIGH <NA>
Trois positions, trois valeurs initiales : valeur initiale en position couchée 130, debout-à-1-minute 121, debout-à-3-minutes 131 — le même sujet, le même paramètre, la même visite de valeur initiale, et pourtant trois valeurs BASE différentes parce que la position compte. Chaque CHG post-valeur-initiale est calculé par rapport à la valeur initiale de sa propre position. Si nous avions marqué une seule valeur initiale à travers les positions (l’approche d’ADLB), les valeurs debout auraient été mesurées par rapport à 130 au lieu de 121 — une erreur de 9 mmHg injectée dans chaque variation debout, et exactement l’erreur que BASETYPE existe pour prévenir.
Demandez à Prova « comment ajouter un marqueur post-valeur-initiale du pire cas (WORSTFL) par position à mon ADVS avec le slice_derivation() d’admiral ? » — 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. Ask Prova →
Problèmes fréquents
Chaque valeur debout est comparée à la valeur initiale en position couchée. C’est le bug d’ADVS, et il survient chaque fois que BASETYPE est absent de la machinerie de la valeur initiale. Si vous marquez ABLFL et copiez BASE avec by_vars = exprs(STUDYID, USUBJID, PARAMCD) — les clés d’ADLB — admiral choisit une valeur initiale à travers toutes les positions, et chaque CHG debout est faux de l’écart couché-debout. Le correctif est d’exécuter derive_basetype_records() en premier et d’ajouter BASETYPE aux by_vars de derive_var_extreme_flag() (pour ABLFL) et de derive_var_base(). Si les valeurs initiales couchée et debout d’un sujet ressortent identiques alors qu’elles devraient différer, voilà pourquoi.
Un paramètre sans point temporel n’obtient aucune valeur initiale. La taille, le poids et la température ont ATPTN == NA, donc ils n’obtiennent un BASETYPE que si votre liste basetypes inclut une condition fourre-tout comme "LAST" = is.na(ATPTN). Omettez-la et ces lignes ont BASETYPE == NA, le filter = !is.na(BASETYPE) les écarte d’ABLFL, et leurs BASE/CHG reviennent en NA. Donnez toujours un groupe BASETYPE aux paramètres sans point temporel.
Des positions dupliquées gonflent un décompte par visite. Un sujet mesuré en position couchée et debout à une même visite y a (au moins) deux lignes SYSBP. Si une table double-compte, c’est que vous avez filtré sur AVISIT mais oublié de garder les positions séparées — filtrez sur ANL01FL == "Y" (qui est par position) ou groupez par ATPTN/BASETYPE, jamais AVISIT seul, pour tout résumé de signes vitaux par visite.
Questions fréquentes
L’ADVS est le jeu de données d’analyse des signes vitaux — un ADaM Findings bâti sur la BDS (structure de données de base), une ligne par sujet, par paramètre (PARAMCD = SYSBP, DIABP, PULSE, TEMP…), par point temporel, avec la valeur dans AVAL. Il ajoute une dimension de point temporel d’analyse (ATPT/ATPTN) qu’ADLB n’a pas, parce que les signes vitaux comme la pression artérielle orthostatique sont mesurés dans plus d’une position à une visite. Vous le construisez à partir du domaine SDTM VS avec admiral, en suivant la vignette BDS finding.
BASETYPE est une variable ADaM qui nomme le groupe d’enregistrements partageant une valeur initiale. Vous en avez besoin chaque fois qu’un couple sujet-paramètre a plus d’une valeur initiale — le plus souvent des signes vitaux mesurés dans plusieurs positions (couché vs debout pour la pression artérielle orthostatique), où une valeur debout doit être comparée à une valeur initiale debout, pas à une couchée. admiral l’assigne avec derive_basetype_records() ; vous ajoutez ensuite BASETYPE aux by_vars des dérivations du marqueur de la valeur initiale et de la variation par rapport à la valeur initiale, de sorte que chaque position soit traitée séparément. ADLB, dont les prélèvements de laboratoire n’ont pas de positions, n’en a pas besoin.
derive_basetype_records() prend une liste basetypes de conditions nommées — par exemple "LAST: AFTER STANDING FOR 1 MINUTE" = ATPTN == 816 — et appose le nom correspondant sur chaque enregistrement comme BASETYPE. Un enregistrement peut correspondre à plus d’une condition (il est alors dupliqué, une fois par BASETYPE correspondant), ce qui est la façon dont une même mesure physique peut servir de référence de valeur initiale pour plusieurs regroupements d’analyse. Chaque dérivation dépendante de la valeur initiale en aval se cale sur BASETYPE.
Les deux sont des jeux de données Findings BDS construits avec les mêmes fonctions admiral, et les deux dérivent la valeur initiale (ABLFL), la variation par rapport à la valeur initiale (CHG/PCHG) et l’indicateur de plage de référence (ANRIND). La différence est le point temporel d’analyse : ADLB (laboratoire) a une mesure par paramètre par visite, donc une valeur initiale par couple sujet-paramètre est correcte. L’ADVS (signes vitaux) peut avoir plusieurs mesures par visite à différentes positions (ATPT/ATPTN), donc il a besoin d’un BASETYPE et d’une valeur initiale par position. Apprenez ADLB en premier — l’ADVS est ce schéma plus la dimension du point temporel.
ATPT est le point temporel d’analyse — la description du point temporel au sein de la visite, telle que “AFTER LYING DOWN FOR 5 MINUTES” ou “AFTER STANDING FOR 1 MINUTE” — et ATPTN est son code numérique, utilisé pour le tri et pour définir BASETYPE. Ils se mappent typiquement directement depuis le VSTPT/VSTPTNUM SDTM. Pour les évaluations orthostatiques, le point temporel encode en fait la position du corps, c’est pourquoi la valeur initiale par point temporel (via BASETYPE) est le nœud d’une dérivation ADVS.
Testez vos connaissances
En utilisant l’objet advs construit dans cette leçon, montrez les enregistrements de valeur initiale (ABLFL == "Y") pour la pression artérielle diastolique (PARAMCD == "DIABP") pour le sujet "01-701-1015", un par position. Les valeurs initiales couchée et debout sont-elles identiques ou différentes — et pourquoi est-ce important ?
Chaque enregistrement de valeur initiale est déjà marqué. Filtrez sur PARAMCD == "DIABP", USUBJID == "01-701-1015" et ABLFL == "Y", puis sélectionnez BASETYPE, ATPT et BASE (ou AVAL). Aucune nouvelle dérivation n’est nécessaire — BASETYPE et ABLFL ont fait le travail.
advs %>%
filter(PARAMCD == "DIABP", USUBJID == "01-701-1015", ABLFL == "Y") %>%
select(BASETYPE, ATPT, AVAL, BASE)advs %>%
filter(PARAMCD == "DIABP", USUBJID == "01-701-1015", ABLFL == "Y") %>%
arrange(ATPTN) %>%
select(BASETYPE, ATPT, AVAL, BASE)Il y a une ligne de valeur initiale par position, et leurs valeurs diffèrent — la valeur initiale couchée n’est pas la valeur initiale debout. Cela importe parce que CHG et PCHG sont calculés par rapport à BASE : si les deux positions partageaient une seule valeur initiale, chaque variation debout serait mesurée à partir de la mauvaise référence. Parce que nous avons calé ABLFL et BASE sur BASETYPE, la variation de chaque position est correcte. Remplacez "DIABP" par "SYSBP" ou "PULSE" et la même structure par position tient.
A. Parce que les signes vitaux ont plus de paramètres que le laboratoire B. Parce qu’un paramètre de signes vitaux peut être mesuré à plusieurs positions en une visite, chacune ayant besoin de sa propre valeur initiale C. Parce que l’ADVS utilise une colonne de valeur différente d’AVAL
B. Les signes vitaux orthostatiques sont mesurés couché et debout à la même visite, donc un couple sujet-paramètre a plus d’une valeur initiale. BASETYPE nomme ces groupes par position de sorte que la valeur initiale (ABLFL), BASE et la variation par rapport à la valeur initiale soient calculées au sein d’une position. A est sans rapport — le nombre de paramètres ne change pas la structure. C est faux : l’ADVS utilise AVAL et la forme BDS exactement comme ADLB. La seule vraie différence est la valeur initiale par position.
Conclusion
L’ADVS est ADLB plus une idée : une valeur initiale par position. La colonne vertébrale BDS est identique — une ligne par sujet, par paramètre, par point temporel, valeur dans AVAL, analyte dans PARAMCD — et les mêmes fonctions admiral dérivent le marqueur de plage de référence (derive_var_anrind()), la valeur initiale (derive_var_extreme_flag() → ABLFL), et la variation par rapport à la valeur initiale (derive_var_base() / derive_var_chg() / derive_var_pchg()). Ce que l’ADVS ajoute est le point temporel d’analyse (ATPT/ATPTN) et, à partir de lui, derive_basetype_records(), qui appose un BASETYPE sur chaque ligne de sorte que toutes ces dérivations s’exécutent au sein d’une position. Ajoutez BASETYPE aux by_vars et la pression artérielle orthostatique — couché versus debout — est traitée correctement sans machinerie supplémentaire. Ratez-le et vous comparez chaque valeur debout à une valeur initiale en position couchée. Réussissez BASETYPE, et le reste est le schéma d’ADLB que vous connaissez déjà.
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
- ADLB en R avec admiral et la structure BDS — le premier ADaM Findings et la structure de données de base que l’ADVS réutilise ; commencez ici si la BDS est nouvelle. · Créer le jeu de données d’analyse au niveau du sujet ADSL — la colonne vertébrale une-ligne-par-sujet depuis laquelle l’ADVS refusionne ses variables de traitement. · Construire le jeu de données ADaM des événements indésirables (ADAE) — le frère OCCDS (Occurrence Data Structure), la contrepartie « événements » de la BDS Findings. · Les standards CDISC : CDASH, SDTM, ADaM, Define-XML — où l’ADVS et la BDS s’inscrivent dans le modèle ADaM.
- Où cela s’inscrit : les fondations réglementaires et CDISC → construire l’ADaM avec admiral (ADSL → ADLB → ADVS — vous êtes ici → ADAE → ADTTE) → produire les tables, listings et figures que chaque analyse livre. L’ADVS étend le schéma BDS d’ADLB avec les valeurs initiales par point temporel dont les signes vitaux ont besoin.
Réutilisation
Citation
@online{2026,
author = {},
title = {ADVS en R avec admiral : le jeu de données d’analyse des
signes vitaux et les valeurs initiales par point temporel},
date = {2026-07-01},
url = {https://www.datanovia.com/learn/pharma-clinical/03-adam-admiral/advs-vital-signs-admiral},
langid = {fr}
}