AE Summary

library(metalite)
library(metalite.ae)

Overview

The purpose of this tutorial is to create production ready AE summary analyses by extending examples shown in the AE summary chapter of the R for Clinical Study Reports and Submission book.

The AE summary analysis provides tables to summarize adverse events information. With a metadata object created by metalite, there are three required functions to create AE summary analysis tables using metalite.ae:

There is one optional function to extend AE summary analysis:

An example output:

Example data

In metalite.ae, we created an example dataset using ADSL and ADAE datasets from the metalite package.

meta <- meta_ae_example()
meta
#> ADaM Meta Data: 
#>    .$data_population     Population data with 254 subjects 
#>    .$data_observation    Observation data with 1191 records 
#>    .$plan    Analysis plan with 16 plans 
#> 
#> 
#>   Analysis population type:
#>     name        id  group var       subset                         label
#> 1 'apat' 'USUBJID' 'TRTA'     SAFFL == 'Y' 'All Participants as Treated'
#> 
#> 
#>   Analysis observation type:
#>     name        id  group var          subset           label
#> 1 'wk12' 'USUBJID' 'TRTA'        SAFFL == 'Y' 'Weeks 0 to 12'
#> 2 'wk24' 'USUBJID' 'TRTA'     AOCC01FL == 'Y' 'Weeks 0 to 24'
#> 
#> 
#>   Analysis parameter type:
#>      name                                label
#> 1   'rel'        'drug-related adverse events'
#> 2 'aeosi' 'adverse events of special interest'
#> 3   'any'                 'any adverse events'
#> 4   'ser'             'serious adverse events'
#>                                 subset
#> 1 AEREL %in% c('POSSIBLE', 'PROBABLE')
#> 2                         AEOSI == 'Y'
#> 3                                     
#> 4                         AESER == 'Y'
#> 
#> 
#>   Analysis function:
#>            name                           label
#> 1  'ae_summary'  'Table: adverse event summary'
#> 2  'ae_listing'        'Listing: adverse event'
#> 3 'ae_specific' 'Table: specific adverse event'

The same metadata structure is used to support all analysis examples in metalite.ae. More details can be found on the metalite package website.

Analysis preparation

The prepare_ae_summary() function is used to calculate statistics required for AE summary analysis using pre-specified keywords in meta. The input of the function is a meta object created by metalite. The output of the function is an outdata object that contains a list of analysis raw datasets.

outdata <- prepare_ae_summary(
  meta,
  population = "apat",
  observation = "wk12",
  parameter = "any;rel;ser"
)
#> [1] "any"
#> [1] "rel"
#> [1] "ser"
outdata
#> List of 12
#>  $ meta           :List of 7
#>  $ population     : chr "apat"
#>  $ observation    : chr "wk12"
#>  $ parameter      : chr "any;rel;ser"
#>  $ n              :'data.frame': 5 obs. of  4 variables:
#>  $ order          : num [1:5] 1 100 200 300 400
#>  $ group          : chr [1:4] "Placebo" "Low Dose" "High Dose" "Total"
#>  $ reference_group: num 1
#>  $ prop           :'data.frame': 5 obs. of  4 variables:
#>  $ diff           :'data.frame': 5 obs. of  2 variables:
#>  $ n_pop          :'data.frame': 1 obs. of  4 variables:
#>  $ name           : chr [1:5] "Participants in population" "with one or more adverse events" "with no adverse events" "with drug-related{^a} adverse events" ...

The output dataset contains commonly used statistics. The variable is indexed by the order of outdata$group.

outdata$group
#> [1] "Placebo"   "Low Dose"  "High Dose" "Total"

The row is indexed by the order of outdata$name.

head(data.frame(outdata$order, outdata$name))
#>   outdata.order                         outdata.name
#> 1             1           Participants in population
#> 2           100      with one or more adverse events
#> 3           200               with no adverse events
#> 4           300 with drug-related{^a} adverse events
#> 5           400          with serious adverse events
outdata$n_pop
#>   n_1 n_2 n_3 n_4
#> 1  86  84  84 254
head(outdata$n)
#>    n_1 n_2 n_3 n_4
#> 1   86  84  84 254
#> 2   69  77  79 225
#> 3   17   7   5  29
#> 21  44  73  70 187
#> 22   0   1   2   3
head(outdata$prop)
#>      prop_1    prop_2    prop_3    prop_4
#> 1        NA        NA        NA        NA
#> 2  80.23256 91.666667 94.047619 88.582677
#> 3  19.76744  8.333333  5.952381 11.417323
#> 21 51.16279 86.904762 83.333333 73.622047
#> 22  0.00000  1.190476  2.380952  1.181102
head(outdata$diff)
#>        diff_2     diff_3
#> 1          NA         NA
#> 2   11.434109  13.815061
#> 21  35.741971  32.170543
#> 22   1.190476   2.380952
#> 3  -11.434109 -13.815061

Format output

After we have the raw analysis results, we can use format_ae_summary() to prepare the outdata for production ready RTF tables.

