AE Specification

library(metalite)
library(metalite.ae)

Overview

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

The AE specification analysis is to provide tables to summarize details of different types of adverse events. With a metadata object created by metalite, there are three required functions to create AE specification analysis table using metalite.ae:

There are three optional functions to extend AE specification 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_specific() function is used to calculate statistics required for AE specification 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_specific(
  meta,
  population = "apat",
  observation = "wk12",
  parameter = "rel"
)
outdata
#> List of 13
#>  $ meta           :List of 7
#>  $ population     : chr "apat"
#>  $ observation    : chr "wk12"
#>  $ parameter      : chr "rel"
#>  $ n              :'data.frame': 138 obs. of  4 variables:
#>  $ order          : num [1:138] 1 100 200 900 1000 ...
#>  $ group          : chr [1:4] "Placebo" "Low Dose" "High Dose" "Total"
#>  $ reference_group: num 1
#>  $ prop           :'data.frame': 138 obs. of  4 variables:
#>  $ diff           :'data.frame': 138 obs. of  2 variables:
#>  $ n_pop          :'data.frame': 1 obs. of  4 variables:
#>  $ name           : chr [1:138] "Participants in population" "with one or more drug-related adverse events" "with no drug-related adverse events" "" ...
#>  $ components     : chr [1:2] "soc" "par"

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 drug-related adverse events
#> 3           200          with no drug-related adverse events
#> 4           900                                             
#> 5          1000                            Cardiac disorders
#> 6          1021                          Atrial fibrillation
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    44  73  70 187
#> 3    42  11  14  67
#> 4    NA  NA  NA  NA
#> 122   6   7   4  17
#> 25    1   0   2   3
head(outdata$prop)
#>        prop_1    prop_2    prop_3    prop_4
#> 1          NA        NA        NA        NA
#> 2   51.162791 86.904762 83.333333 73.622047
#> 3   48.837209 13.095238 16.666667 26.377953
#> 4          NA        NA        NA        NA
#> 122  6.976744  8.333333  4.761905  6.692913
#> 25   1.162791  0.000000  2.380952  1.181102
head(outdata$diff)
#>         diff_2     diff_3
#> 1           NA         NA
#> 2    35.741971  32.170543
#> 3   -35.741971 -32.170543
#> 4           NA         NA
#> 122   1.356589  -2.214839
#> 25   -1.162791   1.218162

Format output

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

tbl <- outdata |> format_ae_specific()
head(tbl$tbl)
#>                                             name n_1 prop_1 n_2 prop_2 n_3
#> 1                     Participants in population  86   <NA>  84   <NA>  84
#> 2   with one or more drug-related adverse events  44 (51.2)  73 (86.9)  70
#> 3            with no drug-related adverse events  42 (48.8)  11 (13.1)  14
#> 4                                                 NA   <NA>  NA   <NA>  NA
#> 122                            Cardiac disorders   6  (7.0)   7  (8.3)   4
#> 25                           Atrial fibrillation   1  (1.2)   0  (0.0)   2
#>     prop_3 n_4 prop_4
#> 1     <NA> 254   <NA>
#> 2   (83.3) 187 (73.6)
#> 3   (16.7)  67 (26.4)
#> 4     <NA>  NA   <NA>
#> 122  (4.8)  17  (6.7)
#> 25   (2.4)   3  (1.2)

Additional statistics

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

tbl <- outdata |> format_ae_specific(display = c("n", "prop", "diff"))
head(tbl$tbl)
#>                                             name n_1 prop_1 n_2 prop_2 n_3
#> 1                     Participants in population  86   <NA>  84   <NA>  84
#> 2   with one or more drug-related adverse events  44 (51.2)  73 (86.9)  70
#> 3            with no drug-related adverse events  42 (48.8)  11 (13.1)  14
#> 4                                                 NA   <NA>  NA   <NA>  NA
#> 122                            Cardiac disorders   6  (7.0)   7  (8.3)   4
#> 25                           Atrial fibrillation   1  (1.2)   0  (0.0)   2
#>     prop_3 diff_2 diff_3
#> 1     <NA>     NA     NA
#> 2   (83.3)   35.7   32.2
#> 3   (16.7)  -35.7  -32.2
#> 4     <NA>     NA     NA
#> 122  (4.8)    1.4   -2.2
#> 25   (2.4)   -1.2    1.2

For advanced analysis, we need extend_*() functions. For example, we can use extend_ae_specific_inference() to 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_specific(display = c("n", "prop", "diff", "diff_ci"))
head(tbl$tbl)
#>                                             name n_1 prop_1 n_2 prop_2 n_3
#> 1                     Participants in population  86   <NA>  84   <NA>  84
#> 2   with one or more drug-related adverse events  44 (51.2)  73 (86.9)  70
#> 3            with no drug-related adverse events  42 (48.8)  11 (13.1)  14
#> 4                                                 NA   <NA>  NA   <NA>  NA
#> 122                            Cardiac disorders   6  (7.0)   7  (8.3)   4
#> 25                           Atrial fibrillation   1  (1.2)   0  (0.0)   2
#>     prop_3 diff_2           ci_2 diff_3           ci_3
#> 1     <NA>     NA   (-4.4,  0.0)     NA   (-4.4,  0.0)
#> 2   (83.3)   35.7   (22.4, 48.0)   32.2   (18.4, 44.8)
#> 3   (16.7)  -35.7 (-48.0, -22.4)  -32.2 (-44.8, -18.4)
#> 4     <NA>     NA           <NA>     NA           <NA>
#> 122  (4.8)    1.4   (-7.3, 10.2)   -2.2  (-10.3,  5.6)
#> 25   (2.4)   -1.2   (-6.3,  3.3)    1.2   (-4.2,  7.3)

We can use extend_ae_specific_duration() to add average duration of AE.

tbl <- outdata |>
  extend_ae_specific_duration(duration_var = "ADURN") |>
  format_ae_specific(display = c("n", "prop", "dur"))

head(tbl$tbl)
#>                                             name n_1 prop_1        dur_1 n_2
#> 1                     Participants in population  86   <NA>           NA  84
#> 2   with one or more drug-related adverse events  44 (51.2)  29.0 ( 3.5)  73
#> 3            with no drug-related adverse events  42 (48.8)           NA  11
#> 4                                                 NA   <NA>           NA  NA
#> 122                            Cardiac disorders   6  (7.0)  27.1 ( 5.9)   7
#> 25                           Atrial fibrillation   1  (1.2)   1.3 ( 0.2)   0
#>     prop_2        dur_2 n_3 prop_3        dur_3
#> 1     <NA>           NA  84   <NA>           NA
#> 2   (86.9)  27.2 ( 3.2)  70 (83.3)  30.6 ( 2.2)
#> 3   (13.1)           NA  14 (16.7)           NA
#> 4     <NA>           NA  NA   <NA>           NA
#> 122  (8.3)  16.1 ( 3.5)   4  (4.8)   1.5 ( 0.4)
#> 25   (0.0)  43.8 ( 9.8)   2  (2.4)  87.2 (22.5)

We can use extend_ae_specific_events() to add average number of AE per subject.

tbl <- outdata |>
  extend_ae_specific_events() |>
  format_ae_specific(display = c("n", "prop", "events"))

head(tbl$tbl)
#>                                             name n_1 prop_1     events_1 n_2
#> 1                     Participants in population  86   <NA>           NA  84
#> 2   with one or more drug-related adverse events  44 (51.2)   0.7 ( 0.1)  73
#> 3            with no drug-related adverse events  42 (48.8)           NA  11
#> 4                                                 NA   <NA>           NA  NA
#> 122                            Cardiac disorders   6  (7.0)   2.3 ( 0.6)   7
#> 25                           Atrial fibrillation   1  (1.2)   1.8 ( 0.6)   0
#>     prop_2     events_2 n_3 prop_3     events_3
#> 1     <NA>           NA  84   <NA>           NA
#> 2   (86.9)   1.6 ( 0.2)  70 (83.3)   1.5 ( 0.2)
#> 3   (13.1)           NA  14 (16.7)           NA
#> 4     <NA>           NA  NA   <NA>           NA
#> 122  (8.3)   1.9 ( 0.4)   4  (4.8)   1.2 ( 0.2)
#> 25   (0.0)   1.7 ( 0.3)   2  (2.4)   1.7 ( 0.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_specific(mock = TRUE)
head(tbl$tbl)
#>                                           name  n_1 prop_1  n_2 prop_2  n_3
#> 1                   Participants in population   xx   <NA>   xx   <NA>   xx
#> 2 with one or more drug-related adverse events   xx (xx.x)   xx (xx.x)   xx
#> 3          with no drug-related adverse events   xx (xx.x)   xx (xx.x)   xx
#> 4                                              <NA>   <NA> <NA>   <NA> <NA>
#> 5                            Cardiac disorders    x  (x.x)    x  (x.x)    x
#> 6                          Atrial fibrillation    x  (x.x)    x  (x.x)    x
#>   prop_3  n_4 prop_4
#> 1   <NA>  xxx   <NA>
#> 2 (xx.x)  xxx (xx.x)
#> 3 (xx.x)   xx (xx.x)
#> 4   <NA> <NA>   <NA>
#> 5  (x.x)   xx  (x.x)
#> 6  (x.x)    x  (x.x)

RTF tables

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

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

The tlf_ae_specific() function also provides some commonly used arguments to customize the table.

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

The mock table can also be generated.

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