Tableau des événements indésirables en R (rtables/tern)

Construisez le tableau de sécurité que porte chaque rapport d’étude clinique — les événements indésirables apparus sous traitement par classe de système d’organes et terme préféré, répartis par bras de traitement — avec le moteur de mise en page rtables et tern sur des données ADAE pharmaverse publiques

Un tutoriel complet et exécutable pour le tableau de synthèse des événements indésirables — le tableau de sécurité TEAE par classe de système d’organes (SOC) et terme préféré (PT). Apprenez ce qu’est le tableau (une synthèse globale des « sujets avec au moins un événement indésirable apparu sous traitement », puis des comptes SOC/PT imbriqués en n (%) de sujets, où le dénominateur est la population de sécurité), puis construisez-le avec rtables et tern : summarize_num_patients() pour les comptes de patients globaux et par SOC, count_occurrences() pour les fréquences par terme préféré, et alt_counts_df pour le dénominateur au niveau sujet — sur des données ADAE pharmaverseadam publiques, de sorte que chaque ligne s’exécute.

Date de publication

30 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Le tableau de synthèse des événements indésirables (AE) est le tableau de sécurité que porte chaque rapport d’étude clinique — les événements indésirables apparus sous traitement (TEAE) résumés par classe de système d’organes (SOC) et terme préféré (PT) imbriqué, répartis par bras de traitement.
  • Comptez les sujets, pas les événements. Les nombres sont des n (%) de sujets ayant présenté au moins un tel événement — un sujet qui rapporte un prurit à trois reprises est compté une seule fois. Compter les lignes brutes revient à compter les événements, l’erreur classique du tableau des AE.
  • Le dénominateur est la population de sécurité. Chaque pourcentage est rapporté au N du bras de la colonne (l’en-tête (N=xx)), tiré de l’ADSL au niveau sujet — fourni à rtables via alt_counts_df, jamais à partir des enregistrements d’AE eux-mêmes.
  • Deux fonctions tern le construisent. summarize_num_patients() donne les lignes « sujets avec ≥ 1 AE » globales et par SOC ; count_occurrences() donne les fréquences par terme préféré.
  • Le tableau publié est trié par fréquence et soumis à un seuil. Le tableau complet atteint des centaines de termes préférés ; un tableau de soumission trie les SOC et les PT par fréquence décroissante et conserve les AE survenant chez ≥ un pourcentage choisi de n’importe quel bras.

Introduction

Après le tableau démographique, la sortie suivante que consulte un évaluateur est le tableau de synthèse des événements indésirables — le cœur de la section sécurité d’un rapport d’étude clinique (CSR). Il répond à la question à laquelle existe toute lecture de sécurité : quels événements indésirables sont survenus, à quelle fréquence, et y en a-t-il eu davantage sur les bras actifs que sous placebo ? Les événements sont regroupés par classe de système d’organes (SOC), listés par terme préféré (PT) au sein de chaque classe, et comptés comme le nombre et le pourcentage de sujets atteints dans chaque bras de traitement.

C’est un tableau dans le flux de travail TLF (tableaux, listings et figures) pharma, et il est construit à partir d’un seul jeu de données d’analyse : ADAE, le jeu de données d’analyse des événements indésirables (une ligne par enregistrement d’événement indésirable). Cette leçon construit le tableau à la manière de l’industrie — avec le moteur de mise en page rtables et son compagnon clinique tern — sur des données pharmaverse publiques, de sorte que chaque ligne s’exécute telle qu’elle est écrite. Elle suit la vignette canonique du tableau des événements indésirables de tern, ré-écrite en une construction guidée unique.

Voici où nous allons — les classes de système d’organes les plus fréquentes par bras, calculées directement à partir de l’ADAE que nous résumons ci-dessous :

Grouped horizontal bar chart of the eight most frequent system organ classes for treatment-emergent adverse events in the CDISC pilot safety population. For each system organ class, three bars give the number of subjects affected on Placebo (azure), Xanomeline Low Dose (orange), and Xanomeline High Dose (green). General disorders and administration-site conditions and skin and subcutaneous tissue disorders dominate, and both active Xanomeline arms have far more subjects affected in those two skin-related classes than placebo, the expected safety signal for a transdermal patch.

Les deux classes liées à la peau — general disorders and administration-site conditions et skin and subcutaneous tissue disorders — dominent, et les deux bras actifs se situent bien au-dessus du placebo sur ce point : la réaction attendue à un patch transdermique. À la fin de cette leçon, vous aurez produit ce signal sous forme de tableau SOC/PT formel. Si « ADAE », « apparu sous traitement » ou « SOC/PT » vous sont nouveaux, construisez d’abord le jeu de données dans Créer l’ADAE en R avec admiral, qui dérive l’ADAE que ce tableau lit.

Ce qu’est un tableau de synthèse des événements indésirables

Un tableau de synthèse des AE est un tableau de comptage doté d’une anatomie fixe à deux niveaux : un bloc supérieur résumant les AE globalement, puis un bloc par classe de système d’organes (SOC) — un regroupement MedDRA (Medical Dictionary for Regulatory Activities) de systèmes corporels apparentés — avec les termes préférés (PT) à l’intérieur. La forme est conventionnelle, de sorte que chacun de ceux que vous construisez se lit de la même façon :

Partie Ce que c’est Dans cette leçon
Synthèse globale Sujets avec ≥ 1 TEAE, et le nombre total d’événements, par bras Les deux premières lignes
Groupes de lignes Un bloc par classe de système d’organes Skin disorders, GI disorders, …
Lignes au sein d’un groupe Une ligne par terme préféré Application-site pruritus, diarrhoea, …
Colonnes Les bras de traitement, plus un Total Placebo · Xanomeline Low · Xanomeline High · All Patients
Statistique de cellule n (%) de sujets atteints dans ce bras 21 (29.2%)
En-tête de colonne (N=) La taille de la population de sécurité du bras Le dénominateur de chaque pourcentage

Deux conventions décident si le tableau est correct :

  • Vous comptez les sujets, pas les événements. Un « événement indésirable apparu sous traitement (TEAE) » est un événement survenu à la première dose ou après — les événements que comptent les tableaux de sécurité. Mais le tableau rapporte combien de sujets ont présenté au moins un tel événement, non combien d’événements sont survenus. Un sujet qui rapporte un prurit au site d’application sur trois jours distincts est un seul sujet dans le compte de ce terme préféré. Se tromper ici — compter les enregistrements d’AE — gonfle chaque nombre et constitue l’erreur la plus courante du tableau des AE.
  • Le dénominateur du pourcentage est la population de sécurité, pas le nombre d’enregistrements d’AE. Un 29.2 % pour « application-site pruritus » sur le bras à forte dose signifie 29.2 % de la population de sécurité à forte dose, tiré du jeu de données au niveau sujet — jamais un pourcentage des lignes d’AE. C’est pourquoi le dénominateur doit être fourni séparément, comme vous le verrez.

Les données

Nous utilisons l’ADAE (jeu de données d’analyse des événements indésirables) et l’ADSL (jeu de données d’analyse au niveau sujet) fournis dans pharmaverseadam — l’étude pilote CDISC (Clinical Data Interchange Standards Consortium) synthétique et libre de droits comparant un placebo à deux doses de Xanomeline, administré sous forme de patch transdermique. L’ADAE porte déjà le AEBODSYS codé (classe de système d’organes) et le AEDECOD (terme préféré), le bras de traitement TRT01A, et l’indicateur d’apparition sous traitement TRTEMFL, de sorte qu’aucune dérivation n’est nécessaire ici — ce travail est réalisé dans la leçon ADAE.

Deux filtres mettent en place l’analyse. Nous conservons les événements apparus sous traitement (TRTEMFL == "Y") pour les enregistrements d’AE, et nous résumons la population de sécurité (SAFFL == "Y", l’indicateur de population de sécurité) — la population standard pour un tableau de sécurité, qui écarte les sujets en échec de sélection n’ayant jamais reçu de dose.

library(dplyr, warn.conflicts = FALSE)
library(pharmaverseadam)

arm_levels <- c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")

# ADSL — one row per subject; the safety population is the denominator source
adsl <- pharmaverseadam::adsl %>%
  filter(SAFFL == "Y") %>%
  mutate(TRT01A = factor(TRT01A, levels = arm_levels))

# ADAE — one row per AE record; keep the treatment-emergent events for the safety population
adae <- pharmaverseadam::adae %>%
  filter(TRTEMFL == "Y", SAFFL == "Y") %>%
  mutate(TRT01A = factor(TRT01A, levels = arm_levels))

adae %>%
  select(USUBJID, TRT01A, AEBODSYS, AEDECOD, TRTEMFL) %>%
  head(5)
# A tibble: 5 × 5
  USUBJID     TRT01A  AEBODSYS                                   AEDECOD TRTEMFL
  <chr>       <fct>   <chr>                                      <chr>   <chr>  
1 01-701-1015 Placebo GENERAL DISORDERS AND ADMINISTRATION SITE… APPLIC… Y      
2 01-701-1015 Placebo GENERAL DISORDERS AND ADMINISTRATION SITE… APPLIC… Y      
3 01-701-1015 Placebo GASTROINTESTINAL DISORDERS                 DIARRH… Y      
4 01-701-1023 Placebo SKIN AND SUBCUTANEOUS TISSUE DISORDERS     ERYTHE… Y      
5 01-701-1023 Placebo SKIN AND SUBCUTANEOUS TISSUE DISORDERS     ERYTHE… Y      

Faire de TRT01A un facteur avec le placebo en premier fixe l’ordre des colonnes du tableau (placebo, puis dose croissante) au lieu de le laisser tomber dans l’ordre alphabétique. Après les filtres, adsl contient les 254 sujets dosés (86 sous placebo, 96 à faible dose, 72 à forte dose) et adae contient leurs 1 122 enregistrements d’AE apparus sous traitement. Ces deux objets — les événements et le dénominateur au niveau sujet — sont tout ce dont le tableau a besoin.

Construire la synthèse globale des AE

Commencez par le haut du tableau : combien de sujets de chaque bras ont présenté au moins un AE apparu sous traitement, et combien d’événements y a-t-il eu au total ? La fonction tern qui crée la mise en page pour cela est summarize_num_patients(), qui compte les sujets uniques (chaque sujet une fois) et les enregistrements non uniques (chaque événement). Vous lisez la mise en page de haut en bas comme une recette ; rien n’est calculé tant que build_table() ne l’applique pas aux données.

library(rtables)
library(tern)

basic_table() %>%
  split_cols_by("TRT01A") %>%                       # one column per arm
  add_overall_col("All Patients") %>%               # the Total column
  add_colcounts() %>%                               # the (N=xx) header row
  summarize_num_patients(
    var    = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique    = "Patients with at least one AE",
      nonunique = "Total number of events"
    )
  ) %>%
  build_table(df = adae, alt_counts_df = adsl)
                                 Placebo     Xanomeline Low Dose   Xanomeline High Dose   All Patients
                                  (N=86)           (N=96)                 (N=72)            (N=254)   
——————————————————————————————————————————————————————————————————————————————————————————————————————
Patients with at least one AE   65 (75.6%)       84 (87.5%)             68 (94.4%)        217 (85.4%) 
Total number of events             281               427                   414                1122    

L’argument clé est alt_counts_df = adsl. Le corps du tableau est construit à partir d’adae (les enregistrements d’AE), mais le N de colonne dans l’en-tête — le dénominateur du pourcentage — doit provenir des sujets, c’est pourquoi nous fournissons à build_table() l’ADSL au niveau sujet comme jeu de données de comptage alternatif. Lisez le résultat : 65 des 86 sujets sous placebo (75.6 %) ont présenté un AE apparu sous traitement, contre 87.5 % à faible dose et 94.4 % à forte dose — davantage de sujets atteints sur les bras actifs, le schéma attendu. La deuxième ligne compte les événements (1 122 au total), ce qui explique qu’elle soit plus grande que les comptes de sujets au-dessus. Sans alt_counts_df, les pourcentages seraient calculés à partir du nombre d’enregistrements d’AE et n’auraient aucun sens.

Construire le tableau par classe de système d’organes et terme préféré

Le tableau complet répète cette synthèse à l’intérieur de chaque classe de système d’organes, puis liste les fréquences par terme préféré en dessous. Deux étapes supplémentaires le construisent : split_rows_by("AEBODSYS") ouvre un groupe de lignes par SOC, et count_occurrences() compte les sujets par terme préféré (une fois par sujet, même en cas d’événements répétés).

lyt <- basic_table() %>%
  split_cols_by("TRT01A") %>%
  add_overall_col("All Patients") %>%
  add_colcounts() %>%
  # top block: overall subjects-with-an-AE summary
  summarize_num_patients(
    var = "USUBJID", .stats = c("unique", "nonunique"),
    .labels = c(unique = "Patients with at least one AE",
                nonunique = "Total number of events")
  ) %>%
  # one row group per system organ class
  split_rows_by(
    "AEBODSYS", child_labels = "visible", nested = FALSE,
    indent_mod = -1L, split_fun = drop_split_levels
  ) %>%
  # per-SOC subjects-with-an-AE-in-this-class summary
  summarize_num_patients(
    var = "USUBJID", .stats = "unique",
    .labels = c(unique = "Patients with at least one AE in this class")
  ) %>%
  # preferred-term frequencies within each class
  count_occurrences(vars = "AEDECOD")

tbl <- build_table(lyt, df = adae, alt_counts_df = adsl)

nrow(tbl)
[1] 278

C’est un tableau des AE complet et correct — mais il compte 278 lignes, car l’étude pilote a enregistré un terme préféré pour presque chaque type d’événement. Une soumission n’en imprime jamais la totalité : elle trie par fréquence et applique un seuil de reporting. Nous traitons cela ensuite. Le point important ici est que lyt est une mise en page réutilisable — une description du tableau, pas un résultat — de sorte que le même objet peut être ré-appliqué à n’importe quel jeu de données (un sous-groupe, une autre étude) sans rien re-spécifier.

En faire le tableau publié : trier par fréquence et appliquer un seuil

Un vrai tableau de sécurité est trié par fréquence décroissante (les SOC et PT les plus fréquents en premier, pas dans l’ordre alphabétique) et limité aux AE survenant assez souvent pour compter — par convention ceux présents chez au moins un pourcentage choisi de sujets dans n’importe quel bras (5 % est un seuil courant). Sélectionnez d’abord les termes préférés qui franchissent le seuil :

# subjects per arm (the denominators)
arm_n <- table(adsl$TRT01A)

# keep preferred terms reported by >=5% of subjects in ANY arm
pt_keep <- adae %>%
  distinct(USUBJID, TRT01A, AEDECOD) %>%               # subject-level: one row per subject x PT
  count(TRT01A, AEDECOD, name = "n_subj") %>%
  mutate(pct = 100 * n_subj / as.numeric(arm_n[as.character(TRT01A)])) %>%
  group_by(AEDECOD) %>%
  summarise(max_pct = max(pct), .groups = "drop") %>%
  filter(max_pct >= 5) %>%
  pull(AEDECOD)

length(pt_keep)
[1] 24

Reconstruisez ensuite le tableau sur ces termes préférés et triez-le. prune_table() supprime les lignes vides, sort_at_path() ordonne les classes de système d’organes par nombre total de sujets (cont_n_allcols) et les termes préférés au sein de chaque classe par fréquence (score_occurrences) :

adae_common <- adae %>%
  filter(AEDECOD %in% pt_keep) %>%
  mutate(AEBODSYS = as.factor(AEBODSYS), AEDECOD = as.factor(AEDECOD))

basic_table(
    title    = "Table 14-3.01",
    subtitles = "Treatment-Emergent Adverse Events in >=5% of Any Arm (Safety Population)"
  ) %>%
  split_cols_by("TRT01A") %>%
  add_overall_col("All Patients") %>%
  add_colcounts() %>%
  split_rows_by(
    "AEBODSYS", child_labels = "visible", nested = FALSE,
    indent_mod = -1L, split_fun = drop_split_levels
  ) %>%
  summarize_num_patients(
    var = "USUBJID", .stats = "unique",
    .labels = c(unique = "Patients with at least one AE in this class")
  ) %>%
  count_occurrences(vars = "AEDECOD") %>%
  build_table(df = adae_common, alt_counts_df = adsl) %>%
  prune_table() %>%
  sort_at_path(path = "AEBODSYS", scorefun = cont_n_allcols) %>%
  sort_at_path(path = c("AEBODSYS", "*", "AEDECOD"), scorefun = score_occurrences)
Table 14-3.01
Treatment-Emergent Adverse Events in >=5% of Any Arm (Safety Population)

—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
                                                        Placebo     Xanomeline Low Dose   Xanomeline High Dose   All Patients
                                                         (N=86)           (N=96)                 (N=72)            (N=254)   
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
SKIN AND SUBCUTANEOUS TISSUE DISORDERS                                                                                       
Patients with at least one AE in this class            19 (22.1%)       36 (37.5%)             35 (48.6%)         90 (35.4%) 
  PRURITUS                                              8 (9.3%)        21 (21.9%)             25 (34.7%)         54 (21.3%) 
  ERYTHEMA                                              8 (9.3%)        14 (14.6%)             14 (19.4%)         36 (14.2%) 
  RASH                                                  5 (5.8%)        13 (13.5%)             8 (11.1%)          26 (10.2%) 
  HYPERHIDROSIS                                         2 (2.3%)         4 (4.2%)              8 (11.1%)          14 (5.5%)  
  SKIN IRRITATION                                       3 (3.5%)         6 (6.2%)               5 (6.9%)          14 (5.5%)  
  BLISTER                                                  0             5 (5.2%)               1 (1.4%)           6 (2.4%)  
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS                                                                         
Patients with at least one AE in this class            16 (18.6%)       39 (40.6%)             31 (43.1%)         86 (33.9%) 
  APPLICATION SITE PRURITUS                             6 (7.0%)        23 (24.0%)             21 (29.2%)         50 (19.7%) 
  APPLICATION SITE ERYTHEMA                             3 (3.5%)        13 (13.5%)             14 (19.4%)         30 (11.8%) 
  APPLICATION SITE DERMATITIS                           5 (5.8%)         9 (9.4%)               7 (9.7%)          21 (8.3%)  
  APPLICATION SITE IRRITATION                           3 (3.5%)         9 (9.4%)              9 (12.5%)          21 (8.3%)  
  APPLICATION SITE VESICLES                             1 (1.2%)         5 (5.2%)               5 (6.9%)          11 (4.3%)  
  FATIGUE                                               1 (1.2%)         5 (5.2%)               5 (6.9%)          11 (4.3%)  
GASTROINTESTINAL DISORDERS                                                                                                   
Patients with at least one AE in this class            12 (14.0%)       10 (10.4%)             16 (22.2%)         38 (15.0%) 
  DIARRHOEA                                            9 (10.5%)         5 (5.2%)               3 (4.2%)          17 (6.7%)  
  VOMITING                                              3 (3.5%)         4 (4.2%)               6 (8.3%)          13 (5.1%)  
  NAUSEA                                                3 (3.5%)         3 (3.1%)               6 (8.3%)          12 (4.7%)  
  SALIVARY HYPERSECRETION                                  0                 0                  4 (5.6%)           4 (1.6%)  
NERVOUS SYSTEM DISORDERS                                                                                                     
Patients with at least one AE in this class             4 (4.7%)        14 (14.6%)             15 (20.8%)         33 (13.0%) 
  DIZZINESS                                             2 (2.3%)         9 (9.4%)              10 (13.9%)         21 (8.3%)  
  HEADACHE                                              3 (3.5%)         3 (3.1%)               5 (6.9%)          11 (4.3%)  
  SYNCOPE                                                  0             5 (5.2%)               2 (2.8%)           7 (2.8%)  
CARDIAC DISORDERS                                                                                                            
Patients with at least one AE in this class             6 (7.0%)         8 (8.3%)              10 (13.9%)         24 (9.4%)  
  SINUS BRADYCARDIA                                     2 (2.3%)         7 (7.3%)              8 (11.1%)          17 (6.7%)  
  MYOCARDIAL INFARCTION                                 4 (4.7%)         2 (2.1%)               4 (5.6%)          10 (3.9%)  
INFECTIONS AND INFESTATIONS                                                                                                  
Patients with at least one AE in this class             8 (9.3%)         5 (5.2%)              9 (12.5%)          22 (8.7%)  
  NASOPHARYNGITIS                                       2 (2.3%)         4 (4.2%)               6 (8.3%)          12 (4.7%)  
  UPPER RESPIRATORY TRACT INFECTION                     6 (7.0%)         1 (1.0%)               3 (4.2%)          10 (3.9%)  
RESPIRATORY, THORACIC AND MEDIASTINAL DISORDERS                                                                              
Patients with at least one AE in this class             1 (1.2%)         5 (5.2%)               5 (6.9%)          11 (4.3%)  
  COUGH                                                 1 (1.2%)         5 (5.2%)               5 (6.9%)          11 (4.3%)  

Le tableau se lit désormais comme une sortie de sécurité publiée. Les deux classes liées à la peau arrivent en tête, exactement comme la figure d’introduction le promettait. Application-site pruritus est le terme préféré le plus marquant — 7.0 % sous placebo contre 24.0 % et 29.2 % sur les deux bras Xanomeline — suivi de application-site erythema et dermatitis. Le schéma est cohérent et cliniquement évident : Xanomeline est ici un patch transdermique, donc les réactions cutanées et au site d’application sont le signal dominant, bien au-dessus du placebo, tandis que les classes systémiques (gastro-intestinales, du système nerveux) sont plus uniformément réparties. Ce contraste — les bras actifs bien au-dessus du placebo sur les classes cutanées — est précisément ce qu’un évaluateur de sécurité cherche en lisant ce tableau.

Une alternative gtsummary rapide — et quand utiliser chaque outil

Pour une exploration rapide ou un manuscrit, gtsummary produit une version HTML soignée de la synthèse globale en quelques appels. Elle est excellente pour la vue de haut niveau « sujets avec un AE par bras » :

library(gtsummary)

adsl %>%
  mutate(any_teae = USUBJID %in% adae$USUBJID) %>%    # did the subject have a TEAE?
  select(TRT01A, any_teae) %>%
  tbl_summary(
    by = TRT01A,
    label = any_teae ~ "Patients with at least one TEAE",
    statistic = any_teae ~ "{n} ({p}%)"
  ) %>%
  add_overall(last = TRUE) %>%
  modify_header(label ~ "**Adverse events**") %>%
  bold_labels()
Adverse events Placebo
N = 861
Xanomeline Low Dose
N = 961
Xanomeline High Dose
N = 721
Overall
N = 2541
Patients with at least one TEAE 65 (76%) 84 (88%) 68 (94%) 217 (85%)
1 n (%)

Les nombres correspondent exactement à la synthèse tern — 65 (76 %) sous placebo, et ainsi de suite — car les deux lisent les mêmes sujets. Utilisez ceci comme repère de décision entre les deux moteurs :

gtsummary rtables + tern
Idéal pour Exploration rapide, manuscrits, la synthèse AE de haut niveau Le tableau de sécurité SOC/PT complet pour une soumission
Tableau SOC → PT imbriqué Possible, mais tern est conçu pour ça La norme, avec tri/élagage/seuils
Contrôle du dénominateur tbl_summary sur les données sujet alt_counts_df (sujets) vs df (événements)
Sortie HTML soigné / gt Monospace, paginée, prête pour RTF
Rôle dans l’industrie Universitaire + analyse précoce La norme pharma pour les TLF validés

En pratique, les équipes utilisent gtsummary pour explorer et rédiger, puis rtables/tern pour le tableau SOC/PT validé qui entre dans la soumission. Ils sont complémentaires, pas concurrents.

🟢 Avec un agent IA

Demandez à Prova « comment ajouter à mon tableau des événements indésirables tern une colonne montrant la différence de risque par rapport au placebo ? » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code exécutable que vous pouvez essayer sur les données pharmaverse d’exemple. The runtime is the judge. Demander à Prova →

Problèmes fréquents

Vos comptes sont bien trop élevés — vous avez compté les événements, pas les sujets. Un tableau des AE rapporte les sujets ayant présenté au moins un événement, mais adae compte de nombreuses lignes par sujet. summarize_num_patients() et count_occurrences() comptent tous deux les sujets uniques pour vous, ce qui est précisément la raison pour laquelle vous les utilisez au lieu de count() sur les lignes brutes. Si un nombre de « sujets avec un AE » dépasse le N du bras, c’est que vous comptez des enregistrements d’AE.

Chaque pourcentage est rapporté au mauvais total. Si vos pourcentages semblent trop petits, c’est que vous avez oublié alt_counts_df = adsl. Le corps du tableau provient des enregistrements d’AE, mais le dénominateur doit être la population de sécurité au niveau sujet — alt_counts_df est ce qui la fournit. Sans lui, tern divise par le nombre de lignes d’AE de chaque colonne et l’en-tête (N=xx) est erroné.

Le tableau est alphabétique au lieu d’être ordonné par fréquence. Par défaut, rtables liste les classes de système d’organes et les termes préférés dans l’ordre des facteurs/alphabétique. Un tableau de sécurité publié est trié par fréquence décroissante. Utilisez sort_at_path() avec cont_n_allcols (pour ordonner les SOC) et score_occurrences (pour ordonner les PT au sein de chaque SOC), comme montré ci-dessus.

Les événements pré-traitement gonflent le tableau. Si vous résumez l’ADAE brut sans filter(TRTEMFL == "Y"), les événements survenus avant la première dose (souvent des antécédents médicaux saisis comme AE) s’infiltrent dans les comptes. Un tableau TEAE compte uniquement les événements apparus sous traitement — filtrez sur TRTEMFL == "Y" d’abord, à chaque fois.

Questions fréquentes

C’est le tableau de sécurité qui résume les événements indésirables apparus sous traitement — ceux survenus à la première dose ou après — par classe de système d’organes (SOC) et terme préféré (PT), répartis par bras de traitement. Chaque cellule est le nombre et le pourcentage de sujets atteints dans ce bras (pas le nombre d’événements), le pourcentage étant rapporté à la taille de la population de sécurité du bras. C’est le tableau central de la section sécurité d’un rapport d’étude clinique, défini dans la directive ICH E3 sur la structure du CSR.

Utilisez rtables + tern : répartissez les colonnes par bras de traitement, ajoutez les effectifs de colonne, puis combinez summarize_num_patients() pour les lignes « sujets avec ≥ 1 AE » avec count_occurrences(vars = "AEDECOD") pour les fréquences par terme préféré, à l’intérieur d’un split_rows_by("AEBODSYS") pour les classes de système d’organes. Appliquez la mise en page avec build_table(df = adae, alt_counts_df = adsl), où alt_counts_df fournit le dénominateur au niveau sujet. La construction complète est détaillée ci-dessus sur des données pharmaverse publiques.

Aux sujets. La cellule 21 (29.2%) signifie que 21 sujets — 29.2 % de la population de sécurité de ce bras — ont présenté au moins un tel événement, quel que soit le nombre de fois où chacun l’a rapporté. Le dénominateur est le N de colonne dans l’en-tête, tiré de l’ADSL au niveau sujet via alt_counts_df. Compter les enregistrements d’AE au lieu des sujets (et diviser par le nombre d’enregistrements) est l’erreur la plus courante du tableau des AE.

Calculez, par terme préféré, le pourcentage de sujets atteints dans chaque bras, conservez les termes dont le maximum entre les bras est ≥ 5 %, filtrez l’ADAE sur ces termes, et reconstruisez le tableau. Le seuil est un choix d’étude (5 % dans n’importe quel bras est courant) ; l’exemple détaillé ci-dessus dérive les termes préférés éligibles avec un distinct() + count() sur les sujets, puis reconstruit et trie le tableau.

Triez le tableau construit avec rtables::sort_at_path(). Ordonnez les classes de système d’organes par nombre total de sujets avec scorefun = cont_n_allcols à path = "AEBODSYS", et ordonnez les termes préférés au sein de chaque classe avec scorefun = score_occurrences à path = c("AEBODSYS", "*", "AEDECOD"). Exécutez prune_table() d’abord pour supprimer les lignes vides. Sans cela, le tableau reste dans l’ordre des facteurs/alphabétique, ce qui n’est pas la façon dont un tableau de sécurité est présenté.

Utilisez gtsummary pour l’exploration rapide, les manuscrits et la synthèse de haut niveau « sujets avec un AE par bras » — il produit du HTML soigné en quelques appels. Utilisez rtables/tern pour le tableau de sécurité SOC/PT imbriqué complet qui entre dans une soumission réglementaire : il est conçu pour la mise en page, les dénominateurs sujets-vs-événements (alt_counts_df), le tri par fréquence et la sortie monospace, exportable en RTF, dont un CSR a besoin. De nombreuses équipes utilisent les deux.

Testez vos connaissances

En utilisant les objets adsl et adae de cette leçon, construisez une synthèse globale des événements indésirables graves apparus sous traitement par bras de traitement : le nombre et le pourcentage de sujets ayant présenté au moins un TEAE grave, plus le nombre total d’événements graves. Les AE graves sont signalés par AESER == "Y" dans les données. Conservez le dénominateur de la population de sécurité.

