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
#> # A tibble: 5,955 × 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
#> # ℹ 5,951 more rows

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
#> # A tibble: 12 × 5
#> # Groups:   TRTAN [3]
#>    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)
#>  5    54 die      "   1"    84 (  1.2)
#>  6    54 drug     "  73"    84 ( 86.9)
#>  7    54 drug_ser "   1"    84 (  1.2)
#>  8    54 ser      "   1"    84 (  1.2)
#>  9    81 all      "  79"    84 ( 94.0)
#> 10    81 drug     "  70"    84 ( 83.3)
#> 11    81 drug_ser "   1"    84 (  1.2)
#> 12    81 ser      "   2"    84 (  2.4)

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       "  86" <NA>    "  84" <NA>    "  84" <NA>   
#> 2 With one or more adverse events  "  69" ( 80.2) "  77" ( 91.7) "  79" ( 94.0)
#> 3 With drug-related adverse events "  44" ( 51.2) "  73" ( 86.9) "  70" ( 83.3)
#> 4 With serious adverse events      "   0" (  0.0) "   1" (  1.2) "   2" (  2.4)
#> # ℹ 2 more rows

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: