Listings de données cliniques en R avec rlistings

Construisez l’annexe à raison d’une ligne par observation d’un rapport d’étude clinique — un listing patient des événements indésirables avec colonnes clés répétées laissées vides et longue sortie paginée — avec rlistings, puis exportez-le en RTF de qualité soumission avec r2rtf, sur des données pharmaverse publiques

Un tutoriel complet et exécutable pour le listing de données cliniques — l’annexe à raison d’une ligne par observation d’un rapport d’étude clinique qui montre chaque enregistrement de chaque sujet, sans synthèse. Découvrez ce qu’est un listing et en quoi il diffère d’un tableau récapitulatif, puis construisez un listing patient des événements indésirables avec rlistings : as_listing() avec key_cols (les colonnes répétées laissées vides lors des répétitions) et disp_cols, paginate_listing() pour la longue sortie, et enfin un RTF de qualité soumission exporté avec le pipe rtf_title / rtf_colheader / rtf_body de r2rtf — sur des données ADAE pharmaverseadam publiques, pour que chaque ligne s’exécute.

Date de publication

1 juillet 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Un listing de données cliniques est l’annexe à raison d’une ligne par observation d’un rapport d’étude clinique — il présente chaque enregistrement de chaque sujet, sans synthèse. Là où un tableau répond à « combien ? », un listing répond à « montre-moi chacun ». C’est le support traçable et auditable des tableaux récapitulatifs.
  • Les listings ne sont pas dédupliqués. Un sujet avec trois événements indésirables obtient trois lignes. C’est l’inverse du tableau des événements indésirables, qui compte chaque sujet une seule fois — et c’est précisément pourquoi les autorités réglementaires demandent les deux.
  • rlistings construit l’objet listing ; r2rtf produit le livrable. as_listing() crée un listing_df structuré avec des colonnes clés (valeurs répétées laissées vides lors des répétitions) et des colonnes d’affichage ; le pipe direct r2rtf le rend en RTF de soumission.
  • Les colonnes clés sont laissées vides lors des répétitions. key_cols = c("USUBJID", "TRT01A") affiche l’identifiant de sujet et le bras sur la première ligne d’un sujet et les laisse vides sur les autres — la signature du listing qui rend lisible une annexe longue et dense.
  • Les longs listings se paginent. paginate_listing() découpe un listing en pages de longueur fixe sans jamais couper maladroitement le bloc d’un sujet à la limite d’une page — ce qui transforme des milliers de lignes en un document de soumission paginé.

Introduction

Une autorité réglementaire demande à voir chaque événement indésirable de l’essai, sujet par sujet : non pas « combien de sujets ont eu des maux de tête », mais les lignes elles-mêmes — ce sujet, cet événement, cette sévérité, ce jour d’étude. Cette demande trouve sa réponse dans un listing de données cliniques : l’annexe à raison d’une ligne par observation qui se tient derrière chaque tableau récapitulatif d’un rapport d’étude clinique (CSR). Un listing présente les données telles qu’enregistrées, sans synthèse, de sorte qu’un évaluateur peut remonter de n’importe quel chiffre d’un tableau jusqu’aux observations individuelles qui l’ont produit.

Les listings figurent dans les annexes du CSR — la section 16 (les Annexes) de la directive ICH E3, qui définit la structure et le contenu d’un rapport d’étude ; le §16 rassemble les éléments justificatifs de l’étude et comprend les listings de données individuelles des patients (§16.2 et §16.4). C’est la sortie la moins prestigieuse et la plus volumineuse du flux de travail TLF (tableaux, listings et figures), et pour une étude de grande taille elle peut atteindre des milliers de pages. C’est précisément pourquoi ils nécessitent un outillage : un listing doit laisser vides les valeurs clés répétées pour rester lisible, se paginer proprement et s’exporter au format RTF (Rich Text Format) qu’utilisent les soumissions réglementaires.

Nous en construisons un à la manière de l’industrie — avec le package pharmaverse rlistings pour l’objet listing et r2rtf pour le RTF — sur des données pharmaverse publiques, de sorte que chaque ligne s’exécute telle quelle. L’exemple est un listing patient des événements indésirables (AE), le listing le plus courant de l’annexe de sécurité.

Voici la forme des données que nous allons disposer — combien d’enregistrements d’événements indésirables chaque sujet apporte. La plupart en ont une poignée ; quelques-uns en ont beaucoup. Un tableau récapitulatif masque cette dispersion ; un listing montre chacune de ces lignes, et c’est pourquoi il a besoin de pagination :

Histogram of the number of treatment-emergent adverse-event records per subject in the CDISC pilot safety population. The x-axis is the count of adverse-event rows a subject contributes to the listing, from 1 up to about 23; the y-axis is the number of subjects. The distribution is strongly right-skewed in brand azure: most subjects contribute only a few records, a long thin tail of subjects contribute ten or more, and a single subject reaches the low-twenties. This spread is why a listing shows every row and must be paginated, whereas a summary table would collapse each subject to a single count.

À la fin, vous aurez transformé ces enregistrements en un véritable listing patient — colonnes clés laissées vides, sortie paginée et fichier RTF écrit sur le disque — le livrable qu’un programmeur clinique remet pour la soumission. Si « ADAE », « population de sécurité » ou « apparu sous traitement » vous sont nouveaux, les leçons précédentes sur la construction des jeux de données d’analyse les couvrent ; ici, nous partons de données prêtes pour l’analyse et nous concentrons sur le listing.

Ce qu’est un listing de données cliniques

Un listing et un tableau récapitulatif sont construits à partir des mêmes données mais répondent à des questions différentes. Le tableau agrège — il compte les sujets, calcule des pourcentages, condense. Le listing énumère — il imprime chaque enregistrement, dans l’ordre, sans rien écarter. Les deux figurent dans le CSR car ils s’adressent à des lecteurs différents : le tableau pour la personne qui lit le résultat, le listing pour la personne qui doit le vérifier.

Un listing bien formé a une anatomie fixe :

Élément Ce que c’est Dans cette leçon
Lignes Une ligne par observation — pas par sujet Une ligne par enregistrement d’événement indésirable
Colonnes clés Les colonnes identifiantes, répétées le long des lignes mais laissées vides lors des répétitions USUBJID (identifiant unique du sujet), TRT01A (bras de traitement effectif)
Colonnes d’affichage Les colonnes de données affichées pour chaque enregistrement Terme AE, sévérité, indicateur de gravité, jour d’étude
Tri Un tri déterministe pour que les mêmes données se disposent toujours de la même façon Par sujet, puis par jour d’étude
Pagination Pages de longueur fixe pour un document paginé, sujets conservés intacts paginate_listing() découpe les lignes

Deux conventions font d’un listing un listing plutôt qu’un data frame imprimé :

  • Les colonnes clés sont laissées vides lors des répétitions. L’identifiant de sujet et le bras s’affichent une fois, sur la première ligne d’un sujet, et sont laissés vides sur les lignes restantes de ce sujet. Votre œil regroupe le bloc sans que l’identifiant ne crie sur chaque ligne. rlistings le fait pour toute colonne que vous nommez dans key_cols.
  • Rien n’est dédupliqué ni synthétisé. Si un sujet a rapporté le même terme préféré trois fois, les trois lignes apparaissent. C’est bien là l’intérêt — le listing est la piste d’audit. (Le tableau des événements indésirables, à l’inverse, compterait ce sujet une seule fois.)

rlistings ou rtables — lequel utiliser ?

Les deux sont des packages de mise en page pharmaverse, et ils sont complémentaires, non concurrents. Choisissez selon ce qu’est la sortie :

rlistings rtables / tern
Produit Un listing — chaque enregistrement, une ligne par observation Un tableau — effectifs, moyennes, pourcentages, agrégés
Objet central listing_df issu de as_listing() TableTree issu de basic_table() + fonctions de mise en page
Répond à « Montre-moi chaque observation » « Combien / quelle est la moyenne »
Dans le CSR Annexes de la section 16 (listings de données des patients) Tableaux récapitulatifs de la section 14
Déduplique ? Non — montre chaque ligne Oui — compte chaque sujet une fois

Une règle empirique : si l’évaluateur a besoin de remonter d’un chiffre jusqu’aux enregistrements individuels, c’est un listing (rlistings) ; s’il a besoin de la synthèse à laquelle un chiffre se résume, c’est un tableau (rtables avec tern). Les deux s’exportent en RTF de la même façon, avec r2rtf.

Les données

Nous utilisons le jeu de données ADAE — le jeu de données ADaM (Analysis Data Model) des événements indésirables, à raison d’une ligne par enregistrement d’événement indésirable — fourni dans pharmaverseadam, l’étude pilote CDISC (Clinical Data Interchange Standards Consortium) synthétique et libre de licence qui compare un placebo à deux doses de Xanomeline. Les jeux de données pharmaverse sont des tibbles, la première étape consiste donc à les convertir en data.frame simple — rlistings et r2rtf attendent des data frames de base, et passer un tibble est la source la plus fréquente d’une erreur déroutante par la suite.

Nous conservons les événements indésirables apparus sous traitement pour la population de sécurité : SAFFL == "Y" (l’indicateur de population de sécurité — tout sujet ayant pris au moins une dose) et TRTEMFL == "Y" (l’indicateur d’apparition sous traitement — événements ayant débuté à partir de la première dose). Un événement indésirable apparu sous traitement (TEAE) est l’unité d’intérêt de l’analyse de sécurité, c’est donc le filtre standard d’un listing d’AE.

library(pharmaverseadam)

# ADAE is a tibble — coerce to a plain data.frame for rlistings / r2rtf.
adae <- as.data.frame(pharmaverseadam::adae)

# Treatment-emergent AEs, safety population.
teae <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y", ]

# The columns the listing will show, ordered by subject then study day.
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "AESER", "ASTDY")
teae <- teae[order(teae$USUBJID, teae$ASTDY), cols]

nrow(teae)      # every treatment-emergent AE record becomes a listing row
[1] 1191
head(teae, 6)
      USUBJID  TRT01A                   AEDECOD    AESEV AESER ASTDY
1 01-701-1015 Placebo APPLICATION SITE ERYTHEMA     MILD     N     2
2 01-701-1015 Placebo APPLICATION SITE PRURITUS     MILD     N     2
3 01-701-1015 Placebo                 DIARRHOEA     MILD     N     8
4 01-701-1023 Placebo                  ERYTHEMA MODERATE     N     3
5 01-701-1023 Placebo                  ERYTHEMA     MILD     N     3
6 01-701-1023 Placebo                  ERYTHEMA     MILD     N     3

Le filtre conserve bien plus d’un millier d’enregistrements — une ligne par événement indésirable, exactement ce que le listing énumérera. Les six colonnes sont les deux colonnes clés (USUBJID, l’identifiant unique du sujet ; TRT01A, le bras de traitement effectif) et quatre colonnes d’affichage : le terme préféré issu du dictionnaire (AEDECOD), la sévérité (AESEV), l’indicateur de gravité (AESER) et le jour d’étude où l’événement a débuté (ASTDY). Nous avons trié les lignes par sujet puis par jour d’étude pour que le listing soit déterministe — les mêmes données se disposent toujours de la même façon, ce qu’exige une soumission.

Pour que la sortie imprimée sur cette page reste lisible, nous construisons le listing à partir des premiers sujets. Dans une soumission réelle, vous passeriez l’intégralité du data frame teae ; rien d’autre ne change.

library(pharmaverseadam)

adae <- as.data.frame(pharmaverseadam::adae)
teae <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y", ]
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "AESER", "ASTDY")
teae <- teae[order(teae$USUBJID, teae$ASTDY), cols]

# first 15 records — a legible slice for the page
lst_data <- head(teae, 15)

Construire le listing avec rlistings

as_listing() transforme un data frame en un listing_df structuré. Vous lui indiquez deux choses : quelles colonnes sont des colonnes clés (key_cols — identifiants répétés qui sont laissés vides lors des répétitions) et lesquelles sont des colonnes d’affichage (disp_cols — les données affichées pour chaque enregistrement). Tout le reste de l’apparence d’un listing — le masquage, l’alignement, l’espacement — en découle.

library(rlistings)
library(pharmaverseadam)

adae <- as.data.frame(pharmaverseadam::adae)
teae <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y", ]
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "AESER", "ASTDY")
teae <- teae[order(teae$USUBJID, teae$ASTDY), cols]
lst_data <- head(teae, 15)

lsting <- as_listing(
  lst_data,
  key_cols  = c("USUBJID", "TRT01A"),                 # blanked on repeat
  disp_cols = c("AEDECOD", "AESEV", "AESER", "ASTDY") # shown for every record
)

lsting
  USUBJID            TRT01A                        AEDECOD                   AESEV     AESER   ASTDY
————————————————————————————————————————————————————————————————————————————————————————————————————
01-701-1015         Placebo               APPLICATION SITE ERYTHEMA           MILD       N       2  
                                          APPLICATION SITE PRURITUS           MILD       N       2  
                                                  DIARRHOEA                   MILD       N       8  
01-701-1023         Placebo                        ERYTHEMA                 MODERATE     N       3  
                                                   ERYTHEMA                   MILD       N       3  
                                                   ERYTHEMA                   MILD       N       3  
                                     ATRIOVENTRICULAR BLOCK SECOND DEGREE     MILD       N      22  
01-701-1028   Xanomeline High Dose        APPLICATION SITE ERYTHEMA           MILD       N       3  
                                          APPLICATION SITE PRURITUS           MILD       N      21  
01-701-1034   Xanomeline High Dose        APPLICATION SITE PRURITUS           MILD       N      58  
                                                   FATIGUE                    MILD       N      125 
01-701-1047         Placebo                     HIATUS HERNIA               MODERATE     N       1  
                                                HIATUS HERNIA               MODERATE     N       1  
                                      UPPER RESPIRATORY TRACT INFECTION       MILD       N      23  
                                           BUNDLE BRANCH BLOCK LEFT           MILD       N      27  

Lisez le listing imprimé de haut en bas et le masquage saute aux yeux : l’identifiant et le bras d’un sujet apparaissent sur la première de ses lignes et sont vides sur les autres, de sorte que chaque sujet se lit comme un seul bloc visuel. Remarquez aussi que rien n’a été condensé — là où un sujet a le même terme préféré sur plusieurs enregistrements, les deux lignes sont présentes. C’est le listing qui fait son travail : c’est la piste d’audit ligne par ligne, pas une synthèse.

L’objet lsting est un listing_df — il se comporte toujours comme un data frame (vous pouvez en extraire des sous-ensembles), mais il porte les instructions de mise en page dont rlistings et r2rtf ont besoin pour le rendre correctement.

Paginer un long listing

Un vrai listing est bien trop long pour un seul écran ou une seule page. paginate_listing() le découpe en pages comportant un nombre fixe de lignes par page (lpp), de sorte qu’il devienne un document paginé. Il renvoie une liste — un listing_df par page — et conserve la mise en page (en-tête, masquage des colonnes clés) sur chaque page.

library(rlistings)
library(pharmaverseadam)

adae <- as.data.frame(pharmaverseadam::adae)
teae <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y", ]
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "AESER", "ASTDY")
teae <- teae[order(teae$USUBJID, teae$ASTDY), cols]
lst_data <- head(teae, 15)

lsting <- as_listing(
  lst_data,
  key_cols  = c("USUBJID", "TRT01A"),
  disp_cols = c("AEDECOD", "AESEV", "AESER", "ASTDY")
)

pages <- paginate_listing(lsting, lpp = 8)   # ~8 lines per page
--- Page 1/3 ---
  USUBJID            TRT01A                        AEDECOD                   AESEV     AESER   ASTDY
————————————————————————————————————————————————————————————————————————————————————————————————————
01-701-1015         Placebo               APPLICATION SITE ERYTHEMA           MILD       N       2  
                                          APPLICATION SITE PRURITUS           MILD       N       2  
                                                  DIARRHOEA                   MILD       N       8  
01-701-1023         Placebo                        ERYTHEMA                 MODERATE     N       3  
                                                   ERYTHEMA                   MILD       N       3  
                                                   ERYTHEMA                   MILD       N       3  

--- Page 2/3 ---
  USUBJID            TRT01A                        AEDECOD                   AESEV     AESER   ASTDY
————————————————————————————————————————————————————————————————————————————————————————————————————
01-701-1023         Placebo          ATRIOVENTRICULAR BLOCK SECOND DEGREE     MILD       N      22  
01-701-1028   Xanomeline High Dose        APPLICATION SITE ERYTHEMA           MILD       N       3  
                                          APPLICATION SITE PRURITUS           MILD       N      21  
01-701-1034   Xanomeline High Dose        APPLICATION SITE PRURITUS           MILD       N      58  
                                                   FATIGUE                    MILD       N      125 
01-701-1047         Placebo                     HIATUS HERNIA               MODERATE     N       1  

--- Page 3/3 ---
  USUBJID            TRT01A                        AEDECOD                   AESEV     AESER   ASTDY
————————————————————————————————————————————————————————————————————————————————————————————————————
01-701-1047         Placebo                     HIATUS HERNIA               MODERATE     N       1  
                                      UPPER RESPIRATORY TRACT INFECTION       MILD       N      23  
                                           BUNDLE BRANCH BLOCK LEFT           MILD       N      27  
length(pages)   # number of pages this listing produces
[1] 3
cat(toString(pages[[1]]))   # the first page, formatted
  USUBJID            TRT01A                        AEDECOD                   AESEV     AESER   ASTDY
————————————————————————————————————————————————————————————————————————————————————————————————————
01-701-1015         Placebo               APPLICATION SITE ERYTHEMA           MILD       N       2  
                                          APPLICATION SITE PRURITUS           MILD       N       2  
                                                  DIARRHOEA                   MILD       N       8  
01-701-1023         Placebo                        ERYTHEMA                 MODERATE     N       3  
                                                   ERYTHEMA                   MILD       N       3  
                                                   ERYTHEMA                   MILD       N       3  

Avec lpp = 8, notre tranche de 15 lignes devient un petit nombre de pages ; la première est montrée ci-dessus, avec le même en-tête et le même masquage que le listing complet. Dans une soumission, vous fixez lpp en fonction de la géométrie de la page (police, marges, orientation) pour que chaque page imprimée se remplisse correctement — l’étape r2rtf ci-dessous détient la mise en page finale, et paginate_listing() est le moyen de prévisualiser et de contrôler le découpage des lignes.

Exporter en RTF de soumission avec r2rtf

L’objet listing est l’artefact de revue ; le fichier RTF est le livrable réglementaire. r2rtf rend un data frame au format Rich Text Format qu’utilisent les documents de soumission, à travers un petit pipe de fonctions de couche : rtf_title() fixe le titre et le sous-titre, rtf_colheader() les intitulés de colonnes, rtf_body() les données (avec les largeurs de colonnes et la justification par colonne), puis rtf_encode() et write_rtf() produisent le fichier.

Nous rendons les données du listing (un data frame simple des lignes) directement avec le pipe r2rtf — le motif pharmaverse canonique. Les largeurs de colonnes sont données sous forme de poids relatifs (col_rel_width), et la justification est d’une lettre par colonne ("l" gauche, "c" centre). Nous écrivons dans un fichier temporaire et affichons sa taille afin que rien ne reste dans le projet.

library(r2rtf)
library(pharmaverseadam)

adae <- as.data.frame(pharmaverseadam::adae)
teae <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y", ]
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "AESER", "ASTDY")
teae <- teae[order(teae$USUBJID, teae$ASTDY), cols]
lst_data <- head(teae, 15)

out <- tempfile(fileext = ".rtf")

lst_data |>
  rtf_title(
    "Listing of Treatment-Emergent Adverse Events",
    "Safety Population"
  ) |>
  rtf_colheader(
    "Subject | Treatment | AE Term | Severity | Serious | Study Day",
    col_rel_width = c(3, 3, 4, 2, 2, 2)
  ) |>
  rtf_body(
    col_rel_width      = c(3, 3, 4, 2, 2, 2),
    text_justification = c("l", "l", "l", "c", "c", "c")
  ) |>
  rtf_encode() |>
  write_rtf(out)

file.info(out)$size   # bytes written — a real RTF the submission can open
[1] 15238

write_rtf() renvoie le chemin et écrit le fichier ; file.info()$size confirme qu’un RTF non vide a été produit. Ouvrez-le dans Word ou n’importe quel lecteur RTF et vous obtenez le listing titré, avec en-têtes de colonnes et paginé — le livrable. Les poids col_rel_width (ici 3,3,4,2,2,2) se partagent la largeur de page proportionnellement, de sorte que le terme AE obtient la colonne la plus large et les indicateurs la plus étroite ; ils doivent comporter une entrée par colonne dans rtf_colheader() comme dans rtf_body().

Note

Un détail délibéré : nous avons envoyé les données du listing (lst_data) dans le pipe r2rtf, et non l’objet listing_df. Le pipe direct sur data frame est le motif pharmaverse robuste et canonique pour la sortie RTF ; il vous donne un contrôle total sur les titres, les largeurs de colonnes et la justification. rlistings détient l’objet de revue à l’écran ; r2rtf détient le fichier de soumission. Deux outils, deux rôles.

🟢 Avec un agent IA

Demandez à Prova « comment ajouter une note de bas de page page par page et un pied de page “Page x sur y” à mon listing r2rtf ? » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code rlistings et r2rtf exécutable que vous pouvez essayer sur les données d’exemple pharmaverse. The runtime is the judge. Demander à Prova →

Problèmes fréquents

Vous avez passé un tibble et obtenu une erreur cryptique. Les jeux de données pharmaverse (pharmaverseadam::adae) sont des tibbles, et rlistings comme r2rtf attendent un data.frame simple. Un tibble peut faire échouer la mise en page ou l’encodeur RTF avec une erreur qui ne mentionne pas du tout les tibbles. Convertissez d’abord : adae <- as.data.frame(pharmaverseadam::adae). C’est le bug de listing le plus fréquent.

formatters::export_as_rtf() échoue sur votre listing. Il est tentant d’exporter l’objet listing_df avec formatters::export_as_rtf(), mais ce pont vers r2rtf est fragile et peut échouer à l’intérieur de rtf_strwidth. Utilisez plutôt le pipe r2rtf direct sur les données du listing (lst_data |> rtf_title() |> rtf_colheader() |> rtf_body() |> rtf_encode() |> write_rtf()), comme ci-dessus — c’est le motif pharmaverse canonique et il vous donne un contrôle explicite de la mise en page.

Les colonnes clés ne se vident pas. Le masquage lors des répétitions ne se produit que pour les colonnes nommées dans key_cols, et seulement lorsque des valeurs égales sont adjacentes. Si l’identifiant de sujet continue de s’imprimer sur chaque ligne, soit vous ne l’avez pas listé dans key_cols, soit les données ne sont pas triées — as_listing() ne vide une valeur répétée que lorsque la valeur identique se situe juste au-dessus. Triez d’abord selon vos colonnes clés (teae[order(teae$USUBJID), ]).

col_rel_width a la mauvaise longueur. rtf_colheader() et rtf_body() ont chacun besoin d’une largeur par colonne, et text_justification d’une lettre par colonne. Un vecteur trop court ou trop long lève une erreur ou désaligne silencieusement le RTF. Comptez vos colonnes d’affichage (plus les colonnes clés que vous imprimez) et faites correspondre exactement la longueur du vecteur — ici six colonnes, donc col_rel_width = c(3, 3, 4, 2, 2, 2).

Questions fréquentes

Un listing de données cliniques est l’annexe à raison d’une ligne par observation d’un rapport d’étude clinique — il imprime chaque enregistrement de chaque sujet, sans synthèse, de sorte qu’un évaluateur peut remonter de n’importe quel chiffre d’un tableau récapitulatif jusqu’aux observations individuelles qui le sous-tendent. Les listings figurent dans la section 16 (les Annexes) de la directive ICH E3, en tant que listings de données individuelles des patients (§16.2 et §16.4). Contrairement à un tableau, un listing n’est pas dédupliqué : un sujet avec trois événements indésirables apparaît sur trois lignes.

Utilisez rlistings pour un listing (chaque enregistrement, une ligne par observation — la piste d’audit) et rtables (généralement avec tern) pour un tableau récapitulatif (effectifs, moyennes, pourcentages — agrégés). Règle empirique : si l’évaluateur a besoin de remonter d’un chiffre jusqu’aux enregistrements individuels, c’est un listing ; s’il a besoin de la synthèse à laquelle les enregistrements se résument, c’est un tableau. Les deux s’exportent en RTF avec r2rtf.

Utilisez le package r2rtf. Enchaînez votre data frame à travers les fonctions de couche : df |> rtf_title("Title", "Subtitle") |> rtf_colheader("Col A | Col B", col_rel_width = c(3, 2)) |> rtf_body(col_rel_width = c(3, 2)) |> rtf_encode() |> write_rtf("out.rtf"). rtf_encode() construit la chaîne RTF et write_rtf() écrit le fichier — le format Rich Text Format qu’utilisent les soumissions réglementaires, ouvrable dans Word.

C’est le masquage des colonnes clés, la signature du listing. Les colonnes nommées dans key_cols affichent leur valeur sur la première d’un groupe de lignes adjacentes identiques et sont laissées vides sur les autres, de sorte que chaque sujet se lit comme un seul bloc visuel au lieu de répéter l’identifiant sur chaque ligne. Cela ne fonctionne que lorsque les données sont triées selon ces colonnes clés, pour que les valeurs identiques soient adjacentes.

Utilisez paginate_listing() de rlistings : paginate_listing(lsting, lpp = 40) découpe le listing en pages d’environ lpp lignes chacune et renvoie une liste d’un listing_df par page, en répétant l’en-tête et le masquage des colonnes clés sur chaque page. Fixez lpp en fonction de la géométrie de votre page pour que chaque page imprimée se remplisse correctement ; la mise en page finale est détenue par l’étape d’export r2rtf.

Testez vos connaissances

À partir des données d’événements indésirables apparus sous traitement de cette leçon, construisez un listing des seuls événements indésirables graves (AESER == "Y"), en conservant les mêmes colonnes clés (USUBJID, TRT01A) et colonnes d’affichage (AEDECOD, AESEV, ASTDY), puis exportez-le en RTF avec un titre indiquant « Listing of Serious Treatment-Emergent Adverse Events ». Combien de lignes le listing des AE graves compte-t-il par rapport au listing complet, et pourquoi est-ce le listing que les autorités réglementaires lisent en premier ?

Ajoutez une condition de plus au sous-ensemble base-R : teae[teae$AESER == "Y", ]. Tout le reste — l’appel as_listing() et le pipe r2rtf — est inchangé ; vous ne faites que filtrer les lignes avant de les disposer. Pensez à convertir d’abord le tibble avec as.data.frame(), et faites correspondre la longueur de col_rel_width à votre nombre de colonnes (cinq ici : deux clés + trois d’affichage).

library(rlistings)
library(r2rtf)
library(pharmaverseadam)

adae <- as.data.frame(pharmaverseadam::adae)

# treatment-emergent AND serious, safety population
ser <- adae[adae$SAFFL == "Y" & adae$TRTEMFL == "Y" & adae$AESER == "Y", ]
cols <- c("USUBJID", "TRT01A", "AEDECOD", "AESEV", "ASTDY")
ser <- ser[order(ser$USUBJID, ser$ASTDY), cols]

nrow(ser)   # far fewer rows than the full TEAE listing

# the listing object (key-column blanking, display columns)
lsting <- as_listing(
  ser,
  key_cols  = c("USUBJID", "TRT01A"),
  disp_cols = c("AEDECOD", "AESEV", "ASTDY")
)
lsting

# export to a submission RTF
out <- tempfile(fileext = ".rtf")
ser |>
  rtf_title("Listing of Serious Treatment-Emergent Adverse Events", "Safety Population") |>
  rtf_colheader("Subject | Treatment | AE Term | Severity | Study Day",
                col_rel_width = c(3, 3, 4, 2, 2)) |>
  rtf_body(col_rel_width = c(3, 3, 4, 2, 2),
           text_justification = c("l", "l", "l", "c", "c")) |>
  rtf_encode() |>
  write_rtf(out)
file.info(out)$size

Le listing des AE graves a bien moins de lignes que le listing complet des événements apparus sous traitement — les événements graves sont, heureusement, la minorité. C’est le listing que les évaluateurs de sécurité lisent en premier, car un événement indésirable grave (qui met en jeu le pronostic vital, entraîne une hospitalisation ou est par ailleurs médicalement important) détermine l’évaluation du bénéfice-risque de l’essai ; les autorités réglementaires veulent voir chacun d’eux, sujet par sujet, et pas seulement un décompte.

A. Un listing compte chaque sujet une fois ; un tableau montre chaque enregistrement B. Un listing montre chaque enregistrement (une ligne par observation) ; un tableau agrège et compte chaque sujet une fois C. Ce sont la même sortie dans deux formats de fichier

B. Un listing énumère — une ligne par observation, rien de dédupliqué, de sorte qu’un sujet avec trois événements indésirables a trois lignes (rlistings). Un tableau agrège — il compte chaque sujet une fois et rapporte n et pourcentage (rtables/tern). A inverse les deux ; C est faux car ils répondent à des questions différentes (remonter à l’enregistrement vs synthétiser le résultat), ce qui est la raison pour laquelle le CSR comporte les deux.

Conclusion

Un listing de données cliniques est l’épine dorsale à raison d’une ligne par observation d’un rapport d’étude clinique — l’annexe qui montre chaque enregistrement pour qu’un évaluateur puisse vérifier chaque tableau. En construire un se résume à deux outils avec deux rôles : rlistings crée l’objet listing avec as_listing() — colonnes clés laissées vides lors des répétitions, colonnes d’affichage montrées, et paginate_listing() pour la longueur — et r2rtf rend le fichier de soumission via le pipe rtf_title() |> rtf_colheader() |> rtf_body() |> rtf_encode() |> write_rtf(). Réussissez trois choses — convertir le tibble en data.frame, nommer les bonnes key_cols sur des données triées pour que le masquage fonctionne, et faire correspondre la longueur de col_rel_width à vos colonnes — et vous aurez produit le listing auditable, paginé et prêt pour la soumission qui se tient derrière chaque tableau récapitulatif du rapport.

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

  • Le tableau des événements indésirables — la contrepartie récapitulative de ce listing : les mêmes données ADAE agrégées en n (%) de sujets par classe de système d’organes et terme préféré avec rtables/tern, là où un listing énumère chaque enregistrement. · Le tableau démographique rtables — la Table 1, la synthèse initiale qui ouvre le rapport, construit avec le même moteur de mise en page. · Le tableau d’efficacité (ANCOVA) — le résultat du critère principal, l’autre moitié des tableaux de la section 14 qu’un listing étaye.
  • Où cela s’inscrit : construire l’ADaM avec admiral → générer les tableaux, listings et figures (les tableaux récapitulatifs → les listings de données des patients (vous êtes ici) → les figures) → constituer le dossier de soumission. Les listings sont l’annexe traçable derrière chaque tableau récapitulatif de cette séquence.
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 = {Listings de données cliniques en R avec rlistings},
  date = {2026-07-01},
  url = {https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/patient-listings-rlistings},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Listings de données cliniques en R avec rlistings.” 2026. July 1. https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/patient-listings-rlistings.