7  AE summary

Following ICH E3 guidance, we summarize number of participants that were included in each safety analysis in Section 12.2, Adverse Events (AEs).

library(haven) # Read SAS data
library(dplyr) # Manipulate data
library(tidyr) # Manipulate data
library(r2rtf) # Reporting in RTF format

In this chapter, we illustrate how to summarize AEs information for a study.

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 arm.

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.

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 participants who meet each AE criteria.

fmt_num <- function(x, digits, width = digits + 4) {
  formatC(
    x,
    digits = digits,
    format = "f",
    width = width
  )
}
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, ")")
  )

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 group.

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 the 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 define the format of the output using code below:

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")

The procedure to generate an AE summary table can be summarized as follows: