Suppose you need to create the table below, and need an ARD representation of the results to get started. Here, we will review an examples for creating a basic demographics table.

To get started, load the {cards} package.

Demographics

Characteristic Placebo
N = 86
Xanomeline Low Dose
N = 84
Xanomeline High Dose
N = 84
Age


    Median (Q1, Q3) 76 (69, 82) 78 (71, 82) 76 (71, 80)
    Mean (SD) 75 (9) 76 (8) 74 (8)
    Min - Max 52 - 89 51 - 88 56 - 88
Age Group, n (%)


    <65 14 (16.3%) 8 ( 9.5%) 11 (13.1%)
    65-80 42 (48.8%) 47 (56.0%) 55 (65.5%)
    >80 30 (34.9%) 29 (34.5%) 18 (21.4%)
Female, n (%) 53 (61.6%) 50 (59.5%) 40 (47.6%)

The table above has three types of data summaries: a continuous variable summary for AGE, a categorical variable summary for AGEGR1, and a dichotomous variable summary for SEX.

Continuous Summaries

To get a continuous variable summary, we will use the ard_summary() function from the {cards} package.

df_continuous_ard <-
  ard_summary(
    ADSL,
    by = ARM,
    variables = AGE,
    statistic = ~ continuous_summary_fns(c(
      "median",
      "p25",
      "p75",
      "mean",
      "sd",
      "min",
      "max"
    ))
  )
df_continuous_ard |> head(5)
#> # An ARD data frame: 5 × 10
#>   group1 group1_level variable context stat_name stat_label   stat fmt_fun
#>   <chr>  <list>       <chr>    <chr>   <chr>     <chr>      <list>  <list>
#> 1 ARM    Placebo      AGE      summary median    Median      76          1
#> 2 ARM    Placebo      AGE      summary p25       Q1          69          1
#> 3 ARM    Placebo      AGE      summary p75       Q3          82          1
#> 4 ARM    Placebo      AGE      summary mean      Mean        75.2        1
#> 5 ARM    Placebo      AGE      summary sd        SD           8.59       1
#> # ℹ 2 more variables: warning <list>, error <list>

Categorical Summaries

To get the categorical variable summary, we will use the ard_tabulate() function.

df_categorical_ard <-
  ard_tabulate(
    ADSL,
    by = ARM,
    variables = AGEGR1
  )
df_categorical_ard |> head(5)
#> # An ARD data frame: 5 × 11
#>   group1 group1_level variable variable_level context  stat_name   stat
#>   <chr>  <list>       <chr>    <list>         <chr>    <chr>     <list>
#> 1 ARM    Placebo      AGEGR1   65-80          tabulate n         42    
#> 2 ARM    Placebo      AGEGR1   65-80          tabulate N         86    
#> 3 ARM    Placebo      AGEGR1   65-80          tabulate p          0.488
#> 4 ARM    Placebo      AGEGR1   <65            tabulate n         14    
#> 5 ARM    Placebo      AGEGR1   <65            tabulate N         86    
#> # ℹ 4 more variables: stat_label <chr>, fmt_fun <list>, warning <list>,
#> #   error <list>

Dichotomous Summaries

To get the dichotomous variable summary, we will use ard_tabulate_value(). In this case, we want to show the Female ("F") level of the SEX variable and specify this with the values argument.

df_dichotomous_ard <-
  ard_tabulate_value(
    ADSL,
    by = ARM,
    variables = SEX,
    value = list(SEX = "F")
  )
df_dichotomous_ard |> head(5)
#> # An ARD data frame: 5 × 11
#>   group1 group1_level         variable variable_level context   stat_name   stat
#>   <chr>  <list>               <chr>    <list>         <chr>     <chr>     <list>
#> 1 ARM    Placebo              SEX      F              tabulate… n         53    
#> 2 ARM    Placebo              SEX      F              tabulate… N         86    
#> 3 ARM    Placebo              SEX      F              tabulate… p          0.616
#> 4 ARM    Xanomeline High Dose SEX      F              tabulate… n         40    
#> 5 ARM    Xanomeline High Dose SEX      F              tabulate… N         84    
#> # ℹ 4 more variables: stat_label <chr>, fmt_fun <list>, warning <list>,
#> #   error <list>

Combine Results

As a last step, you can combine all of these objects into a single object using bind_ard(), which is similar to dplyr::bind_rows() and includes additional structural checks for our results.

bind_ard(
  df_continuous_ard,
  df_categorical_ard,
  df_dichotomous_ard
)
#> # An ARD data frame: 57 × 11
#>    group1 group1_level         variable variable_level context stat_name   stat
#>    <chr>  <list>               <chr>    <list>         <chr>   <chr>     <list>
#>  1 ARM    Placebo              AGE      <NULL>         summary median     76   
#>  2 ARM    Placebo              AGE      <NULL>         summary p25        69   
#>  3 ARM    Placebo              AGE      <NULL>         summary p75        82   
#>  4 ARM    Placebo              AGE      <NULL>         summary mean       75.2 
#>  5 ARM    Placebo              AGE      <NULL>         summary sd          8.59
#>  6 ARM    Placebo              AGE      <NULL>         summary min        52   
#>  7 ARM    Placebo              AGE      <NULL>         summary max        89   
#>  8 ARM    Xanomeline High Dose AGE      <NULL>         summary median     76   
#>  9 ARM    Xanomeline High Dose AGE      <NULL>         summary p25        70.5 
#> 10 ARM    Xanomeline High Dose AGE      <NULL>         summary p75        80   
#> # ℹ 47 more rows
#> # ℹ 4 more variables: stat_label <chr>, fmt_fun <list>, warning <list>,
#> #   error <list>

Shortcut

The ard_stack() function provides a shortcut to perform the calculations above in a single step.

In the example below, the data and .by arguments are passed to each subsequent ard_*() function call. Moreover, this will also return the univariate tabulation of the .by variable, which would be used to add counts to the header row of the table.

ard_stack(
  data = ADSL,
  .by = ARM,
  ard_summary(
    variables = AGE,
    statistic = ~ continuous_summary_fns(c(
      "median",
      "p25",
      "p75",
      "mean",
      "sd",
      "min",
      "max"
    ))
  ),
  ard_tabulate(variables = AGEGR1),
  ard_tabulate_value(variables = SEX, value = list(SEX = "F"))
)
#> # An ARD data frame: 66 × 11
#>    group1 group1_level variable variable_level context  stat_name   stat
#>    <chr>  <list>       <chr>    <list>         <chr>    <chr>     <list>
#>  1 ARM    Placebo      AGE      <NULL>         summary  median    76    
#>  2 ARM    Placebo      AGE      <NULL>         summary  p25       69    
#>  3 ARM    Placebo      AGE      <NULL>         summary  p75       82    
#>  4 ARM    Placebo      AGE      <NULL>         summary  mean      75.2  
#>  5 ARM    Placebo      AGE      <NULL>         summary  sd         8.59 
#>  6 ARM    Placebo      AGE      <NULL>         summary  min       52    
#>  7 ARM    Placebo      AGE      <NULL>         summary  max       89    
#>  8 ARM    Placebo      AGEGR1   65-80          tabulate n         42    
#>  9 ARM    Placebo      AGEGR1   65-80          tabulate N         86    
#> 10 ARM    Placebo      AGEGR1   65-80          tabulate p          0.488
#> # ℹ 56 more rows
#> # ℹ 4 more variables: stat_label <chr>, fmt_fun <list>, warning <list>,
#> #   error <list>

Adverse Events

Next, we will review several examples for creating basic adverse events (AE) tables. We will skip to examples utilizing the shortcut functions ard_stack_hierarchical() and ard_stack_hierarchical_count(). These functions utilize multiple calls to ard_hierarchical() and ard_hierarchical_count() to calculate the needed summary statistics.

For the computations below, we will not only make use of a subset of the ADAE dataset. We will also rely on ADSL for the full study population, which is used as the denominator in the rate calculations.

To match the treatment arm variables, we need to do a small data manipulation on the naming of the treatment variable.

# rename trt variable
adsl <- ADSL

# subset to Treatment emergent AES
adae <- ADAE |>
  # keep the most reported AEs for a smaller table
  dplyr::filter(.by = AETERM, dplyr::n() > 25, TRTEMFL == "Y")

Participant-level summaries

A common type of AE table contains participant-level summaries. Here, we are reporting the number and percentage of subjects with at least one AE by system organ class and preferred term.

Body System or Organ Class
    Dictionary-Derived Term
Placebo
N = 86
1
Xanomeline High Dose
N = 84
1
Xanomeline Low Dose
N = 84
1
Any Adverse Event 30 (34.9%) 59 (70.2%) 63 (75.0%)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS 15 (17.4%) 31 (36.9%) 34 (40.5%)
    APPLICATION SITE DERMATITIS 5 ( 5.8%) 7 ( 8.3%) 9 (10.7%)
    APPLICATION SITE ERYTHEMA 3 ( 3.5%) 15 (17.9%) 12 (14.3%)
    APPLICATION SITE IRRITATION 3 ( 3.5%) 9 (10.7%) 9 (10.7%)
    APPLICATION SITE PRURITUS 6 ( 7.0%) 22 (26.2%) 22 (26.2%)
NERVOUS SYSTEM DISORDERS 2 ( 2.3%) 11 (13.1%) 8 ( 9.5%)
    DIZZINESS 2 ( 2.3%) 11 (13.1%) 8 ( 9.5%)
SKIN AND SUBCUTANEOUS TISSUE DISORDERS 16 (18.6%) 30 (35.7%) 29 (34.5%)
    ERYTHEMA 8 ( 9.3%) 14 (16.7%) 14 (16.7%)
    PRURITUS 8 ( 9.3%) 26 (31.0%) 21 (25.0%)
    RASH 5 ( 5.8%) 9 (10.7%) 13 (15.5%)
1 n (%)

The ard_stack_hierarchical() function provides a shortcut to perform the calculations needed for the summary table in a single step.

In the example below, the data and by arguments are passed to each subsequent calculation. The function utilizes USUBJID (passed to the id argument) as the subject identifier for participant-level calculations, and ADSL is used to define the denominator. With variables = c(AEBODSYS, AEDECOD), the function returns rates of adverse events by AEDECOD nested within AESOC as well as by AESOC. With over_variables = TRUE, the function also returns rates of any adverse event across all system organ classes and preferred terms.

ard_stack_hierarchical(
  data = adae,
  by = TRTA,
  variables = c(AEBODSYS, AEDECOD),
  denominator = adsl,
  id = USUBJID,
  over_variables = TRUE
)
#> # An ARD data frame: 117 × 13
#>    group1 group1_level group2 group2_level variable         variable_level      
#>    <chr>  <list>       <chr>  <list>       <chr>            <list>              
#>  1 NA     <NULL>       NA     <NULL>       TRTA             Placebo             
#>  2 NA     <NULL>       NA     <NULL>       TRTA             Placebo             
#>  3 NA     <NULL>       NA     <NULL>       TRTA             Placebo             
#>  4 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline High Dose
#>  5 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline High Dose
#>  6 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline High Dose
#>  7 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline Low Dose 
#>  8 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline Low Dose 
#>  9 NA     <NULL>       NA     <NULL>       TRTA             Xanomeline Low Dose 
#> 10 TRTA   Placebo      NA     <NULL>       ..ard_hierarchi… TRUE                
#> # ℹ 107 more rows
#> # ℹ 7 more variables: context <chr>, stat_name <chr>, stat_label <chr>,
#> #   stat <list>, fmt_fun <list>, warning <list>, error <list>

Event-level Summaries

In addition to participant-level summaries, event-level summaries are often needed. For these types of tables, we report total counts of AEs , and therefore we can use the ADAE data directly. We will need to count AEs overall, by system organ class, and by preferred term (within system organ class).

Body System or Organ Class
    Dictionary-Derived Term
Placebo
N = 86
1
Xanomeline High Dose
N = 84
1
Xanomeline Low Dose
N = 84
1
Total Number of Adverse Events 64 176 169
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS 29 86 85
    APPLICATION SITE DERMATITIS 9 12 15
    APPLICATION SITE ERYTHEMA 3 23 20
    APPLICATION SITE IRRITATION 7 16 18
    APPLICATION SITE PRURITUS 10 35 32
NERVOUS SYSTEM DISORDERS 3 15 13
    DIZZINESS 3 15 13
SKIN AND SUBCUTANEOUS TISSUE DISORDERS 32 75 71
    ERYTHEMA 12 22 22
    PRURITUS 11 38 31
    RASH 9 15 18
1 n

The ard_stack_hierarchical_count() function provides a shortcut to perform the calculations needed for the summary table in a single step.

In the example below, the data and by arguments are passed to each subsequent calculation. With variables = c(AEBODSYS, AEDECOD), the function returns counts of adverse events by AEDECOD nested within AESOC as well as by AESOC. With over_variables = TRUE, the function also returns counts of any adverse event across all system organ classes and preferred terms.

ard_stack_hierarchical_count(
  data = adae,
  by = TRTA,
  variables = c(AEBODSYS, AETERM),
  over_variables = TRUE
)
#> # An ARD data frame: 36 × 13
#>    group1 group1_level         group2  
#>    <chr>  <list>               <chr>   
#>  1 TRTA   Placebo              NA      
#>  2 TRTA   Xanomeline High Dose NA      
#>  3 TRTA   Xanomeline Low Dose  NA      
#>  4 TRTA   Placebo              NA      
#>  5 TRTA   Xanomeline High Dose NA      
#>  6 TRTA   Xanomeline Low Dose  NA      
#>  7 TRTA   Placebo              AEBODSYS
#>  8 TRTA   Xanomeline High Dose AEBODSYS
#>  9 TRTA   Xanomeline Low Dose  AEBODSYS
#> 10 TRTA   Placebo              AEBODSYS
#> # ℹ 26 more rows
#> # ℹ 10 more variables: group2_level <list>, variable <chr>,
#> #   variable_level <list>, context <chr>, stat_name <chr>, stat_label <chr>,
#> #   stat <list>, fmt_fun <list>, warning <list>, error <list>