Les AE graves sont un sous-ensemble des enregistrements d’AE, alors filtrez adae sur AESER == "Y" et passez-le comme df à build_table() — mais conservez alt_counts_df = adsl pour que le dénominateur reste la population de sécurité complète (le pourcentage est « de tous les sujets dosés », pas « de ceux ayant un AE grave »). La mise en page est la même synthèse globale summarize_num_patients() que vous avez construite en premier.

library(dplyr)
library(rtables)
library(tern)

adae_serious <- adae %>% filter(AESER == "Y")

basic_table() %>%
  split_cols_by("TRT01A") %>%
  add_overall_col("All Patients") %>%
  add_colcounts() %>%
  summarize_num_patients(
    var = "USUBJID", .stats = c("unique", "nonunique"),
    .labels = c(unique = "Patients with at least one serious AE",
                nonunique = "Total number of serious events")
  ) %>%
  build_table(df = adae_serious, alt_counts_df = adsl)

Les AE graves sont bien plus rares que les AE en général, donc les comptes sont petits dans chaque bras. Le détail crucial est alt_counts_df = adsl : il maintient le dénominateur à la population de sécurité complète, de sorte qu’une cellule se lit « de tous les sujets dosés sur ce bras, voici combien ont eu un événement grave » — le sens dont un tableau de sécurité a besoin. Pour l’étendre à une ventilation SOC/PT, ajoutez les mêmes étapes split_rows_by("AEBODSYS") + count_occurrences("AEDECOD") sur adae_serious.

A. Le nombre d’enregistrements d’événements indésirables (chaque occurrence) dans ce bras B. Le nombre de sujets ayant présenté au moins un tel événement dans ce bras, rapporté au N de la population de sécurité du bras C. Le nombre de sujets ayant cet événement, rapporté à l’ensemble des participants de l’étude

B. Chaque cellule compte les sujets (un sujet ayant trois épisodes du même terme préféré est compté une fois), et le dénominateur du pourcentage est le N du bras de cette colonne — la population de sécurité, fournie via alt_counts_df. A est le compte d’événements (la ligne « Total number of events », et l’erreur classique si vous comptez les lignes brutes) ; C utiliserait le total général comme dénominateur au lieu du N du bras, ce que la répartition par TRT01A évite.

Conclusion

Le tableau de synthèse des événements indésirables est la lecture de sécurité d’un rapport d’étude clinique, et il se résume à deux règles : comptez les sujets, pas les événements, et divisez par la population de sécurité. Avec rtables et tern, ces règles sont intégrées à la mise en page — summarize_num_patients() pour les comptes de sujets globaux et par classe, count_occurrences() pour les fréquences par terme préféré, et alt_counts_df pour le dénominateur au niveau sujet. Ajoutez un tri par fréquence et un seuil de reporting et vous obtenez le tableau publié : des classes de système d’organes et des termes préférés ordonnés selon leur fréquence, avec le signal actif-versus-placebo — ici, les réactions cutanées du patch transdermique — se lisant directement sur la page. La mise en page est réutilisable, de sorte que le même objet produit un tableau de sous-groupe, un tableau des AE graves, ou le tableau de la prochaine étude sans re-spécification.

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

  • Créer l’ADAE en R avec admiral — dérive le jeu de données des événements indésirables (la structure de données d’occurrence, OCCDS — TRTEMFL, indicateurs d’occurrence) que ce tableau résume. · Le tableau démographique rtables — la Table 1 qui ouvre le CSR, construite avec le même moteur de mise en page rtables/tern et gtsummary. · Construire le jeu de données ADaM temps-jusqu’à-événement (ADTTE) — le jeu de données d’efficacité derrière la figure de Kaplan-Meier, la prochaine sortie de la série TLF.
  • Où cela s’inscrit : construire les ADaM avec admiral (ADSL, ADAE, ADTTE) → générer les tableaux, listings et figures (le tableau démographique → les tableaux de synthèse des AE (vous êtes ici) → la figure de Kaplan-Meier) → assembler la soumission. Le tableau des AE est la pièce maîtresse de sécurité 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 = {Tableau des événements indésirables en R (rtables/tern)},
  date = {2026-06-30},
  url = {https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/adverse-event-table-rtables},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Tableau des événements indésirables en R (rtables/tern).” 2026. June 30. https://www.datanovia.com/learn/pharma-clinical/04-tlf-generation/adverse-event-table-rtables.