tbl <- outdata |> format_ae_summary()
tbl$tbl
#>                                    name n_1 prop_1 n_2 prop_2 n_3 prop_3 n_4
#> 1            Participants in population  86   <NA>  84   <NA>  84   <NA> 254
#> 2       with one or more adverse events  69 (80.2)  77 (91.7)  79 (94.0) 225
#> 3                with no adverse events  17 (19.8)   7  (8.3)   5  (6.0)  29
#> 21 with drug-related{^a} adverse events  44 (51.2)  73 (86.9)  70 (83.3) 187
#> 22          with serious adverse events   0  (0.0)   1  (1.2)   2  (2.4)   3
#>    prop_4
#> 1    <NA>
#> 2  (88.6)
#> 3  (11.4)
#> 21 (73.6)
#> 22  (1.2)

Additional statistics

The display argument allows us to select statistics. For example, we can add risk difference.

tbl <- outdata |> format_ae_summary(display = c("n", "prop", "diff"))
tbl$tbl
#>                                    name n_1 prop_1 n_2 prop_2 n_3 prop_3 diff_2
#> 1            Participants in population  86   <NA>  84   <NA>  84   <NA>     NA
#> 2       with one or more adverse events  69 (80.2)  77 (91.7)  79 (94.0)   11.4
#> 3                with no adverse events  17 (19.8)   7  (8.3)   5  (6.0)   35.7
#> 21 with drug-related{^a} adverse events  44 (51.2)  73 (86.9)  70 (83.3)    1.2
#> 22          with serious adverse events   0  (0.0)   1  (1.2)   2  (2.4)  -11.4
#>    diff_3
#> 1      NA
#> 2    13.8
#> 3    32.2
#> 21    2.4
#> 22  -13.8

For advanced analysis, we need extend_ae_specific_inference(). For example, we can add 95% confidence interval based on Miettinen and Nurminen (M&N) method. The details of the M&N method can be found in the rate compare vignette.

tbl <- outdata |>
  extend_ae_specific_inference() |>
  format_ae_summary(display = c("n", "prop", "diff", "diff_ci"))

tbl$tbl
#>                                    name n_1 prop_1 n_2 prop_2 n_3 prop_3 diff_2
#> 1            Participants in population  86   <NA>  84   <NA>  84   <NA>     NA
#> 2       with one or more adverse events  69 (80.2)  77 (91.7)  79 (94.0)   11.4
#> 3                with no adverse events  17 (19.8)   7  (8.3)   5  (6.0)   35.7
#> 21 with drug-related{^a} adverse events  44 (51.2)  73 (86.9)  70 (83.3)    1.2
#> 22          with serious adverse events   0  (0.0)   1  (1.2)   2  (2.4)  -11.4
#>             ci_2 diff_3          ci_3
#> 1   (-4.4,  0.0)     NA  (-4.4,  0.0)
#> 2   ( 1.0, 22.2)   13.8  ( 4.0, 24.3)
#> 3  (-22.2, -1.0)   32.2 (-24.3, -4.0)
#> 21  (22.4, 48.0)    2.4  (18.4, 44.8)
#> 22  (-3.1,  6.5)  -13.8  (-2.0,  8.3)

Mock data preparation

The mock argument allows us to create a mock table easily.

Note: The purpose of the mock argument is not to create a comprehensive mock table template, but a handy way to help user create a mock table that mimics the exact output layout.

Additional work is required to develop a flexible mock table generation tool (for example, a dedicated mock table generation package).

tbl <- outdata |> format_ae_summary(mock = TRUE)
tbl$tbl
#>                                   name n_1 prop_1 n_2 prop_2 n_3 prop_3 n_4
#> 1           Participants in population  xx   <NA>  xx   <NA>  xx   <NA> xxx
#> 2      with one or more adverse events  xx (xx.x)  xx (xx.x)  xx (xx.x) xxx
#> 3               with no adverse events  xx (xx.x)   x  (x.x)   x  (x.x)  xx
#> 4 with drug-related{^a} adverse events  xx (xx.x)  xx (xx.x)  xx (xx.x) xxx
#> 5          with serious adverse events   x  (x.x)   x  (x.x)   x  (x.x)   x
#>   prop_4
#> 1   <NA>
#> 2 (xx.x)
#> 3 (xx.x)
#> 4 (xx.x)
#> 5  (x.x)

RTF tables

The last step is to prepare the RTF table using tlf_ae_summary().

outdata |>
  format_ae_summary() |>
  tlf_ae_summary(
    source = "Source:  [CDISCpilot: adam-adsl; adae]",
    path_outtable = "rtf/ae0summary1.rtf"
  )
#> The output is saved in/rtmp/RtmpPhK17m/Rbuild1a125d24fe88/metalite.ae/vignettes/rtf/ae0summary1.rtf

The tlf_ae_summary() function also provides some commonly used argument to customize the table.

outdata |>
  format_ae_summary() |>
  tlf_ae_summary(
    source = "Source:  [CDISCpilot: adam-adsl; adae]",
    col_rel_width = c(6, rep(1, 8)),
    text_font_size = 8,
    orientation = "landscape",
    path_outtable = "rtf/ae0summary2.rtf"
  )
#> The output is saved in/rtmp/RtmpPhK17m/Rbuild1a125d24fe88/metalite.ae/vignettes/rtf/ae0summary2.rtf

The mock table can also be generated.

outdata |>
  format_ae_summary(mock = TRUE) |>
  tlf_ae_summary(
    source = "Source:  [CDISCpilot: adam-adsl; adae]",
    path_outtable = "rtf/mock_ae0summary1.rtf"
  )
#> The output is saved in/rtmp/RtmpPhK17m/Rbuild1a125d24fe88/metalite.ae/vignettes/rtf/mock_ae0summary1.rtf