AE Summary

Following the ICH E3 guidance, we need to summarize which patients were included in each efficacy analysis in Section 12.2, Adverse Events (AEs).

library(esubdemo)
## Warning in eval(ei, envir): The current R version is not the same with the
## current project in 4.1.0
library(haven) # Read SAS data
library(dplyr) # Manipulate data
library(tidyr) # Manipulate data
library(r2rtf) # Reporting in RTF format

The data used to summarize AE information is in adsl and adae datasets.

adsl <- read_sas("data-adam/adsl.sas7bdat")
adae <- read_sas("data-adam/adae.sas7bdat")

We first summarize participants in population by treatment group.

pop <- adsl %>%
  filter(SAFFL == "Y") %>%
  rename(TRTAN = TRT01AN) %>%
  count(TRTAN, name = "tot")

pop
## # A tibble: 3 × 2
##   TRTAN   tot
##   <dbl> <int>
## 1     0    86
## 2    54    84
## 3    81    84

We transform the data to simplify the analysis of each required AE criteria of interest.

  • With one or more adverse events
  • With drug-related adverse events
  • With serious adverse events
  • With serious drug-related adverse events
  • Who died"
tidy_ae <- adae %>%
  mutate(
    all = SAFFL == "Y",
    drug = AEREL %in% c("POSSIBLE", "PROBABLE"),
    ser = AESER == "Y",
    drug_ser = drug & ser,
    die = AEOUT == "FATAL"
  ) %>%
  select(USUBJID, TRTAN, all, drug, ser, drug_ser, die) %>%
  pivot_longer(cols = c(all, drug, ser, drug_ser, die))

tidy_ae %>% head(4)
## # A tibble: 4 × 4
##   USUBJID     TRTAN name     value
##   <chr>       <dbl> <chr>    <lgl>
## 1 01-701-1015     0 all      TRUE 
## 2 01-701-1015     0 drug     TRUE 
## 3 01-701-1015     0 ser      FALSE
## 4 01-701-1015     0 drug_ser FALSE

We summarize the number and percentage of patients meet each AE criteria.

ana <- tidy_ae %>%
  filter(value == TRUE) %>%
  group_by(TRTAN, name) %>%
  summarise(n = n_distinct(USUBJID)) %>%
  left_join(pop, by = "TRTAN") %>%
  mutate(
    pct = fmt_num(n / tot * 100, digits = 1),
    n = fmt_num(n, digits = 0),
    pct = paste0("(", pct, ")")
  )
## `summarise()` has grouped output by 'TRTAN'. You can override using the
## `.groups` argument.
ana %>% head(4)
## # A tibble: 4 × 5
## # Groups:   TRTAN [2]
##   TRTAN name  n        tot pct    
##   <dbl> <chr> <chr>  <int> <chr>  
## 1     0 all   "  69"    86 ( 80.2)
## 2     0 die   "   2"    86 (  2.3)
## 3     0 drug  "  44"    86 ( 51.2)
## 4    54 all   "  77"    84 ( 91.7)

We prepare reporting ready dataset for each AE criteria.

t_ae <- ana %>%
  pivot_wider(
    id_cols = "name",
    names_from = TRTAN,
    values_from = c(n, pct),
    values_fill = list(
      n = "   0",
      pct = "(  0.0)"
    )
  )

t_ae <- t_ae %>%
  mutate(name = factor(
    name,
    c("all", "drug", "ser", "drug_ser", "die"),
    c(
      "With one or more adverse events",
      "With drug-related adverse events",
      "With serious adverse events",
      "With serious drug-related adverse events",
      "Who died"
    )
  )) %>%
  arrange(name)

We prepare reporting ready dataset for analysis population.

t_pop <- pop %>%
  mutate(
    name = "Participants in population",
    tot = fmt_num(tot, digits = 0)
  ) %>%
  pivot_wider(
    id_cols = name,
    names_from = TRTAN,
    names_prefix = "n_",
    values_from = tot
  )

t_pop
## # A tibble: 1 × 4
##   name                       n_0    n_54   n_81  
##   <chr>                      <chr>  <chr>  <chr> 
## 1 Participants in population "  86" "  84" "  84"

The final report data is saved in tbl_ae_summary

tbl_ae_summary <- bind_rows(t_pop, t_ae) %>%
  select(name, ends_with("_0"), ends_with("_54"), ends_with("_81"))

tbl_ae_summary
## # A tibble: 6 × 7
##   name                                     n_0   pct_0 n_54  pct_54 n_81  pct_81
##   <chr>                                    <chr> <chr> <chr> <chr>  <chr> <chr> 
## 1 Participants in population               "  8… NA    "  8… NA     "  8… NA    
## 2 With one or more adverse events          "  6… ( 80… "  7… ( 91.… "  7… ( 94.…
## 3 With drug-related adverse events         "  4… ( 51… "  7… ( 86.… "  7… ( 83.…
## 4 With serious adverse events              "   … (  0… "   … (  1.… "   … (  2.…
## 5 With serious drug-related adverse events "   … (  0… "   … (  1.… "   … (  1.…
## 6 Who died                                 "   … (  2… "   … (  1.… "   … (  0.…

We start to define the format of the output.

tbl_ae_summary %>%
  rtf_title(
    "Analysis of Adverse Event Summary",
    "(Safety Analysis Population)"
  ) %>%
  rtf_colheader(" | Placebo | Xanomeline Low Dose| Xanomeline High Dose",
    col_rel_width = c(3.5, rep(2, 3))
  ) %>%
  rtf_colheader(" | n | (%) | n | (%) | n | (%)",
    col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),
    border_top = c("", rep("single", 6)),
    border_left = c("single", rep(c("single", ""), 3))
  ) %>%
  rtf_body(
    col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),
    text_justification = c("l", rep("c", 6)),
    border_left = c("single", rep(c("single", ""), 3))
  ) %>%
  rtf_footnote("Every subject is counted a single time for each applicable row and column.") %>%
  rtf_encode() %>%
  write_rtf("tlf/tlf_ae_summary.rtf")

In conclusion, the procedure to generate a AE summary table as shown in the above example is listed as follows:

  • Step 1: Read data into R, i.e., adae and adsl.
  • Step 2: Summarize participants in population by treatment group, and we name the dataset as t_pop.
  • Step 3: Summarize participants in population by required AE criteria of interest, and we name the dataset as t_ae.
  • Step 4: Rowly bind t_pop and t_ae and format it by r2rtf.