CDISC Pilot replication w/ gtsummary

CDISCPILOT01 - Initial Case Study of the CDISC SDTM/ADaM Pilot Project

CSR
Tables
Figures
Author

Agustin Calatroni

Published

March 11, 2024

CDISCPILOT01

Brief Description of Case Study

This study was a prospective, randomized, multi-center, double-blind, placebo-controlled, parallel-group study. The objectives of the study were to evaluate the efficacy and safety of transdermal xanomeline, 50 cm2 and 75 cm2 , and placebo in subjects with mild to moderate Alzheimer’s disease.


CDISC Pilot Replication

Submissions Pilot to FDA

Report

Unable to display PDF file. Download instead.

Tables

Tables 14-1

Table 14-1.01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adsl_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt')

# UPDATE DATA
adsl <- adsl_orig %>% 
   mutate(TRT01P = factor(TRT01P, 
                          levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                          labels = c('Placebo', 'Low Dose','High Dose')))

# TABLE
tbl_summary(
   data = adsl,
   by = TRT01P,
   include = c(ITTFL, SAFFL, EFFFL, COMP24FL, DCDECOD),
   value = list(everything() ~ 'Y',
                DCDECOD ~ 'COMPLETED'),
   label = list( ITTFL ~ 'Intent-To-Treat (ITT)', 
                 SAFFL ~ 'Safety', 
                 EFFFL ~ 'Efficacy', 
                 COMP24FL ~ 'Complete Week 24',
                 DCDECOD ~ 'Complete Study')) %>% 
   modify_header(
      label = '',
      all_stat_cols(stat_0 = FALSE) ~ '**{level}<br>(N={n})**') %>% 
   add_overall(
      last = TRUE,
      col_label =  '**Total<br>(N={N})**') %>% 
   modify_spanning_header(
      c(stat_2, stat_3) ~ '**Xanomeline**') %>% 
   modify_footnote(
      all_stat_cols() ~ 'N in column headers represents number of subjects entered in study (i.e., signed informed consent).') %>%
   modify_table_styling(
      columns = label,
      rows = label == "Intent-To-Treat (ITT)",
      footnote = "The ITT population includes all subjects randomized.") %>% 
   modify_table_styling(
      columns = label,
      rows = label == "Safety",
      footnote = "The Safety population includes all randomized subjects known to have taken at least one dose of randomized study drug.") %>% 
   modify_table_styling(
      columns = label,
      rows = label == "Efficacy",
      footnote = "The Efficacy population includes all subjects in the safety population who also have at least one post-baseline ADAS-Cog and CIBIC+ assessment.") %>% 
   modify_caption(
      '**Table 14-1.01 <br> Summary of Populations**')
Table 14-1.01
Summary of Populations
Placebo
(N=86)
1
Xanomeline Total
(N=254)
1
Low Dose
(N=84)
1
High Dose
(N=84)
1
Intent-To-Treat (ITT)2 86 (100%) 84 (100%) 84 (100%) 254 (100%)
Safety3 86 (100%) 84 (100%) 84 (100%) 254 (100%)
Efficacy4 79 (92%) 81 (96%) 74 (88%) 234 (92%)
Complete Week 24 60 (70%) 28 (33%) 30 (36%) 118 (46%)
Complete Study 58 (67%) 25 (30%) 27 (32%) 110 (43%)
1 N in column headers represents number of subjects entered in study (i.e., signed informed consent).
2 The ITT population includes all subjects randomized.
3 The Safety population includes all randomized subjects known to have taken at least one dose of randomized study drug.
4 The Efficacy population includes all subjects in the safety population who also have at least one post-baseline ADAS-Cog and CIBIC+ assessment.

Table 14-1.02

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adsl_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt')

# UPDATE DATA
adsl <- adsl_orig %>% 
   mutate(TRT01P = factor(TRT01P, 
                          levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                          labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(DCDECOD = case_when(
      COMP24FL == 'Y' & COMP24FL != 'Complete' ~ 'Completed',
      .default = DCDECOD)) %>% 
   mutate(DCDECOD = factor(DCDECOD) %>% 
             str_to_title() %>% 
             fct_recode(`Subject decided to withdraw` = 'Withdrawal By Subject') %>% 
             fct_relevel('Adverse Event',
                         'Death',
                         'Lack Of Efficacy',
                         'Lost To Follow-Up',
                         'Subject decided to withdraw')) %>% 
   mutate(COMP24FL = factor(COMP24FL, 
                            levels = c('Y','N'), 
                            labels = c('Completed Week 24','Early Termination (prior to Week 24)')) )

# TEST
p_loefl <- adsl %>%
   mutate(loefl = ifelse(DCREASCD %in% 'Lack of Efficacy', 1, 0)) %>%
   with(.,table(TRT01P, loefl)) %>% 
   fisher.test() %>% 
   broom::tidy() %>% 
   pull(p.value)

# TABLE
tbl_summary(
   data = adsl,
   by = TRT01P,
   include = c(COMP24FL, DCDECOD),
   label = list(COMP24FL = 'Completion Status',
                DCDECOD = 'Reason for Early Termination (prior to Week 24)'),
   missing = 'always',
   missing_text = 'Missing') %>% 
   modify_header(
      label = '',
      all_stat_cols(stat_0 = FALSE) ~ '**{level}<br>(N={n})**') %>% 
   add_overall(
      last = TRUE,
      col_label =  '**Total<br>(N={N})**') %>% 
   remove_row_type(
      variables = DCDECOD,
      type = 'level',
      level_value = 'Completed') %>% 
   add_p(
      test = list(COMP24FL ~ 'fisher.test',
                  DCDECOD  ~ 'chisq.test'),
      pvalue_fun = function(x) style_pvalue(x, digits = 3)) %>%
   modify_footnote(
      all_stat_cols() ~ NA,
      p.value ~ "Fisher's exact test.") %>% 
   modify_spanning_header(
      c(stat_2, stat_3) ~ '**Xanomeline**') %>% 
   modify_table_styling(
      columns = label,
      rows = label == 'Lack Of Efficacy',
      footnote = 'Based on either patient/caregiver perception or physician perception.') %>% 
   modify_table_body(
      ~.x %>% 
         mutate(label  = case_match(
            label,
            'Physician Decision'          ~ 'Physician decided to withdraw subject',
            'Study Terminated By Sponsor' ~ 'Sponsor decision',
            .default = label)) %>% 
         mutate(p.value  = ifelse(variable == 'DCDECOD' & label == 'Lack Of Efficacy', p_loefl, p.value )) %>% 
         mutate(across(all_stat_cols(), ~gsub('^0.*', '0 (0%)', .))) ) %>%  
   modify_caption(
      '**Table 14-1.02 <br> Summary of End of Study Data**') 
Table 14-1.02
Summary of End of Study Data
Placebo
(N=86)
Xanomeline Total
(N=254)
p-value1
Low Dose
(N=84)
High Dose
(N=84)
Completion Status



<0.001
    Completed Week 24 60 (70%) 28 (33%) 30 (36%) 118 (46%)
    Early Termination (prior to Week 24) 26 (30%) 56 (67%) 54 (64%) 136 (54%)
    Missing 0 (0%) 0 (0%) 0 (0%) 0 (0%)
Reason for Early Termination (prior to Week 24)



<0.001
    Adverse Event 8 (9.3%) 44 (52%) 39 (46%) 91 (36%)
    Death 1 (1.2%) 1 (1.2%) 0 (0%) 2 (0.8%)
    Lack Of Efficacy2 3 (3.5%) 0 (0%) 1 (1.2%) 4 (1.6%) 0.328
    Lost To Follow-Up 1 (1.2%) 0 (0%) 0 (0%) 1 (0.4%)
    Subject decided to withdraw 9 (10%) 8 (9.5%) 8 (9.5%) 25 (9.8%)
    Physician decided to withdraw subject 1 (1.2%) 0 (0%) 2 (2.4%) 3 (1.2%)
    Protocol Violation 2 (2.3%) 1 (1.2%) 3 (3.6%) 6 (2.4%)
    Sponsor decision 1 (1.2%) 2 (2.4%) 1 (1.2%) 4 (1.6%)
    Missing 0 (0%) 0 (0%) 0 (0%) 0 (0%)
1 Fisher’s exact test.
2 Based on either patient/caregiver perception or physician perception.

Table 14-1.03

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adsl_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt')

# LONG
adsl_l <- adsl_orig %>% 
   select(SUBJID, SITEGR1, SITEID, TRT01P, ITTFL, EFFFL, COMP24FL ) %>% 
   pivot_longer(cols = -c(SUBJID, SITEGR1, SITEID, TRT01P),
                names_to = 'DESC',
                values_to = 'YN') %>%
   mutate(YN = ifelse(YN == 'Y', 1, 0)) %>% 
   filter(YN == 1) %>% 
   mutate(DESC = factor(DESC, 
                        levels = c('ITTFL','EFFFL','COMP24FL'),
                        labels = c('ITT','Eff','Com'))) %>% 
   mutate(TRT01P = factor(TRT01P, 
                          levels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'),
                          labels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'))) %>% 
   mutate(SITE = str_glue('{SITEGR1} \U2014 {SITEID}'))

# TABLE
tbl_merge( 
   tbls = list(
      adsl_l %>% tbl_cross(row = SITE,
                           col = DESC,
                           margin = 'row'),
      
      adsl_l %>% filter(TRT01P == 'Placebo') %>% 
         tbl_cross(row = SITE,
                   col = DESC,
                   margin = 'row'),
      
      adsl_l %>% filter(TRT01P == 'Xanomeline Low Dose') %>% 
         tbl_cross(row = SITE,
                   col = DESC,
                   margin = 'row'),
      
      adsl_l %>% filter(TRT01P == 'Xanomeline High Dose') %>% 
         tbl_cross(row = SITE,
                   col = DESC,
                   margin = 'row')),
   tab_spanner = c('**<br>&nbsp;Total<br>(N=254)**', '**Xanomeline<br>Low Dose<br>(N=84)**', '**Xanomeline<br>High Dose<br>(N=84)**', '**<br>&nbsp;Placebo<br>(N=86)**')) %>% 
   modify_table_body(
      ~.x %>% 
         filter(!(variable == 'SITE' & row_type == 'label')) %>% 
         mutate(across(all_stat_cols(), ~ifelse(.x %in% c(0, NA), '0', .x) ) ) %>% 
         relocate(c(stat_1_1, stat_2_1, stat_3_1), .after = last_col())) %>% 
   modify_header(
      label           ~ '**Pooled \U2014 Site ID**',
      all_stat_cols() ~ '**{level}**') %>% 
   modify_footnote(
      starts_with('stat_1_') ~ 'ITT: Number of subjects in the ITT population',
      starts_with('stat_2_') ~ 'Eff: Number of subjects in the Efficacy population',
      starts_with('stat_3_') ~ 'Com: Number of subjects completing Week 24.') %>%
   modify_column_alignment(
      columns = all_stat_cols() , align = 'right') %>% 
   modify_caption(
      '**Table 14-1.03<br>Summary of Number of Subjects By Site**') 
Table 14-1.03
Summary of Number of Subjects By Site
Pooled — Site ID Xanomeline
Low Dose
(N=84)
Xanomeline
High Dose
(N=84)

 Placebo
(N=86)

 Total
(N=254)
ITT1 Eff2 Com3 ITT1 Eff2 Com3 ITT1 Eff2 Com3 ITT1 Eff2 Com3
    701 — 701 14 14 11 13 13 5 14 14 7 41 41 23
    703 — 703 6 5 4 6 5 1 6 5 2 18 15 7
    704 — 704 9 9 5 8 7 3 8 8 0 25 24 8
    705 — 705 5 3 2 5 5 3 6 4 1 16 12 6
    708 — 708 9 9 7 8 8 2 8 5 2 25 22 11
    709 — 709 7 7 5 7 6 2 7 7 3 21 20 10
    710 — 710 11 8 6 10 10 2 10 8 5 31 26 13
    713 — 713 3 3 3 3 3 2 3 2 2 9 8 7
    716 — 716 8 8 7 8 8 3 8 8 3 24 24 13
    718 — 718 4 4 3 5 5 1 4 4 1 13 13 5
    900 — 702 0 0 0 1 1 0 0 0 0 1 1 0
    900 — 706 1 1 1 1 1 0 1 1 0 3 3 1
    900 — 707 1 1 1 1 1 0 0 0 0 2 2 1
    900 — 711 1 1 1 1 1 0 2 1 0 4 3 1
    900 — 714 2 2 2 2 2 1 2 2 1 6 6 4
    900 — 715 3 2 2 3 3 1 2 2 0 8 7 3
    900 — 717 2 2 0 2 2 2 3 3 3 7 7 5
Total 86 79 60 84 81 28 84 74 30 254 234 118
1 ITT: Number of subjects in the ITT population
2 Eff: Number of subjects in the Efficacy population
3 Com: Number of subjects completing Week 24.

Tables 14-2

Table 14-2.01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adsl_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt')

# DATA
adsl <- adsl_orig %>% 
   mutate(TRT01P = factor(TRT01P, 
                          levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                          labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(SEX = factor(SEX, labels = c('Female','Male')) %>% fct_rev(),
          AGEGR1 = factor(AGEGR1, 
                          level = c('<65','65-80','>80'),
                          labels = c('<65 yrs','65-80 yrs','>80 yrs')),
          RACE = factor(RACE) %>% str_to_title(),
          BMIBLGR1 = factor(BMIBLGR1, levels = c('<25','25-<30','>=30') ),
          DURDSGR1 = factor(DURDSGR1, labels = c('<12 months', '>=12 months')))

# TABLE
tbl_summary(
   data = adsl,
   by = TRT01P,
   include = c(AGE, AGEGR1, SEX, RACE, MMSETOT, DURDIS, DURDSGR1, EDUCLVL, WEIGHTBL, HEIGHTBL, BMIBL, BMIBLGR1),
   type = list(all_continuous() ~ 'continuous2'),
   statistic = list( all_continuous() ~ c("{N_nonmiss}", 
                                          "{mean}",
                                          "{sd}",
                                          "{median}",
                                          "{min}",
                                          "{max}"),
                     all_categorical() ~ c("{n} ({p}%)")),
   label = list( AGE    ~ 'Age (y)',
                 AGEGR1 ~ '',
                 SEX    ~ 'Sex',
                 RACE   ~ 'Race (Origin)',
                 MMSETOT~ 'MMSE',
                 DURDIS ~ 'Duration of disease',
                 DURDSGR1 ~ '',
                 BMIBL  ~ 'Baseline BMI',
                 BMIBLGR1 ~ ''),
   digits = list(all_continuous() ~ c(0, 1, 2, 1, 1, 1),
                 all_categorical() ~ c(0, 0) ),
   sort = list(RACE ~ 'frequency'),
   missing = 'no',
   missing_text = 'Missing') %>% 
   modify_header(
      label = '',
      all_stat_cols(stat_0 = FALSE) ~ '**{level}<br>(N={n})**'
   ) %>% 
   add_overall(
      last = TRUE,
      col_label =  '**Total<br>(N={N})**') %>%
   add_p(
      test = list(all_continuous() ~ 'aov', 
                  all_categorical() ~ 'chisq.test'),
      pvalue_fun = function(x) style_pvalue(x, digits = 3)) %>% 
   modify_spanning_header(
      c(stat_2, stat_3) ~ '**Xanomeline**') %>% 
   modify_footnote(
      all_stat_cols() ~ NA,
      p.value ~ 'P-values are results of ANOVA treatment group comparison for continuous variable and Pearsons chisquare test for categorical variables.') %>% 
   modify_table_styling(
      columns = label,
      rows =  variable == 'DURDIS' & row_type  == 'label',
      footnote = "Duration of disease is computed as months between date of enrollment and date of onset of the first definite symptoms of Alzheimers disease.") %>% 
   modify_column_alignment(
      all_stat_cols(), align = 'right') %>% 
   modify_table_body(
      ~.x %>% 
         mutate(label = case_match(
            label,
            "Minimum" ~ "Min",
            "Maximum" ~ "Max",
            "N"       ~ "n",
            .default = label)) ) %>% 
   modify_caption(
      '**Table 14-2.01<br>Summary of Demographic and Baseline Characteristics**')
Table 14-2.01
Summary of Demographic and Baseline Characteristics
Placebo
(N=86)
Xanomeline Total
(N=254)
p-value1
Low Dose
(N=84)
High Dose
(N=84)
Age (y)



0.593
    n 86 84 84 254
    Mean 75.2 75.7 74.4 75.1
    SD 8.59 8.29 7.89 8.25
    Median 76.0 77.5 76.0 77.0
    Min 52.0 51.0 56.0 51.0
    Max 89.0 88.0 88.0 89.0




0.144
    <65 yrs 14 (16%) 8 (10%) 11 (13%) 33 (13%)
    65-80 yrs 42 (49%) 47 (56%) 55 (65%) 144 (57%)
    >80 yrs 30 (35%) 29 (35%) 18 (21%) 77 (30%)
Sex



0.141
    Male 33 (38%) 34 (40%) 44 (52%) 111 (44%)
    Female 53 (62%) 50 (60%) 40 (48%) 143 (56%)
Race (Origin)



0.604
    White 78 (91%) 78 (93%) 74 (88%) 230 (91%)
    Black Or African American 8 (9%) 6 (7%) 9 (11%) 23 (9%)
    American Indian Or Alaska Native 0 (0%) 0 (0%) 1 (1%) 1 (0%)
MMSE



0.595
    n 86 84 84 254
    Mean 18.0 17.9 18.5 18.1
    SD 4.27 4.22 4.16 4.21
    Median 19.5 18.0 20.0 19.0
    Min 10.0 10.0 10.0 10.0
    Max 23.0 24.0 24.0 24.0
Duration of disease2



0.153
    n 86 84 84 254
    Mean 42.7 48.7 40.5 43.9
    SD 30.24 29.58 24.69 28.40
    Median 35.3 40.3 36.0 36.3
    Min 7.2 7.8 2.2 2.2
    Max 183.1 130.8 135.0 183.1




0.789
    <12 months 5 (6%) 3 (4%) 4 (5%) 12 (5%)
    >=12 months 81 (94%) 81 (96%) 80 (95%) 242 (95%)
Years of Education



0.388
    n 86 84 84 254
    Mean 12.6 13.2 12.5 12.8
    SD 2.95 4.15 2.92 3.38
    Median 12.0 12.0 12.0 12.0
    Min 6.0 3.0 6.0 3.0
    Max 21.0 24.0 20.0 24.0
Baseline Weight (kg)



0.003
    n 86 83 84 253
    Mean 62.8 67.3 70.0 66.6
    SD 12.77 14.12 14.65 14.13
    Median 60.6 64.9 69.2 66.7
    Min 34.0 45.4 41.7 34.0
    Max 86.2 106.1 108.0 108.0
Baseline Height (cm)



0.126
    n 86 84 84 254
    Mean 162.6 163.4 165.8 163.9
    SD 11.52 10.42 10.13 10.76
    Median 162.6 162.6 165.1 162.9
    Min 137.2 135.9 146.1 135.9
    Max 185.4 195.6 190.5 195.6
Baseline BMI



0.013
    n 86 83 84 253
    Mean 23.6 25.1 25.3 24.7
    SD 3.67 4.27 4.16 4.09
    Median 23.4 24.3 24.8 24.2
    Min 15.1 17.7 13.7 13.7
    Max 33.3 40.1 34.5 40.1




0.233
    <25 59 (69%) 47 (56%) 44 (52%) 150 (59%)
    25-<30 21 (24%) 27 (32%) 28 (33%) 76 (30%)
    >=30 6 (7%) 10 (12%) 12 (14%) 28 (11%)
1 P-values are results of ANOVA treatment group comparison for continuous variable and Pearsons chisquare test for categorical variables.
2 Duration of disease is computed as months between date of enrollment and date of onset of the first definite symptoms of Alzheimers disease.

Tables 14-3

Table 14-3.01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y') %>% 
   filter(AVISITN %in% c(0, 24)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
data       <- adqsadas
variable   <- 'CHG'
by         <- 'TRTP'
adj.vars   <- c('BASE','SITEGR1')

lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   
   b
}
#lm_pairwise(adqsadas, 'CHG','TRTP', c('BASE','SITEGR1'))

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   # l <- lm(as.formula(f), data = data %>% mutate(!!sym(by) := as.numeric(!!sym(by))) )
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}
#lm_trend(adqsadas, 'CHG','TRTP', c('BASE','SITEGR1'))

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 24',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.01<br>Primary Endpoint Analysis: ADAS Cog (11) - Change from Baseline to Week 24 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.01
Primary Endpoint Analysis: ADAS Cog (11) - Change from Baseline to Week 24 - LOCF
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

79
24.1 (12.19)
21.0 (5;61)

81
24.4 (12.92)
21.0 (5;57)

74
21.3 (11.74)
18.0 (3;57)





Week 24, n
Mean (SD)
Median (Range)

79
26.7 (13.79)
24.0 (5;62)

81
26.4 (13.18)
25.0 (6;62)

74
22.8 (12.48)
20.0 (3;62)





Change from Baseline, n
Mean (SD)
Median (Range)

79
2.5 (5.80)
2.0 (-11;16)

81
2.0 (5.55)
2.0 (-11;17)

74
1.5 (4.26)
1.0 (-7;13)

0.245

0.569
-0.5 (0.82)
(-2.1;1.1)

0.233
-1.0 (0.84)
(-2.7;0.7)

0.520
-0.5 (0.84)
(-2.2;1.1)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons
Warning

I updated the table layout because gtsummary does not accommodate stacking rows with different formats well.

Caution

Here, we need to apply gt::fmt_markdown to format the line breaks within the table.

Table 14-3.02

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqscibc_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqscibc.xpt')

# SUBSET
adqscibc <- adqscibc_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'CIBICVAL' & ANL01FL == 'Y') %>% 
   filter(AVISITN == 24) %>% 
   filter(!is.na(AVAL)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   b
   }

lm_trend  <- function(data, variable, by, adj.vars = c('SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()

   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqscibc,
   by = TRTP,
   include = c(AVAL),
   type = list(AVAL ~ 'continuous'),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(AVAL ~ 'Week 24')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = AVAL ~ lm_trend ) %>% 
   add_stat(fns = AVAL ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ ' Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.02<br>Primary Endpoint Analysis: CIBIC+ - Summary at Week 24 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.02
Primary Endpoint Analysis: CIBIC+ - Summary at Week 24 - LOCF
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Week 24, n
Mean (SD)
Median (Range)

79
4.3 (0.77)
4.0 (2;6)

81
4.2 (0.79)
4.0 (2;6)

74
4.3 (0.81)
4.0 (3;6)

0.960

0.489
-0.1 (0.13)
(-0.3;0.2)

0.799
0.0 (0.13)
(-0.2;0.3)

0.349
0.1 (0.13)
(-0.1;0.4)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.

Table 14-3.03

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y') %>% 
   filter(AVISITN %in% c(0, 8)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')))

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   # l <- lm(as.formula(f), data = data %>% mutate(!!sym(by) := as.numeric(!!sym(by))) )
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 8',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.03<br>ADAS Cog (11) - Change from Baseline to Week 8 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.03
ADAS Cog (11) - Change from Baseline to Week 8 - LOCF
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

79
24.1 (12.19)
21.0 (5;61)

81
24.4 (12.92)
21.0 (5;57)

74
21.3 (11.74)
18.0 (3;57)





Week 8, n
Mean (SD)
Median (Range)

79
25.0 (13.10)
22.0 (5;62)

81
26.2 (12.98)
25.0 (5;62)

74
22.3 (12.41)
19.0 (2;62)





Change from Baseline, n
Mean (SD)
Median (Range)

79
0.8 (4.81)
1.0 (-12;16)

81
1.8 (4.14)
2.0 (-12;14)

74
1.0 (3.62)
1.0 (-8;13)

0.497

0.099
1.1 (0.65)
(-0.2;2.4)

0.751
0.2 (0.67)
(-1.1;1.5)

0.195
-0.9 (0.66)
(-2.2;0.4)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons

Table 14-3.04

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqscibc_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqscibc.xpt')

# SUBSET
adqscibc <- adqscibc_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'CIBICVAL' & ANL01FL == 'Y') %>% 
   filter(AVISITN == 8) %>% 
   filter(!is.na(AVAL)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   b
   }

lm_trend  <- function(data, variable, by, adj.vars = c('SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()

   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqscibc,
   by = TRTP,
   include = c(AVAL),
   type = list(AVAL ~ 'continuous'),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(AVAL ~ 'Week 8')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = AVAL ~ lm_trend ) %>% 
   add_stat(fns = AVAL ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ ' Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.04<br>CIBIC+ - Summary at Week 8 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.04
CIBIC+ - Summary at Week 8 - LOCF
Placebo
(N=77)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=73)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Week 8, n
Mean (SD)
Median (Range)

77
3.9 (0.73)
4.0 (2;6)

81
4.0 (0.72)
4.0 (2;6)

73
4.1 (0.75)
4.0 (2;6)

0.167

0.754
0.0 (0.12)
(-0.2;0.3)

0.128
0.2 (0.12)
(-0.1;0.4)

0.218
0.1 (0.12)
(-0.1;0.4)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.

Table 14-3.05

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y') %>% 
   filter(AVISITN %in% c(0, 16)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')))

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 16',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.05<br>ADAS Cog (11) - Change from Baseline to Week 16 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.05
ADAS Cog (11) - Change from Baseline to Week 16 - LOCF
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

79
24.1 (12.19)
21.0 (5;61)

81
24.4 (12.92)
21.0 (5;57)

74
21.3 (11.74)
18.0 (3;57)





Week 16, n
Mean (SD)
Median (Range)

79
26.1 (14.16)
23.0 (5;63)

81
26.0 (13.05)
25.0 (5;62)

74
22.5 (12.33)
20.0 (4;62)





Change from Baseline, n
Mean (SD)
Median (Range)

79
2.0 (5.89)
2.0 (-17;23)

81
1.6 (4.10)
2.0 (-9;14)

74
1.2 (4.33)
1.0 (-11;13)

0.412

0.724
-0.3 (0.77)
(-1.8;1.2)

0.392
-0.7 (0.79)
(-2.2;0.9)

0.606
-0.4 (0.78)
(-1.9;1.1)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons

Table 14-3.06

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE)

# IMPORT DATA
adqscibc_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqscibc.xpt')

# SUBSET
adqscibc <- adqscibc_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'CIBICVAL' & ANL01FL == 'Y') %>% 
   filter(AVISITN == 16) %>% 
   filter(!is.na(AVAL)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   b
   }

lm_trend  <- function(data, variable, by, adj.vars = c('SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()

   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqscibc,
   by = TRTP,
   include = c(AVAL),
   type = list(AVAL ~ 'continuous'),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(AVAL ~ 'Week 16')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = AVAL ~ lm_trend ) %>% 
   add_stat(fns = AVAL ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ ' Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.06<br>CIBIC+ - Summary at Week 16 - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.06
CIBIC+ - Summary at Week 16 - LOCF
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Week 16, n
Mean (SD)
Median (Range)

79
4.2 (0.70)
4.0 (3;6)

81
4.0 (0.77)
4.0 (2;6)

74
4.0 (0.75)
4.0 (2;5)

0.214

0.219
-0.1 (0.12)
(-0.4;0.1)

0.272
-0.1 (0.12)
(-0.4;0.1)

0.916
0.0 (0.12)
(-0.2;0.2)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons.

Table 14-3.07

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(COMP24FL == "Y" & EFFFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y' & DTYPE != 'LOCF') %>% 
   filter(AVISITN %in% c(0, 24)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')))

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   # l <- lm(as.formula(f), data = data %>% mutate(!!sym(by) := as.numeric(!!sym(by))) )
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 24',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons<br>
                                          Note that only assessments falling within the assessment window are included in the summary for a visit.',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.07<br>ADAS Cog (11) - Change from Baseline to Week 24 - Completers at Wk 24-Observed Cases-Windowed**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.07
ADAS Cog (11) - Change from Baseline to Week 24 - Completers at Wk 24-Observed Cases-Windowed
Placebo
(N=59)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=27)
High Dose
(N=30)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

59
23.2 (11.74)
21.0 (5;51)

27
24.0 (13.89)
20.0 (5;57)

30
20.5 (11.50)
18.0 (3;49)





Week 24, n
Mean (SD)
Median (Range)

59
25.3 (13.32)
23.0 (5;58)

27
24.1 (11.87)
22.0 (8;51)

30
21.8 (12.60)
18.5 (3;44)





Change from Baseline, n
Mean (SD)
Median (Range)

59
2.1 (5.89)
2.0 (-11;16)

27
0.1 (5.86)
1.0 (-11;12)

30
1.3 (4.51)
1.0 (-7;13)

0.234

0.105
-2.1 (1.26)
(-4.6;0.4)

0.461
-0.9 (1.22)
(-3.3;1.5)

0.430
1.2 (1.47)
(-1.8;4.1)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons
Note that only assessments falling within the assessment window are included in the summary for a visit.

Table 14-3.08

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(EFFFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y' & SEX == "M") %>% 
   filter(AVISITN %in% c(0, 24)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   # l <- lm(as.formula(f), data = data %>% mutate(!!sym(by) := as.numeric(!!sym(by))) )
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 24',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.08<br>ADAS Cog (11) - Change from Baseline to Week 24 in Male Subjects - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.08
ADAS Cog (11) - Change from Baseline to Week 24 in Male Subjects - LOCF
Placebo
(N=33)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=34)
High Dose
(N=39)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

33
22.8 (13.45)
19.0 (5;61)

34
23.3 (14.03)
21.5 (7;57)

39
20.8 (11.11)
17.0 (3;51)





Week 24, n
Mean (SD)
Median (Range)

33
24.7 (13.89)
20.0 (5;62)

34
25.7 (14.72)
24.0 (6;57)

39
22.7 (12.32)
21.0 (3;51)





Change from Baseline, n
Mean (SD)
Median (Range)

33
1.9 (6.14)
1.0 (-11;16)

34
2.5 (5.61)
1.0 (-6;14)

39
1.8 (3.77)
1.0 (-7;13)

0.873

0.712
0.5 (1.30)
(-2.1;3.1)

0.915
0.1 (1.25)
(-2.4;2.6)

0.783
-0.3 (1.26)
(-2.9;2.2)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons

Table 14-3.09

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# SUBSET
adqsadas <- adqsadas_orig %>%
   filter(EFFFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y' & SEX == "F") %>% 
   filter(AVISITN %in% c(0, 24)) %>% 
   filter(!is.na(CHG)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) 

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   # l <- lm(as.formula(f), data = data %>% mutate(!!sym(by) := as.numeric(!!sym(by))) )
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsadas,
   by = TRTP,
   include = c(BASE, AVAL, CHG),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Week 24',
                CHG ~ 'Change from Baseline')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = CHG ~ lm_trend ) %>% 
   add_stat(fns = CHG ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.09<br>ADAS Cog (11) - Change from Baseline to Week 24 in Female Subjects - LOCF**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.09
ADAS Cog (11) - Change from Baseline to Week 24 in Female Subjects - LOCF
Placebo
(N=46)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=47)
High Dose
(N=35)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

46
25.1 (11.25)
23.5 (5;51)

47
25.2 (12.15)
21.0 (5;55)

35
21.8 (12.54)
18.0 (5;57)





Week 24, n
Mean (SD)
Median (Range)

46
28.1 (13.70)
24.0 (8;59)

47
26.9 (12.09)
25.0 (8;62)

35
22.9 (12.84)
19.0 (4;62)





Change from Baseline, n
Mean (SD)
Median (Range)

46
3.0 (5.57)
3.0 (-8;16)

47
1.7 (5.54)
2.0 (-11;17)

35
1.1 (4.77)
0.0 (-7;13)

0.094

0.160
-1.6 (1.10)
(-3.7;0.6)

0.135
-1.8 (1.20)
(-4.2;0.6)

0.843
-0.2 (1.21)
(-2.6;2.2)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons

Table 14-3.10

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# DATA LOCF
adqsadas_locf <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y') %>% 
   filter(AVISITN %in% c(0, 8, 16, 24)) %>% 
   select(USUBJID, TRTP, AVISIT, AVISITN, AVAL, BASE, CHG)

# DATA WINDOWED
adqsadas_win <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & ITTFL=='Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y') %>% 
   filter(AVISITN %in% c(0, 8, 16, 24)) %>% 
   filter(AVISITN != 0 & DTYPE != 'LOCF') %>% 
   select(USUBJID, TRTP, AVISIT, AVISITN, AVAL, BASE, CHG)

# LONG
adqsadas_l <- 
   bind_rows(
      LOCF     = adqsadas_locf,
      Windowed = adqsadas_win,
      .id = 'SET' ) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')) ) %>% 
   mutate(AVISIT_SET = case_when(
      AVISIT == 'Baseline' ~ AVISIT,
      .default = str_glue('{AVISIT} ({SET})'))) %>% 
   mutate(AVISIT_SET = factor(AVISIT_SET) %>% 
             fct_relevel('Baseline',
                         'Week 8 (Windowed)', 'Week 16 (Windowed)', 'Week 24 (Windowed)',
                         'Week 8 (LOCF)', 'Week 16 (LOCF)', 'Week 24 (LOCF)') ) %>% 
   pivot_longer(cols = c(AVAL, BASE, CHG),
                names_to = 'VAR',
                values_to = 'AVAL',
                values_drop_na = TRUE)

# NEST
adqsadas_n <- adqsadas_l %>% 
   droplevels() %>% 
   nest_by(SET, TRTP, AVISITN, AVISIT, AVISIT_SET, VAR)

# TABLE BY STAT WIDE
stats <- c('N'    = '{length}', 
           'Mean' = '{mean}',
           'Std'  = '{sd}',
           'Med.' = '{median}',
           'Min.' = '{min}',
           'Max.' = '{max}')

adqsadas_t <- adqsadas_n %>% 
   mutate(t = list(
      purrr::imap(
         stats,
         ~data %>%
            tbl_summary(include = 'AVAL', 
                        missing = 'no', 
                        digits = list(AVAL ~ 5),
                        label = list(AVAL ~ str_glue('{AVISIT_SET}')),
                        statistic = ~.x)  %>%
            modify_header(all_stat_cols() ~ stringr::str_glue('**{.y}**'))
         ) %>%
         tbl_merge(tab_spanner = FALSE) %>%
         modify_footnote(~NA))
   )

# TRANS to WIDE
adqsadas_w <- adqsadas_t %>% 
   select(-data) %>% 
   pivot_wider(names_from = VAR,
               values_from = t) %>%
   rowwise() %>% 
   mutate(t_m = ifelse( is.null(CHG) ,
                        list( tbl_merge(tbls = list(AVAL, BASE) ) ),
                        list( tbl_merge(tbls = list(AVAL, BASE, CHG)) ) ) )

# TABLES STACK
adqsadas_s <- adqsadas_w %>% 
   group_by(AVISIT_SET, TRTP) %>% 
   summarise(t_s = list( 
      tbl_stack(t_m) ) )

# PRINT TABLE
with(adqsadas_s,
     tbl_stack( tbls = t_s,
                group_header = TRTP)) %>% 
   modify_fmt_fun(
      update = list(
         starts_with('stat_0_1_') ~ function(x) style_number(as.numeric(x), digits = 0),
         starts_with('stat_0_2_') ~ function(x) style_number(as.numeric(x), digits = 1),
         starts_with('stat_0_3_') ~ function(x) style_number(as.numeric(x), digits = 2),
         starts_with('stat_0_4_') ~ function(x) style_number(as.numeric(x), digits = 1),
         starts_with('stat_0_5_') ~ function(x) style_number(as.numeric(x), digits = 0),
         starts_with('stat_0_6_') ~ function(x) style_number(as.numeric(x), digits = 0)),
      rows = !is.na(stat_0_2_1) ) %>% 
   modify_column_hide(
      columns = c(stat_0_1_2,
                  stat_0_4_2,
                  stat_0_5_2,
                  stat_0_6_2,
                  
                  stat_0_1_3) ) %>% 
   modify_header(
      label ~ '') %>% 
   modify_spanning_header(
      ends_with('_1') ~ '&nbsp;',
      ends_with('_2') ~ '**Baseline**',
      ends_with('_3') ~ '**Change from baseline**')%>% 
   modify_caption(
      '**Table 14-3.10<br>ADAS Cog (11) - Mean and Mean Change from Baseline over Time**') %>% 
   bstfun::bold_italicize_group_labels(bold = TRUE)
Table 14-3.10
ADAS Cog (11) - Mean and Mean Change from Baseline over Time
  Baseline Change from baseline
N Mean Std Med. Min. Max. Mean Std Mean Std Med. Min. Max.
Placebo
Baseline 79 24.1 12.19 21.0 5 61 24.1 12.19




Week 8 (Windowed) 79 25.0 13.10 22.0 5 62 24.1 12.19 0.8 4.81 1.0 -12 16
Week 16 (Windowed) 68 25.1 13.42 21.0 5 63 23.4 11.32 1.7 5.92 2.0 -17 23
Week 24 (Windowed) 65 25.7 13.90 23.0 5 59 23.6 12.13 2.1 5.99 2.0 -11 16
Week 8 (LOCF) 79 25.0 13.10 22.0 5 62 24.1 12.19 0.8 4.81 1.0 -12 16
Week 16 (LOCF) 79 26.1 14.16 23.0 5 63 24.1 12.19 2.0 5.89 2.0 -17 23
Week 24 (LOCF) 79 26.7 13.79 24.0 5 62 24.1 12.19 2.5 5.80 2.0 -11 16
Low Dose
Baseline 81 24.4 12.92 21.0 5 57 24.4 12.92




Week 8 (Windowed) 81 26.2 12.98 25.0 5 62 24.4 12.92 1.8 4.14 2.0 -12 14
Week 16 (Windowed) 42 26.2 12.23 25.0 8 53 25.0 12.52 1.2 4.33 1.0 -8 13
Week 24 (Windowed) 49 25.6 13.81 24.0 7 57 24.4 13.76 1.3 6.05 1.0 -11 17
Week 8 (LOCF) 81 26.2 12.98 25.0 5 62 24.4 12.92 1.8 4.14 2.0 -12 14
Week 16 (LOCF) 81 26.0 13.05 25.0 5 62 24.4 12.92 1.6 4.10 2.0 -9 14
Week 24 (LOCF) 81 26.4 13.18 25.0 6 62 24.4 12.92 2.0 5.55 2.0 -11 17
High Dose
Baseline 74 21.3 11.74 18.0 3 57 21.3 11.74




Week 8 (Windowed) 74 22.3 12.41 19.0 2 62 21.3 11.74 1.0 3.62 1.0 -8 13
Week 16 (Windowed) 40 21.9 12.39 19.5 4 49 21.1 11.79 0.8 4.92 1.0 -11 10
Week 24 (Windowed) 41 21.8 12.38 19.0 3 45 20.1 11.13 1.7 4.74 1.0 -7 13
Week 8 (LOCF) 74 22.3 12.41 19.0 2 62 21.3 11.74 1.0 3.62 1.0 -8 13
Week 16 (LOCF) 74 22.5 12.33 20.0 4 62 21.3 11.74 1.2 4.33 1.0 -11 13
Week 24 (LOCF) 74 22.8 12.48 20.0 3 62 21.3 11.74 1.5 4.26 1.0 -7 13

Table 14-3.11

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtreg, gtsummary)
# MMRM MODEL
pacman::p_load(emmeans, mmrm)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# DATA LOCF
adqsadas <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y' & DTYPE != 'LOCF' & AVISITN > 0) %>% 
   select(USUBJID, TRTP, SITEGR1, AVISIT, AVISITN, AVAL, BASE, CHG) %>% 
   mutate(USUBJID = factor(USUBJID)) %>% 
   mutate(across(c(USUBJID, SITEGR1, AVISIT), factor)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(AVISIT = factor(AVISIT,
                          levels = c( 'Week 8', 'Week 16', 'Week 24') ) )

# EMMEANS & BIND DATA
m <- mmrm(
   CHG ~ SITEGR1 + BASE * AVISIT + TRTP * AVISIT + us(AVISIT | USUBJID),
   data = adqsadas,
   reml = TRUE,
   control = mmrm_control(
      method = 'Kenward-Roger') )

e <- emmeans(m, ~TRTP, calc = c(n = '.wgt.'), infer = TRUE)
c <- contrast(e, method = 'revpairwise') %>% summary(infer = TRUE, adjust = 'none')

d <- bind_rows(
   e %>% data.frame() %>% rename(estimate = emmean),
   c %>% data.frame() %>% rename(TRTP = contrast)) %>% 
   select(-n, -df, -t.ratio)

# WIDE DATA
d_l <- d %>% 
   pivot_wider(values_from = -TRTP,
               names_from = TRTP) %>% 
   janitor::clean_names()

# TABLE
tbl_listing(
   data = d_l ) %>% 
   modify_column_merge('{estimate_placebo} ({se_placebo})') %>% 
   modify_column_merge('{estimate_low_dose} ({se_low_dose})') %>% 
   modify_column_merge('{estimate_high_dose} ({se_high_dose})') %>%
   modify_column_merge('{p_value_low_dose_placebo}<br>{estimate_low_dose_placebo}   ({se_low_dose_placebo})<br>({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})') %>% 
   modify_column_merge('{p_value_high_dose_placebo}<br>{estimate_high_dose_placebo} ({se_high_dose_placebo})<br>({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})') %>%
   modify_column_merge('{p_value_high_dose_low_dose}<br>{estimate_high_dose_low_dose}   ({se_high_dose_low_dose})<br>({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})') %>%
   modify_column_hide(
      c('upper_cl_placebo','upper_cl_low_dose','upper_cl_high_dose',
        'lower_cl_placebo','lower_cl_low_dose','lower_cl_high_dose',
        'p_value_placebo','p_value_low_dose','p_value_high_dose')) %>% 
   modify_fmt_fun(
      list(
         starts_with( c('estimate_', 'upper_', 'lower_') ) ~ function(x) style_number(x, digits = 1),
         starts_with( 'se_') ~ function(x) style_number(x, digits = 2),
         starts_with( 'p_value_') ~ function(x) style_pvalue(x, digits = 3)),
      rows = !is.na(estimate_placebo)) %>% 
   modify_spanning_header(
      c(estimate_low_dose, estimate_high_dose) ~ '**Xanomeline**',
      c(p_value_low_dose_placebo:p_value_high_dose_low_dose) ~ '**p-value / Diff (SE) / (95% CI)**') %>% 
   modify_header(
      estimate_placebo ~ '**Placebo**',
      estimate_low_dose ~ '**Low Dose**',
      estimate_high_dose ~ '**High Dose**',
      p_value_low_dose_placebo  ~ '**Low Dose - <br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose - <br>Placebo**',
      p_value_high_dose_low_dose    ~ '**High Dose - <br>Low Dose**') %>% 
   modify_column_alignment(
      columns = starts_with(c('est','p_val')), 'center') %>% 
   modify_footnote(
      update = list(
         c(p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ '
         Note: The change from baseline is calculated as the post-baseline score minus the baseline score. <br>The
         covariates included in the MMRM model are treatment, site group, time and treatment by time interaction,<br>
         baseline ADAS-Cog (11) score, and baseline ADAS-Cog (11) score by time interaction.' ))%>% 
   modify_caption(
      '**Table 14-3.11<br>ADAS Cog (11) - Repeated Measures Analysis of Change from Baseline to Week 24**')
Table 14-3.11
ADAS Cog (11) - Repeated Measures Analysis of Change from Baseline to Week 24
Placebo Xanomeline p-value / Diff (SE) / (95% CI)
Low Dose High Dose Low Dose -
Placebo
1
High Dose -
Placebo
1
High Dose -
Low Dose
1
1.6 (0.49) 1.5 (0.52) 1.1 (0.55) 0.954
0.0 (0.70)
(-1.4;1.3)
0.555
-0.4 (0.72)
(-1.8;1.0)
0.604
-0.4 (0.74)
(-1.9;1.1)
1 Note: The change from baseline is calculated as the post-baseline score minus the baseline score.
The covariates included in the MMRM model are treatment, site group, time and treatment by time interaction,
baseline ADAS-Cog (11) score, and baseline ADAS-Cog (11) score by time interaction.
Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtreg, gtsummary)
# MMRM MODEL
pacman::p_load(emmeans, mmrm)

# THEME
theme_gtsummary_compact()

# IMPORT DATA
adqsadas_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsadas.xpt')

# DATA LOCF
adqsadas <- adqsadas_orig %>%
   filter(EFFFL == 'Y' & PARAMCD == 'ACTOT' & ANL01FL == 'Y' & DTYPE != 'LOCF' & AVISITN > 0) %>% 
   select(USUBJID, TRTP, SITEGR1, AVISIT, AVISITN, AVAL, BASE, CHG) %>% 
   mutate(USUBJID = factor(USUBJID)) %>% 
   mutate(across(c(USUBJID, SITEGR1, AVISIT), factor)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(AVISIT = factor(AVISIT,
                          levels = c( 'Week 8', 'Week 16', 'Week 24') ) )

# EMMEANS & BIND DATA
m <- mmrm(
   CHG ~ SITEGR1 + BASE + AVISIT + TRTP * AVISIT + us(AVISIT | USUBJID),
   data = adqsadas,
   reml = TRUE,
   control = mmrm_control(
      method = 'Kenward-Roger'))

e <- emmeans(m, ~TRTP|AVISIT, calc = c(n = '.wgt.'), infer = TRUE)
c <- contrast(e, method = 'revpairwise') %>% summary(infer = TRUE, adjust = 'none')

d <- bind_rows(
   e %>% data.frame() %>% rename(estimate = emmean),
   c %>% data.frame() %>% rename(TRTP = contrast)) %>% 
   select(-n, -df, -t.ratio)

# WIDE DATA
d_l <- d %>% 
   pivot_wider(id_cols = AVISIT,
               values_from = -c(TRTP, AVISIT),
               names_from = TRTP) %>% 
   janitor::clean_names()

# TABLE
tbl_listing(
   data = d_l ) %>% 
   modify_column_merge('{estimate_placebo} ({se_placebo})') %>% 
   modify_column_merge('{estimate_low_dose} ({se_low_dose})') %>% 
   modify_column_merge('{estimate_high_dose} ({se_high_dose})') %>%
   modify_column_merge('{p_value_low_dose_placebo}<br>{estimate_low_dose_placebo}   ({se_low_dose_placebo})<br>({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})') %>% 
   modify_column_merge('{p_value_high_dose_placebo}<br>{estimate_high_dose_placebo} ({se_high_dose_placebo})<br>({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})') %>%
   modify_column_merge('{p_value_high_dose_low_dose}<br>{estimate_high_dose_low_dose}   ({se_high_dose_low_dose})<br>({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})') %>%
   modify_column_hide(
      c('upper_cl_placebo','upper_cl_low_dose','upper_cl_high_dose',
        'lower_cl_placebo','lower_cl_low_dose','lower_cl_high_dose',
        'p_value_placebo','p_value_low_dose','p_value_high_dose')) %>% 
   modify_fmt_fun(
      list(
         starts_with( c('estimate_', 'upper_', 'lower_') ) ~ function(x) style_number(x, digits = 1),
         starts_with( 'se_') ~ function(x) style_number(x, digits = 2),
         starts_with( 'p_value_') ~ function(x) style_pvalue(x, digits = 3)),
      rows = !is.na(estimate_placebo)) %>% 
   modify_header( avisit ~ 'Week') %>% 
   modify_spanning_header(
      c(estimate_low_dose, estimate_high_dose) ~ '**Xanomeline**',
      c(p_value_low_dose_placebo:p_value_high_dose_low_dose) ~ '**p-value / Diff (SE) / (95% CI)**') %>% 
   modify_header(
      estimate_placebo ~ '**Placebo**',
      estimate_low_dose ~ '**Low Dose**',
      estimate_high_dose ~ '**High Dose**',
      p_value_low_dose_placebo  ~ '**Low Dose - <br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose - <br>Placebo**',
      p_value_high_dose_low_dose    ~ '**High Dose - <br>Low Dose**') %>% 
   modify_column_alignment(
      columns = starts_with(c('est','p_val')), 'center') %>% 
   modify_footnote(
      update = list(
         c(p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ '
         Note: The change from baseline is calculated as the post-baseline score minus the baseline score. <br>The
         covariates included in the MMRM model are treatment, site group, time and treatment by time interaction,<br>
         baseline ADAS-Cog (11) score, and baseline ADAS-Cog (11) score by time interaction.' ))%>% 
   modify_caption(
      '**Table 14-3.11<br>ADAS Cog (11) - Repeated Measures Analysis of Change from Baseline to Week 8, 16 and 24**')
Table 14-3.11
ADAS Cog (11) - Repeated Measures Analysis of Change from Baseline to Week 8, 16 and 24
Week Placebo Xanomeline p-value / Diff (SE) / (95% CI)
Low Dose High Dose Low Dose -
Placebo
1
High Dose -
Placebo
1
High Dose -
Low Dose
1
Week 8 0.6 (0.48) 1.6 (0.47) 0.8 (0.49) 0.107
1.0 (0.65)
(-0.2;2.3)
0.757
0.2 (0.67)
(-1.1;1.5)
0.205
-0.8 (0.66)
(-2.2;0.5)
Week 16 1.8 (0.64) 1.2 (0.76) 1.1 (0.79) 0.587
-0.5 (0.98)
(-2.5;1.4)
0.488
-0.7 (1.00)
(-2.7;1.3)
0.882
-0.2 (1.09)
(-2.3;2.0)
Week 24 2.3 (0.68) 1.7 (0.76) 1.5 (0.82) 0.550
-0.6 (1.01)
(-2.6;1.4)
0.441
-0.8 (1.06)
(-2.9;1.3)
0.848
-0.2 (1.11)
(-2.4;2.0)
1 Note: The change from baseline is calculated as the post-baseline score minus the baseline score.
The covariates included in the MMRM model are treatment, site group, time and treatment by time interaction,
baseline ADAS-Cog (11) score, and baseline ADAS-Cog (11) score by time interaction.
Warning

I successfully reproduced the results, unlike the attempt by atorus-research, by using the newer mmrm package.

Tip

The title of the table suggests a ‘… change from baseline to week 24’; however, as slophaven pointed out, the numbers presented do not correspond to changes up to week 24 but rather represent the average changes observed at weeks 8, 16, and 24. This discrepancy suggests that the intended focus of this analysis was on the interaction, which we have accordingly replicated here.

Table 14-3.12

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gt, gtsummary)
pacman::p_load(emmeans)

# THEME
theme_gtsummary_compact()

# OPTIONS
theme_gtsummary_language(
   language = 'en',
   decimal.mark = '.',
   ci.sep = '; ',
   iqr.sep = '; ',
   set_theme = TRUE
)

# IMPORT DATA
adqsnpix_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqsnpix.xpt')

# SUBSET
adqsnpix <- adqsnpix_orig %>%
   filter(EFFFL == 'Y' & ITTFL == 'Y' & PARAMCD == 'NPTOTMN') %>%
   filter(AVISITN %in% c(0, 98)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   select(USUBJID, SITEGR1, TRTP, AVISITN, AVAL)

# TRANSFORM
adqsnpix <- adqsnpix %>% 
   mutate(AVISITN = factor(AVISITN, labels = c('BASE','AVAL'))) %>% 
   pivot_wider(names_from = AVISITN,
               values_from = AVAL)

# REGRESSION
lm_pairwise <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'), ...) {
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), data = data)
   e <- l %>% 
      emmeans::emmeans(specs = t, calc = c(n = '.wgt.')) %>% 
      emmeans::contrast(method = 'revpairwise') %>% 
      summary(infer = TRUE, adjust = 'none')
   b <- e %>% 
      select( -df, -t.ratio) %>% 
      rename(label = contrast) %>% 
      pivot_wider(names_from = label,
                  values_from = -1) %>% 
      janitor::clean_names()
   b
}

lm_trend  <- function(data, variable, by, adj.vars = c('BASE','SITEGR1'),...){
   f <- str_glue('{variable} ~ {paste(adj.vars,  collapse = " + ")} + {by}')
   t <- str_glue('~ {by}') %>% as.formula()
   l <- lm(as.formula(f), 
           data = data %>% 
              mutate(!!sym(by) := factor(!!sym(by), labels = c(0, 54, 81)) %>% 
                        as.character() %>% 
                        as.numeric() ) )
   b <- l %>% broom::tidy(conf.int = TRUE) %>% slice(n())
   b %>% select(p.value)
}

# SUMMARY w/ PAIRWISE
tbl_summary(
   data = adqsnpix,
   by = TRTP,
   include = c(BASE, AVAL),
   statistic = list(everything() ~ c('{N_nonmiss}<br>{mean} ({sd})<br>{median} ({min};{max})')),
   digit = list(everything() ~ c(0, 1, 2, 1, 0, 0)),
   missing = 'no',
   label = list(BASE ~ 'Baseline',
                AVAL ~ 'Mean of Weeks 4-24')) %>% 
   add_stat_label(
      label = all_continuous() ~ c('n<br>Mean (SD)<br>Median (Range)') ) %>% 
   add_stat(fns = AVAL ~ lm_trend ) %>% 
   add_stat(fns = AVAL ~ lm_pairwise) %>% 
   modify_fmt_fun( list(
      starts_with('p.value')    ~ function(x) style_pvalue(x, digits = 3) ,
      starts_with('estimate_')  ~ function(x) style_number(x, digits = 1),
      starts_with('se_')        ~ function(x) style_number(x, digits = 2),
      starts_with('lower_cl')   ~ function(x) style_number(x, digits = 1),
      starts_with('upper_cl')   ~ function(x) style_number(x, digits = 1))) %>% 
   modify_column_merge(
      pattern = '{p_value_low_dose_placebo}<br>
      {estimate_low_dose_placebo} ({se_low_dose_placebo})<br>
      ({lower_cl_low_dose_placebo};{upper_cl_low_dose_placebo})<br>',
      rows = !is.na(estimate_low_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_placebo}<br>
      {estimate_high_dose_placebo} ({se_high_dose_placebo})<br>
      ({lower_cl_high_dose_placebo};{upper_cl_high_dose_placebo})<br>',
      rows = !is.na(estimate_high_dose_placebo)) %>% 
   modify_column_merge(
      pattern = '{p_value_high_dose_low_dose}<br>
      {estimate_high_dose_low_dose} ({se_high_dose_low_dose})<br>
      ({lower_cl_high_dose_low_dose};{upper_cl_high_dose_low_dose})<br>',
      rows = !is.na(estimate_high_dose_low_dose)) %>% 
   modify_header(
      label = '',
      all_stat_cols() ~ '**{level}<br>(N={n})**',
      p.value ~ '**Trend Test**',
      p_value_low_dose_placebo ~ '**Low Dose -<br>Placebo**', 
      p_value_high_dose_placebo ~ '**High Dose -<br>Placebo**',
      p_value_high_dose_low_dose ~ '**High Dose -<br>Low Dose**') %>%    
   modify_spanning_header(
      c(stat_2, stat_3) ~ '<br><br>**Xanomeline**',
                p.value ~ '<br><br>**p-value**',
      starts_with('p_value_') ~ '**p-value<br>Diff of LS Means (SE)<br>95% CI**') %>% 
   modify_footnote(
      update = list(
         c(p.value, 
           p_value_low_dose_placebo,
           p_value_high_dose_placebo,
           p_value_high_dose_low_dose) ~ 'Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.<br>
                                          Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons<br>
                                          Note that only assessments falling within the assessment window are included in the summary for a visit.',
         c(p.value)                    ~ 'Test for a non-zero coefficient for treatment (dose) as a continuous variable.') ) %>% 
   modify_caption(
      '**Table 14-3.12<br>Mean NPI-X Total Score from Week 4 through Week 24 - Windowed**') %>% 
   as_gt() %>% 
   gt::fmt_markdown(columns = -c(p.value))
Table 14-3.12
Mean NPI-X Total Score from Week 4 through Week 24 - Windowed
Placebo
(N=79)


Xanomeline


p-value
p-value
Diff of LS Means (SE)
95% CI
Low Dose
(N=81)
High Dose
(N=74)
Trend Test1 Low Dose -
Placebo
2
High Dose -
Placebo
2
High Dose -
Low Dose
2

Baseline, n
Mean (SD)
Median (Range)

79
9.5 (12.10)
5.0 (0;66)

81
8.7 (9.82)
4.0 (0;32)

74
11.9 (13.70)
8.0 (0;61)





Mean of Weeks 4-24, n
Mean (SD)
Median (Range)

76
9.1 (11.82)
4.2 (0;67)

69
7.7 (10.09)
3.3 (0;49)

65
7.8 (10.54)
2.8 (0;42)

0.142

0.594
-0.6 (1.14)
(-2.9;1.6)

0.119
-1.8 (1.16)
(-4.1;0.5)

0.311
-1.2 (1.19)
(-3.5;1.1)

1 Test for a non-zero coefficient for treatment (dose) as a continuous variable.
2 Based on Analysis of covariance (ANCOVA) model with treatment and site group as factors and baseline value as a covariate.
Pairwise comparison with treatment as a categorical variable: p-values without adjustment for multiple comparisons
Note that only assessments falling within the assessment window are included in the summary for a visit.
Warning

Table 14-3.12. Difference in values of Mean of Weeks 4-24. This was programmed using the derived NPTOTMN variable. The ARM in the original CDISC Pilot Define.xml was followed as best as possible to determine the subset. This means that the counts are a discrepancy with the original CDISC Pilot analysis data, which is no longer available, therefore we were not able to investigate the discrepancy with the current PHUSE CDISC Pilot replication data. The subsequent statistical summaries therefore also have differences.

Table 14-3.13

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_compact()

# DATA LOCF
adqscibc_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adqscibc.xpt') 

adqscibc <- adqscibc_orig %>% 
   filter(EFFFL == 'Y' & ITTFL == 'Y', AVISITN %in% c(8, 16, 24) & ANL01FL=='Y') %>%
   select(USUBJID, TRTP, SITEGR1, AVISIT, AVISITN, AVAL) %>% 
   mutate(AVALC = factor(AVAL, levels = c(1:7) ) %>% 
             fct_recode('Marked improvement'   = '1',
                        'Moderate improvement' = '2',
                        'Minimal improvement'  = '3',
                        'No Change'            = '4',
                        'Minimal worsening'    = '5',
                        'Moderate worsening'   = '6',
                        'Marked worsening'     = '7') ) %>% 
   mutate(across(c(USUBJID, SITEGR1, AVISIT), factor)) %>% 
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(AVISIT = factor(AVISIT,
                          levels = c( 'Week 8', 'Week 16', 'Week 24') ) )

# cMH TEST
mntl_test <- function (data, variable, by, strata = 'SITEGR1', ...) {
   expr(
      stats::mantelhaen.test(
         !!data[[variable]], 
         as.factor(!!data[[by]]),
         as.factor(!!data[[strata]]) )) %>% 
      eval() %>% 
      broom::tidy() %>% 
      select(p.value, method)
}
# mntl_test(adqscibc %>% filter(AVISIT == 'Week 8'), 'AVAL', 'TRTP', strata = 'SITEGR1')

# SUMMARY
t_summary <- adqscibc %>% 
   tbl_strata2(
      strata = AVISIT,
      .tbl_fun = 
         ~ .x %>% 
         tbl_summary(
            include = AVALC,
            by = TRTP,
            label = list(AVALC = .y)) %>% 
         add_p( 
            test = AVALC ~ 'mntl_test',
            pvalue_fun = \(.) style_pvalue(., digits = 3)),
      .combine_with = 'tbl_stack',
      .combine_args = list(group_header = NULL),
      .quiet = TRUE)

# CROSS
adqscibc %>% 
   tbl_strata2(
      strata = AVISIT,
      .tbl_fun = 
         ~ .x %>% 
         tbl_cross(
            label = list(AVALC = .y ),
            row = AVALC,
            col = TRTP, 
            digits = c(0, 0),
            percent = 'column',
            margin = 'row',
            margin_text = 'n') %>%
         add_p( pvalue_fun = \(.) style_pvalue(., digits = 3)),
      .combine_with = 'tbl_stack',
      .combine_args = list(group_header = NULL)) %>%
   modify_table_body(
      ~ .x %>%
         mutate(across(starts_with('stat_'), ~ifelse(var_label=='n', str_replace(.x, '\\(100%\\)', ''), .x) ) ) %>% 
         mutate(across(starts_with('stat_'), ~ifelse(var_label!='n', str_replace(.x, '\\(0%\\)', ''), .x) ) ) %>% 
         rows_update(t_summary$table_body %>% filter(!is.na(p.value)),
                     by = c('tbl_id1', 'var_label', 'row_type', 'label') ) %>%
         slice(c(1,n(),2:8), .by = tbl_id1)) %>%
   modify_header( label ~ '**Assessment**',
                  all_stat_cols() ~ '**{level}**',
                  p.value         ~ '**p-value**') %>% 
   modify_spanning_header(
      stat_1            ~ '&nbsp;',
      c(stat_2, stat_3) ~ '**Xanomeline**' ) %>%
   modify_column_alignment(all_stat_cols(), 'left') %>% 
   # modify_column_indent(columns = label, undo = TRUE) %>% 
   modify_footnote(
      p.value ~ 'Overall comparison of treatments using CMH test (Pearson Chi-Square), controlling for site group.') %>% 
   modify_caption(
      '**Table 14-3.13<br>CIBIC+ - Categorical Analysis - LOCF**')
Table 14-3.13
CIBIC+ - Categorical Analysis - LOCF
Assessment   Xanomeline p-value1
Placebo Low Dose High Dose
Week 8


0.478
n 77 81 73
    Marked improvement 0 0 0
    Moderate improvement 1 (1%) 2 (2%) 1 (1%)
    Minimal improvement 19 (25%) 16 (20%) 13 (18%)
    No Change 45 (58%) 48 (59%) 38 (52%)
    Minimal worsening 10 (13%) 14 (17%) 20 (27%)
    Moderate worsening 2 (3%) 1 (1%) 1 (1%)
    Marked worsening 0 0 0
Week 16


0.770
n 79 81 74
    Marked improvement 0 0 0
    Moderate improvement 0 3 (4%) 2 (3%)
    Minimal improvement 12 (15%) 12 (15%) 13 (18%)
    No Change 41 (52%) 46 (57%) 39 (53%)
    Minimal worsening 25 (32%) 19 (23%) 20 (27%)
    Moderate worsening 1 (1%) 1 (1%) 0
    Marked worsening 0 0 0
Week 24


0.868
n 79 81 74
    Marked improvement 0 0 0
    Moderate improvement 1 (1%) 1 (1%) 0
    Minimal improvement 9 (11%) 14 (17%) 11 (15%)
    No Change 38 (48%) 37 (46%) 33 (45%)
    Minimal worsening 28 (35%) 27 (33%) 25 (34%)
    Moderate worsening 3 (4%) 2 (2%) 5 (7%)
    Marked worsening 0 0 0
1 Overall comparison of treatments using CMH test (Pearson Chi-Square), controlling for site group.
Important

I was unable to reproduce the Cochran-Mantel-Haenszel Chi-Squared Test for this table. However, by using the original data from atorus-research, I could replicate my findings, leading me to believe that there may be an error in the original table.

Table 14-4

Table 14-4-01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# Read in ADSL
adsl_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt') 


# STACK
adsl_l <- bind_rows(
   list(
      COMP24FL =  adsl_orig %>% filter(COMP24FL == 'Y') , 
      SAFFL    =  adsl_orig %>% filter(SAFFL == 'Y')
   ),
   .id = 'POP') %>% 
   select(POP, USUBJID, TRT01P, AVGDD, CUMDOSE) %>% 
   mutate(TRT01P = factor(TRT01P, 
                          levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                          labels = c('Placebo', 'Xanomeline<br>Low Dose','Xanomeline<br>High Dose'))) %>% 
   mutate(POP = factor(POP,
                       labels = c('**Completers at Week 24** <br>',
                                  '**Safety Population** <br> <small>Includes completers and early terminators</small>') ))

# TABLE
adsl_l %>%
   tbl_strata(
      strata = POP,
      .tbl_fun =
         ~ .x %>%
         tbl_summary(
            by = TRT01P,
            include = c(AVGDD, CUMDOSE),
            label = list(AVGDD ~ 'Average daily dose (mg)',
                         CUMDOSE ~ 'Cumulative dose at end of study'),
            type = list(everything() ~ 'continuous2'),
            digits    = list(everything() ~ c(            0,        1,      2,          1,       1,       1)),
            statistic = list(everything() ~ c('{N_nonmiss}', '{mean}', '{sd}', '{median}', '{min}', '{max}'))) %>% 
         modify_header(
            label = '',
            all_stat_cols() ~ '**{level}<br>(n={n})**') %>% 
         modify_table_body(
            ~ .x %>%
               mutate(label = ifelse(row_type == 'level', str_to_lower(label), label) ) %>% 
               mutate(label = case_when(
                  label == 'minimum' ~ 'min',
                  label == 'maximum' ~ 'max',
                  .default = label))),
      .header = '**{strata}<br>(n={n})**',
      .combine_with = 'tbl_merge') %>% 
   modify_table_styling(
      columns = label,
      rows =  variable == 'CUMDOSE' & row_type  == 'label',
      footnote = 'End of Study refers to week 26/Early Termination.') %>% 
   modify_caption(
      '**Table 14-4.01<br> Summary of Planned Exposure to Study Drug, as of End of Study**' )
Table 14-4.01
Summary of Planned Exposure to Study Drug, as of End of Study
Completers at Week 24

(n=118)
Safety Population
Includes completers and early terminators
(n=254)
Placebo
(n=60)
Xanomeline
Low Dose
(n=28)
Xanomeline
High Dose
(n=30)
Placebo
(n=86)
Xanomeline
Low Dose
(n=84)
Xanomeline
High Dose
(n=84)
Average daily dose (mg)





    n 60 28 30 86 84 84
    mean 0.0 54.0 77.0 0.0 54.0 71.6
    sd 0.00 0.00 0.58 0.00 0.00 8.11
    median 0.0 54.0 76.9 0.0 54.0 75.1
    min 0.0 54.0 76.1 0.0 54.0 54.0
    max 0.0 54.0 78.6 0.0 54.0 78.6
Cumulative dose at end of study1





    n 60 28 30 86 84 84
    mean 0.0 9918.6 14089.5 0.0 5347.3 7551.0
    sd 0.00 603.84 481.01 0.00 3680.35 5531.04
    median 0.0 9936.0 14080.5 0.0 4455.0 5778.0
    min 0.0 7884.0 12960.0 0.0 108.0 54.0
    max 0.0 11448.0 15417.0 0.0 11448.0 15417.0
1 End of Study refers to week 26/Early Termination.

Table 14-5

Table 14-5-01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary, gtreg)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ ADSL
adsl_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt') 

adsl <- adsl_orig %>% 
   filter(SAFFL == 'Y') %>% 
   rename(TRTA = TRT01A) %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')))

# READ ADAE
adae_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adae.xpt') 

adae <- adae_orig %>% 
   filter(SAFFL == 'Y' & TRTEMFL == 'Y') %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(across(c(AEBODSYS, AETERM), ~str_to_title(.x))) 
   # %>% filter(AEBODSYS %in% c('Cardiac Disorders') )

# COMPLETE DATA AE SOC
cmpl_ <- .complete_ae_data(
   data = adae,
   id   = "USUBJID",
   ae   = "AETERM",
   soc  = "AEBODSYS",
   strata = 'TRTA',
   id_df = adsl) %>% 
   mutate(across(c(`..soc..`,`..ae..`), ~ factor(.x)) ) 

# COMPLETE DATA TT
cmpl_tt <- .complete_ae_data(
   data = adae %>% mutate(AETERM   = 'Any Body System',
                          AEBODSYS = 'Any Body System'),
   id   = "USUBJID",
   ae   = "AETERM",
   soc  = "AEBODSYS",
   strata = 'TRTA',
   id_df = adsl) %>% 
   mutate(across(c(`..soc..`,`..ae..`), ~ factor(.x)) ) 

# LONG w/ WITH TOTALS
cmpl_l <- 
   bind_rows(cmpl_,
             cmpl_tt) %>% 
   pivot_longer(cols = c(`..soc..`,`..ae..`)) %>% 
   mutate(ae = ifelse(name == '..soc..', str_glue('All {soc}'), str_glue('{ae}')) ) %>% 
   nest_by(name, soc, ae) %>% 
   filter(! (name == '..ae..' & ae == 'Any Body System')) %>% 
   arrange(soc, ae)


cmpl_tbl <- cmpl_l %>% 
   mutate(
      t_n  = list(
         tbl_summary(
            data = data,
            by = strata,
            include = `by`,
            value = list(`by` ~ 'Overall'),
            label = list(`by` ~ ae),
            statistic = list(everything() ~ '[{n}]')
         ) 
      ),
      t_3g = list( 
         tbl_summary(
            data = data %>% filter(value == TRUE),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) 
      ),
      t_PvsL = list(
         tbl_summary(
            data = data %>% 
               filter(value == TRUE) %>% 
               filter(strata %in% c('Placebo','Low Dose')) %>% 
               droplevels(),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) %>% 
            add_p(
               test = list(all_categorical() ~ 'fisher.test'),
               pvalue_fun = function(x) style_pvalue(x, digits = 3) 
            ) %>% 
            add_significance_stars(
               thresholds = c(0.15),
               hide_p = FALSE,
               pattern = "{p.value}{stars}"
            ) 
      ),
      t_PvsH = list(
         tbl_summary(
            data = data %>% 
               filter(value == TRUE) %>% 
               filter(strata %in% c('Placebo','High Dose')) %>% 
               droplevels(),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) %>% 
            add_p(
               test = list(all_categorical() ~ 'fisher.test'),
               pvalue_fun = function(x) style_pvalue(x, digits = 3) 
            ) %>% 
            add_significance_stars(
               thresholds = c(0.15),
               hide_p = FALSE,
               pattern = "{p.value}{stars}"
            ) 
      ),
      t = list(
         tbl_merge( tbls = list(t_3g,
                                t_n,
                                t_PvsL %>% modify_column_hide(all_stat_cols()) ,
                                t_PvsH %>% modify_column_hide(all_stat_cols()) ),
                    tab_spanner = FALSE)
      )
      
   )

# STACK
with(cmpl_tbl,
     tbl_stack(tbls = t,
               quiet = TRUE) ) %>%
   modify_column_merge( "{stat_1_1} {stat_1_2}") %>% 
   modify_column_merge( "{stat_2_1} {stat_2_2}") %>% 
   modify_column_merge( "{stat_3_1} {stat_3_2}") %>% 
   modify_header(
      label = '',
      c(stat_1_1, 
        stat_2_1,
        stat_3_1) ~ '**{level}<br>(N = {n})**',
      p.value_3 ~ '**Placebo v<br>Low Dose**',
      p.value_4 ~ '**Placebo v<br>High Dose**') %>% 
   modify_spanning_header(
      c(stat_2_1, stat_3_1) ~ "**Xanomeline**",
      c(p.value_3, p.value_4) ~ "**Fisher's Exact p-values**") %>% 
   modify_table_body(
      ~ .x %>%
         mutate(stat_3_2_n =  str_extract(stat_3_2 , "\\d+") %>% as.numeric()) %>% 
         mutate(across(starts_with('stat_'), ~str_replace_all(.x, "\\[0\\]", ""))) %>%
         mutate(across(starts_with('stat_'), ~str_replace(., "0 \\(0%\\)", "0"))) %>% 
         slice(c(n(), 1:(n()-1)) ) ) %>% 
   modify_table_body(
      ~ .x %>%
         mutate(row_type = ifelse( label %in% label[!str_detect(label, "^All")], 'level','label') ) ) %>% 
   modify_column_alignment(columns = all_stat_cols(), align = "left") %>% 
   bold_labels() %>% 
   modify_footnote(
      all_stat_cols() ~ 
         "Treatment emergent events are defined as events which start on or after the start of treatment.<br>
      Adverse events are coded using MedDRA.<br>
      Percentages are based on the number of subjects in the safety population within each treatment group.<br>
      [AE] represents the total number of times an event was recorded.",
      starts_with('p.value') ~ 
   "P-values are based on Fisher's Exact test for the comparison of placebo versus each active treatment
      group. An asterisk is appended to p-values that are less than 0.15.") %>% 
   modify_caption('**Table 14-5.01<br>Incidence of Treatment Emergent Adverse Events by Treatment Group**')
Table 14-5.01
Incidence of Treatment Emergent Adverse Events by Treatment Group
Placebo
(N = 86)
1
Xanomeline Fisher’s Exact p-values
Low Dose
(N = 84)
1
High Dose
(N = 84)
1
Placebo v
Low Dose
2
Placebo v
High Dose
2
All Any Body System 65 (76%) [281] 77 (92%) [412] 76 (90%) [433] 0.007* 0.014*
All Cardiac Disorders 12 (14%) [26] 13 (15%) [30] 15 (18%) [30] 0.831 0.534
    Atrial Fibrillation 1 (1.2%) [1] 1 (1.2%) [1] 3 (3.6%) [5] >0.999 0.365
    Atrial Flutter 0 1 (1.2%) [1] 1 (1.2%) [2] 0.494 0.494
    Atrial Hypertrophy 1 (1.2%) [2] 0 0 >0.999 >0.999
    Atrioventricular Block First Degree 1 (1.2%) [1] 1 (1.2%) [1] 0 >0.999 >0.999
    Atrioventricular Block Second Degree 1 (1.2%) [1] 0 0 >0.999 >0.999
    Bradycardia 1 (1.2%) [4] 0 0 >0.999 >0.999
    Bundle Branch Block Left 1 (1.2%) [1] 0 0 >0.999 >0.999
    Bundle Branch Block Right 1 (1.2%) [2] 1 (1.2%) [1] 0 >0.999 >0.999
    Cardiac Disorder 0 0 1 (1.2%) [1]
0.494
    Cardiac Failure Congestive 1 (1.2%) [1] 0 0 >0.999 >0.999
    Myocardial Infarction 4 (4.7%) [4] 2 (2.4%) [4] 4 (4.8%) [8] 0.682 >0.999
    Palpitations 0 2 (2.4%) [2] 0 0.243
    Sinus Arrhythmia 1 (1.2%) [2] 0 0 >0.999 >0.999
    Sinus Bradycardia 2 (2.3%) [2] 7 (8.3%) [10] 8 (9.5%) [12] 0.097* 0.056*
    Supraventricular Extrasystoles 1 (1.2%) [2] 1 (1.2%) [2] 1 (1.2%) [1] >0.999 >0.999
    Supraventricular Tachycardia 0 1 (1.2%) [2] 0 0.494
    Tachycardia 1 (1.2%) [2] 0 0 >0.999 >0.999
    Ventricular Extrasystoles 0 2 (2.4%) [4] 1 (1.2%) [1] 0.243 0.494
    Ventricular Hypertrophy 1 (1.2%) [1] 0 0 >0.999 >0.999
    Wolff-Parkinson-White Syndrome 0 1 (1.2%) [2] 0 0.494
All Congenital, Familial And Genetic Disorders 0 1 (1.2%) [1] 2 (2.4%) [2] 0.494 0.243
    Ventricular Septal Defect 0 1 (1.2%) [1] 2 (2.4%) [2] 0.494 0.243
All Ear And Labyrinth Disorders 1 (1.2%) [2] 2 (2.4%) [2] 1 (1.2%) [1] 0.618 >0.999
    Cerumen Impaction 0 1 (1.2%) [1] 0 0.494
    Ear Pain 1 (1.2%) [2] 0 0 >0.999 >0.999
    Vertigo 0 1 (1.2%) [1] 1 (1.2%) [1] 0.494 0.494
All Eye Disorders 2 (2.3%) [5] 2 (2.4%) [2] 1 (1.2%) [2] >0.999 >0.999
    Conjunctival Haemorrhage 0 1 (1.2%) [1] 0 0.494
    Conjunctivitis 1 (1.2%) [2] 0 0 >0.999 >0.999
    Eye Allergy 1 (1.2%) [1] 0 0 >0.999 >0.999
    Eye Pruritus 1 (1.2%) [1] 0 0 >0.999 >0.999
    Eye Swelling 1 (1.2%) [1] 0 0 >0.999 >0.999
    Vision Blurred 0 1 (1.2%) [1] 1 (1.2%) [2] 0.494 0.494
    Abdominal Discomfort 0 0 1 (1.2%) [1]
0.494
    Abdominal Pain 1 (1.2%) [1] 3 (3.6%) [3] 1 (1.2%) [2] 0.365 >0.999
All Gastrointestinal Disorders 17 (20%) [26] 14 (17%) [22] 20 (24%) [36] 0.692 0.580
    Constipation 1 (1.2%) [1] 0 0 >0.999 >0.999
    Diarrhoea 9 (10%) [10] 4 (4.8%) [5] 4 (4.8%) [4] 0.248 0.248
    Dyspepsia 1 (1.2%) [2] 1 (1.2%) [2] 0 >0.999 >0.999
    Dysphagia 0 1 (1.2%) [1] 0 0.494
    Flatulence 1 (1.2%) [2] 0 0 >0.999 >0.999
    Gastrointestinal Haemorrhage 0 0 1 (1.2%) [1]
0.494
    Gastrooesophageal Reflux Disease 1 (1.2%) [1] 0 0 >0.999 >0.999
    Glossitis 1 (1.2%) [1] 0 0 >0.999 >0.999
    Hiatus Hernia 1 (1.2%) [2] 0 0 >0.999 >0.999
    Nausea 3 (3.5%) [3] 3 (3.6%) [5] 6 (7.1%) [13] >0.999 0.326
    Rectal Haemorrhage 0 1 (1.2%) [2] 0 0.494
    Salivary Hypersecretion 0 0 4 (4.8%) [5]
0.057*
    Stomach Discomfort 0 0 1 (1.2%) [1]
0.494
    Vomiting 3 (3.5%) [3] 3 (3.6%) [4] 7 (8.3%) [9] >0.999 0.208
All General Disorders And Administration Site Conditions 21 (24%) [46] 47 (56%) [118] 40 (48%) [124] <0.001* 0.002*
    Application Site Bleeding 0 1 (1.2%) [1] 0 0.494
    Application Site Dermatitis 5 (5.8%) [9] 9 (11%) [15] 7 (8.3%) [12] 0.277 0.563
    Application Site Desquamation 0 1 (1.2%) [1] 0 0.494
    Application Site Discharge 0 0 1 (1.2%) [1]
0.494
    Application Site Discolouration 0 1 (1.2%) [1] 0 0.494
    Application Site Erythema 3 (3.5%) [3] 12 (14%) [20] 15 (18%) [23] 0.015* 0.002*
    Application Site Induration 1 (1.2%) [1] 0 0 >0.999 >0.999
    Application Site Irritation 3 (3.5%) [7] 9 (11%) [18] 9 (11%) [16] 0.078* 0.078*
    Application Site Pain 0 0 2 (2.4%) [2]
0.243
    Application Site Perspiration 0 0 2 (2.4%) [3]
0.243
    Application Site Pruritus 6 (7.0%) [10] 22 (26%) [32] 22 (26%) [35] <0.001* <0.001*
    Application Site Reaction 1 (1.2%) [2] 0 1 (1.2%) [1] >0.999 >0.999
    Application Site Swelling 0 1 (1.2%) [1] 2 (2.4%) [3] 0.494 0.243
    Application Site Urticaria 0 2 (2.4%) [2] 1 (1.2%) [1] 0.243 0.494
    Application Site Vesicles 1 (1.2%) [2] 4 (4.8%) [5] 6 (7.1%) [6] 0.208 0.062*
    Application Site Warmth 0 1 (1.2%) [2] 0 0.494
    Asthenia 1 (1.2%) [2] 0 1 (1.2%) [1] >0.999 >0.999
    Chest Discomfort 0 0 2 (2.4%) [2]
0.243
    Chest Pain 0 0 2 (2.4%) [2]
0.243
    Chills 1 (1.2%) [3] 1 (1.2%) [2] 1 (1.2%) [1] >0.999 >0.999
    Fatigue 1 (1.2%) [2] 5 (6.0%) [5] 5 (6.0%) [5] 0.115* 0.115*
    Feeling Abnormal 0 0 1 (1.2%) [1]
0.494
    Feeling Cold 0 0 1 (1.2%) [1]
0.494
    Inflammation 0 1 (1.2%) [1] 0 0.494
    Malaise 0 1 (1.2%) [2] 2 (2.4%) [3] 0.494 0.243
    Oedema 0 2 (2.4%) [2] 0 0.243
    Oedema Peripheral 2 (2.3%) [3] 1 (1.2%) [1] 2 (2.4%) [3] >0.999 >0.999
    Pain 0 1 (1.2%) [2] 1 (1.2%) [1] 0.494 0.494
    Pyrexia 2 (2.3%) [2] 0 1 (1.2%) [1] 0.497 >0.999
    Secretion Discharge 0 1 (1.2%) [2] 0 0.494
    Sudden Death 0 1 (1.2%) [1] 0 0.494
    Swelling 0 1 (1.2%) [1] 0 0.494
    Ulcer 0 1 (1.2%) [1] 0 0.494
All Hepatobiliary Disorders 1 (1.2%) [1] 0 0 >0.999 >0.999
    Hyperbilirubinaemia 1 (1.2%) [1] 0 0 >0.999 >0.999
All Immune System Disorders 0 1 (1.2%) [2] 0 0.494
    Hypersensitivity 0 1 (1.2%) [2] 0 0.494
All Infections And Infestations 16 (19%) [35] 9 (11%) [16] 13 (15%) [20] 0.194 0.685
    Bronchitis 1 (1.2%) [1] 0 0 >0.999 >0.999
    Cellulitis 0 1 (1.2%) [1] 0 0.494
    Cervicitis 1 (1.2%) [2] 0 0 >0.999 >0.999
    Cystitis 1 (1.2%) [1] 0 1 (1.2%) [1] >0.999 >0.999
    Ear Infection 2 (2.3%) [4] 0 0 0.497 0.497
    Gastroenteritis Viral 1 (1.2%) [1] 0 0 >0.999 >0.999
    Hordeolum 0 0 1 (1.2%) [1]
0.494
    Influenza 1 (1.2%) [2] 1 (1.2%) [1] 1 (1.2%) [1] >0.999 >0.999
    Localised Infection 1 (1.2%) [2] 0 0 >0.999 >0.999
    Lower Respiratory Tract Infection 0 0 1 (1.2%) [2]
0.494
    Nasopharyngitis 2 (2.3%) [4] 4 (4.8%) [9] 6 (7.1%) [8] 0.441 0.166
    Pneumonia 0 1 (1.2%) [2] 0 0.494
    Rhinitis 0 0 1 (1.2%) [1]
0.494
    Upper Respiratory Tract Infection 6 (7.0%) [12] 1 (1.2%) [2] 3 (3.6%) [5] 0.117* 0.496
    Urinary Tract Infection 2 (2.3%) [4] 0 1 (1.2%) [1] 0.497 >0.999
    Vaginal Mycosis 1 (1.2%) [2] 0 0 >0.999 >0.999
    Viral Infection 0 1 (1.2%) [1] 0 0.494
All Injury, Poisoning And Procedural Complications 4 (4.7%) [9] 5 (6.0%) [12] 5 (6.0%) [8] 0.745 0.745
    Contusion 1 (1.2%) [1] 1 (1.2%) [3] 2 (2.4%) [3] >0.999 0.618
    Excoriation 2 (2.3%) [3] 1 (1.2%) [2] 1 (1.2%) [1] >0.999 >0.999
    Facial Bones Fracture 0 0 1 (1.2%) [1]
0.494
    Fall 1 (1.2%) [2] 2 (2.4%) [2] 1 (1.2%) [1] 0.618 >0.999
    Hip Fracture 1 (1.2%) [2] 0 2 (2.4%) [2] >0.999 0.618
    Joint Dislocation 0 1 (1.2%) [1] 0 0.494
    Skin Laceration 1 (1.2%) [1] 2 (2.4%) [2] 0 0.618 >0.999
    Wound 0 1 (1.2%) [2] 0 0.494
All Investigations 10 (12%) [19] 6 (7.1%) [7] 6 (7.1%) [8] 0.432 0.432
    Biopsy 0 0 1 (1.2%) [1]
0.494
    Biopsy Prostate 0 0 1 (1.2%) [1]
0.494
    Blood Alkaline Phosphatase Increased 1 (1.2%) [1] 0 0 >0.999 >0.999
    Blood Cholesterol Increased 0 0 1 (1.2%) [1]
0.494
    Blood Creatine Phosphokinase Increased 1 (1.2%) [2] 0 0 >0.999 >0.999
    Blood Glucose Increased 0 1 (1.2%) [1] 1 (1.2%) [2] 0.494 0.494
    Blood Urine Present 1 (1.2%) [1] 0 0 >0.999 >0.999
    Body Temperature Increased 0 1 (1.2%) [1] 0 0.494
    Cystoscopy 1 (1.2%) [1] 0 0 >0.999 >0.999
    Electrocardiogram St Segment Depression 4 (4.7%) [4] 1 (1.2%) [2] 0 0.368 0.121*
    Electrocardiogram T Wave Amplitude Decreased 1 (1.2%) [1] 1 (1.2%) [1] 0 >0.999 >0.999
    Electrocardiogram T Wave Inversion 2 (2.3%) [3] 1 (1.2%) [1] 1 (1.2%) [1] >0.999 >0.999
    Heart Rate Increased 1 (1.2%) [2] 0 0 >0.999 >0.999
    Heart Rate Irregular 1 (1.2%) [4] 0 0 >0.999 >0.999
    Nasal Mucosa Biopsy 0 1 (1.2%) [1] 0 0.494
    Weight Decreased 0 0 1 (1.2%) [2]
0.494
All Metabolism And Nutrition Disorders 6 (7.0%) [8] 1 (1.2%) [1] 2 (2.4%) [4] 0.117* 0.278
    Decreased Appetite 1 (1.2%) [2] 0 1 (1.2%) [2] >0.999 >0.999
    Dehydration 1 (1.2%) [1] 0 0 >0.999 >0.999
    Diabetes Mellitus 1 (1.2%) [1] 0 0 >0.999 >0.999
    Food Craving 1 (1.2%) [1] 1 (1.2%) [1] 0 >0.999 >0.999
    Hyponatraemia 1 (1.2%) [1] 0 0 >0.999 >0.999
    Increased Appetite 1 (1.2%) [2] 0 1 (1.2%) [2] >0.999 >0.999
All Musculoskeletal And Connective Tissue Disorders 4 (4.7%) [6] 7 (8.3%) [10] 7 (8.3%) [10] 0.367 0.367
    Arthralgia 1 (1.2%) [1] 2 (2.4%) [4] 1 (1.2%) [1] 0.618 >0.999
    Arthritis 0 0 1 (1.2%) [1]
0.494
    Back Pain 1 (1.2%) [2] 1 (1.2%) [1] 3 (3.6%) [4] >0.999 0.365
    Flank Pain 0 0 1 (1.2%) [1]
0.494
    Muscle Spasms 0 1 (1.2%) [1] 1 (1.2%) [2] 0.494 0.494
    Muscular Weakness 0 1 (1.2%) [2] 0 0.494
    Myalgia 0 0 1 (1.2%) [1]
0.494
    Pain In Extremity 1 (1.2%) [1] 0 0 >0.999 >0.999
    Shoulder Pain 1 (1.2%) [2] 2 (2.4%) [2] 0 0.618 >0.999
All Neoplasms Benign, Malignant And Unspecified (Incl Cysts And Polyps) 0 2 (2.4%) [3] 1 (1.2%) [1] 0.243 0.494
    Colon Cancer 0 1 (1.2%) [1] 0 0.494
    Malignant Fibrous Histiocytoma 0 1 (1.2%) [2] 0 0.494
    Prostate Cancer 0 0 1 (1.2%) [1]
0.494
All Nervous System Disorders 8 (9.3%) [11] 20 (24%) [40] 25 (30%) [41] 0.013* <0.001*
    Amnesia 0 0 1 (1.2%) [2]
0.494
    Balance Disorder 0 1 (1.2%) [3] 0 0.494
    Burning Sensation 0 0 2 (2.4%) [2]
0.243
    Cognitive Disorder 0 0 1 (1.2%) [1]
0.494
    Complex Partial Seizures 0 1 (1.2%) [1] 0 0.494
    Coordination Abnormal 0 1 (1.2%) [1] 0 0.494
    Dizziness 2 (2.3%) [3] 8 (9.5%) [13] 11 (13%) [15] 0.056* 0.009*
    Headache 3 (3.5%) [3] 3 (3.6%) [4] 5 (6.0%) [8] >0.999 0.493
    Hemianopia Homonymous 0 1 (1.2%) [1] 0 0.494
    Hypersomnia 0 0 1 (1.2%) [1]
0.494
    Lethargy 0 1 (1.2%) [1] 1 (1.2%) [1] 0.494 0.494
    Paraesthesia 0 0 1 (1.2%) [1]
0.494
    Paraesthesia Oral 0 1 (1.2%) [1] 0 0.494
    Parkinson's Disease 1 (1.2%) [1] 0 0 >0.999 >0.999
    Parosmia 0 0 1 (1.2%) [2]
0.494
    Partial Seizures With Secondary Generalisation 0 0 1 (1.2%) [1]
0.494
    Psychomotor Hyperactivity 1 (1.2%) [1] 0 0 >0.999 >0.999
    Somnolence 2 (2.3%) [3] 3 (3.6%) [5] 1 (1.2%) [1] 0.680 >0.999
    Stupor 0 1 (1.2%) [1] 0 0.494
    Syncope 0 4 (4.8%) [6] 3 (3.6%) [4] 0.057* 0.118*
    Syncope Vasovagal 0 0 1 (1.2%) [1]
0.494
    Transient Ischaemic Attack 0 2 (2.4%) [3] 1 (1.2%) [1] 0.243 0.494
    Agitation 2 (2.3%) [2] 2 (2.4%) [2] 1 (1.2%) [1] >0.999 >0.999
All Psychiatric Disorders 10 (12%) [12] 10 (12%) [14] 8 (9.5%) [11] >0.999 0.804
    Anxiety 0 3 (3.6%) [4] 0 0.118*
    Completed Suicide 1 (1.2%) [1] 0 0 >0.999 >0.999
    Confusional State 2 (2.3%) [2] 3 (3.6%) [3] 1 (1.2%) [1] 0.680 >0.999
    Delirium 0 0 1 (1.2%) [1]
0.494
    Delusion 1 (1.2%) [1] 0 1 (1.2%) [1] >0.999 >0.999
    Depressed Mood 0 1 (1.2%) [2] 0 0.494
    Disorientation 1 (1.2%) [1] 0 0 >0.999 >0.999
    Hallucination 0 0 1 (1.2%) [1]
0.494
    Hallucination, Visual 0 0 1 (1.2%) [1]
0.494
    Insomnia 2 (2.3%) [3] 0 2 (2.4%) [2] 0.497 >0.999
    Irritability 1 (1.2%) [2] 1 (1.2%) [1] 0 >0.999 >0.999
    Libido Decreased 0 0 1 (1.2%) [1]
0.494
    Listless 0 0 1 (1.2%) [1]
0.494
    Nightmare 0 0 1 (1.2%) [1]
0.494
    Restlessness 0 1 (1.2%) [2] 0 0.494
All Renal And Urinary Disorders 4 (4.7%) [5] 3 (3.6%) [3] 3 (3.6%) [4] >0.999 >0.999
    Calculus Urethral 0 0 1 (1.2%) [1]
0.494
    Dysuria 1 (1.2%) [1] 1 (1.2%) [1] 0 >0.999 >0.999
    Incontinence 0 1 (1.2%) [1] 0 0.494
    Micturition Urgency 1 (1.2%) [1] 1 (1.2%) [1] 1 (1.2%) [2] >0.999 >0.999
    Nephrolithiasis 1 (1.2%) [1] 0 1 (1.2%) [1] >0.999 >0.999
    Pollakiuria 1 (1.2%) [2] 0 0 >0.999 >0.999
All Reproductive System And Breast Disorders 2 (2.3%) [4] 0 1 (1.2%) [1] 0.497 >0.999
    Benign Prostatic Hyperplasia 1 (1.2%) [2] 0 1 (1.2%) [1] >0.999 >0.999
    Pelvic Pain 1 (1.2%) [2] 0 0 >0.999 >0.999
All Respiratory, Thoracic And Mediastinal Disorders 8 (9.3%) [12] 9 (11%) [14] 10 (12%) [22] 0.803 0.626
Allergic Granulomatous Angiitis 0 0 1 (1.2%) [1]
0.494
    Cough 1 (1.2%) [1] 5 (6.0%) [7] 5 (6.0%) [7] 0.115* 0.115*
    Dysphonia 0 1 (1.2%) [1] 0 0.494
    Dyspnoea 1 (1.2%) [1] 1 (1.2%) [1] 1 (1.2%) [1] >0.999 >0.999
    Emphysema 1 (1.2%) [1] 0 0 >0.999 >0.999
    Epistaxis 0 1 (1.2%) [1] 2 (2.4%) [2] 0.494 0.243
    Haemoptysis 1 (1.2%) [2] 0 0 >0.999 >0.999
    Nasal Congestion 3 (3.5%) [3] 1 (1.2%) [1] 3 (3.6%) [4] 0.621 >0.999
    Pharyngeal Erythema 0 0 1 (1.2%) [2]
0.494
    Pharyngolaryngeal Pain 0 1 (1.2%) [1] 1 (1.2%) [1] 0.494 0.494
    Postnasal Drip 1 (1.2%) [2] 0 0 >0.999 >0.999
    Productive Cough 0 0 1 (1.2%) [1]
0.494
    Rales 1 (1.2%) [2] 0 0 >0.999 >0.999
    Respiratory Tract Congestion 0 0 1 (1.2%) [1]
0.494
    Rhinorrhoea 0 1 (1.2%) [2] 1 (1.2%) [2] 0.494 0.494
    Actinic Keratosis 0 0 1 (1.2%) [1]
0.494
All Skin And Subcutaneous Tissue Disorders 20 (23%) [45] 39 (46%) [111] 40 (48%) [104] 0.002* 0.001*
    Alopecia 1 (1.2%) [1] 0 0 >0.999 >0.999
    Blister 0 5 (6.0%) [8] 1 (1.2%) [2] 0.028* 0.494
    Cold Sweat 1 (1.2%) [3] 0 0 >0.999 >0.999
    Dermatitis Contact 0 1 (1.2%) [2] 0 0.494
    Drug Eruption 1 (1.2%) [1] 0 0 >0.999 >0.999
    Erythema 8 (9.3%) [12] 14 (17%) [22] 14 (17%) [22] 0.175 0.175
    Hyperhidrosis 2 (2.3%) [2] 4 (4.8%) [5] 8 (9.5%) [10] 0.441 0.056*
    Pruritus 8 (9.3%) [11] 21 (25%) [31] 26 (31%) [38] 0.008* <0.001*
    Pruritus Generalised 0 1 (1.2%) [4] 1 (1.2%) [1] 0.494 0.494
    Rash 5 (5.8%) [9] 13 (15%) [18] 9 (11%) [15] 0.048* 0.277
    Rash Erythematous 0 1 (1.2%) [1] 0 0.494
    Rash Maculo-Papular 0 0 1 (1.2%) [1]
0.494
    Rash Pruritic 0 1 (1.2%) [2] 2 (2.4%) [3] 0.494 0.243
    Skin Exfoliation 0 1 (1.2%) [2] 0 0.494
    Skin Irritation 3 (3.5%) [4] 6 (7.1%) [13] 5 (6.0%) [8] 0.326 0.493
    Skin Odour Abnormal 0 0 1 (1.2%) [1]
0.494
    Skin Ulcer 1 (1.2%) [2] 0 0 >0.999 >0.999
    Urticaria 0 1 (1.2%) [3] 1 (1.2%) [2] 0.494 0.494
    Alcohol Use 0 0 1 (1.2%) [1]
0.494
All Social Circumstances 0 0 1 (1.2%) [1]
0.494
    Acrochordon Excision 0 0 1 (1.2%) [1]
0.494
All Surgical And Medical Procedures 2 (2.3%) [2] 1 (1.2%) [1] 2 (2.4%) [2] >0.999 >0.999
    Cataract Operation 1 (1.2%) [1] 1 (1.2%) [1] 0 >0.999 >0.999
    Eye Laser Surgery 1 (1.2%) [1] 0 0 >0.999 >0.999
    Skin Lesion Excision 0 0 1 (1.2%) [1]
0.494
All Vascular Disorders 3 (3.5%) [7] 3 (3.6%) [3] 1 (1.2%) [1] >0.999 0.621
    Hot Flush 0 1 (1.2%) [1] 0 0.494
    Hypertension 1 (1.2%) [2] 1 (1.2%) [1] 0 >0.999 >0.999
    Hypotension 2 (2.3%) [3] 1 (1.2%) [1] 0 >0.999 0.497
    Orthostatic Hypotension 1 (1.2%) [2] 0 0 >0.999 >0.999
    Wound Haemorrhage 0 0 1 (1.2%) [1]
0.494
1 Treatment emergent events are defined as events which start on or after the start of treatment.
Adverse events are coded using MedDRA.
Percentages are based on the number of subjects in the safety population within each treatment group.
[AE] represents the total number of times an event was recorded.
2 P-values are based on Fisher’s Exact test for the comparison of placebo versus each active treatment group. An asterisk is appended to p-values that are less than 0.15.
Caution

The order within each AEBODSYS (Body System or Organ Class) is currently alphabetical, but we can apply different sorting methods if desired.

Table 14-5-02

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary, gtreg)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ ADSL
adsl_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt') 

adsl <- adsl_orig %>% 
   filter(SAFFL == 'Y') %>% 
   rename(TRTA = TRT01A) %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose')))

# READ ADAE
adae_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adae.xpt') 

adae <- adae_orig %>% 
   filter(SAFFL == 'Y' & TRTEMFL == 'Y' & AESER == 'Y') %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(across(c(AEBODSYS, AETERM), ~str_to_title(.x))) 

# COMPLETE DATA AE SOC
cmpl_ <- .complete_ae_data(
   data = adae,
   id   = "USUBJID",
   ae   = "AETERM",
   soc  = "AEBODSYS",
   strata = 'TRTA',
   id_df = adsl) %>% 
   mutate(across(c(`..soc..`,`..ae..`), ~ factor(.x)) ) 

# COMPLETE DATA TT
cmpl_tt <- .complete_ae_data(
   data = adae %>% mutate(AETERM   = 'Any Body System',
                          AEBODSYS = 'Any Body System'),
   id   = "USUBJID",
   ae   = "AETERM",
   soc  = "AEBODSYS",
   strata = 'TRTA',
   id_df = adsl) %>% 
   mutate(across(c(`..soc..`,`..ae..`), ~ factor(.x)) ) 

# LONG w/ WITH TOTALS
cmpl_l <- 
   bind_rows(cmpl_,
             cmpl_tt) %>% 
   pivot_longer(cols = c(`..soc..`,`..ae..`)) %>% 
   mutate(ae = ifelse(name == '..soc..', str_glue('All {soc}'), str_glue('{ae}')) ) %>% 
   nest_by(name, soc, ae) %>% 
   filter(! (name == '..ae..' & ae == 'Any Body System')) %>% 
   arrange(soc, ae)

# TABLE
cmpl_tbl <- cmpl_l %>% 
   mutate(
      t_n  = list(
         tbl_summary(
            data = data,
            by = strata,
            include = `by`,
            value = list(`by` ~ 'Overall'),
            label = list(`by` ~ ae),
            statistic = list(everything() ~ '[{n}]')
         ) 
      ),
      t_3g = list( 
         tbl_summary(
            data = data %>% filter(value == TRUE),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) 
      ),
      t_PvsL = list(
         tbl_summary(
            data = data %>% 
               filter(value == TRUE) %>% 
               filter(strata %in% c('Placebo','Low Dose')) %>% 
               droplevels(),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) %>% 
            add_p(
               test = list(all_categorical() ~ 'fisher.test'),
               pvalue_fun = function(x) style_pvalue(x, digits = 3) 
            ) %>% 
            add_significance_stars(
               thresholds = c(0.15),
               hide_p = FALSE,
               pattern = "{p.value}{stars}"
            ) 
      ),
      t_PvsH = list(
         tbl_summary(
            data = data %>% 
               filter(value == TRUE) %>% 
               filter(strata %in% c('Placebo','High Dose')) %>% 
               droplevels(),
            by = strata,
            value = list(`by` ~ 'Overall'),
            include = `by`,
            label = list(`by` ~ ae),
         ) %>% 
            add_p(
               test = list(all_categorical() ~ 'fisher.test'),
               pvalue_fun = function(x) style_pvalue(x, digits = 3) 
            ) %>% 
            add_significance_stars(
               thresholds = c(0.15),
               hide_p = FALSE,
               pattern = "{p.value}{stars}"
            ) 
      ),
      t = list(
         tbl_merge( tbls = list(t_3g,
                                t_n,
                                t_PvsL %>% modify_column_hide(all_stat_cols()) ,
                                t_PvsH %>% modify_column_hide(all_stat_cols()) ),
                    tab_spanner = FALSE)
      )
      
   )

# STACK
with(cmpl_tbl,
     tbl_stack(tbls = t,
               quiet = TRUE) ) %>%
   modify_column_merge( "{stat_1_1} {stat_1_2}") %>% 
   modify_column_merge( "{stat_2_1} {stat_2_2}") %>% 
   modify_column_merge( "{stat_3_1} {stat_3_2}") %>% 
   modify_header(
      label = '',
      c(stat_1_1, 
        stat_2_1,
        stat_3_1) ~ '**{level}<br>(N = {n})**',
      p.value_3 ~ '**Placebo v<br>Low Dose**',
      p.value_4 ~ '**Placebo v<br>High Dose**') %>% 
   modify_spanning_header(
      c(stat_2_1, stat_3_1) ~ "**Xanomeline**",
      c(p.value_3, p.value_4) ~ "**Fisher's Exact p-values**") %>% 
   modify_table_body(
      ~ .x %>%
         mutate(stat_3_2_n =  str_extract(stat_3_2 , "\\d+") %>% as.numeric()) %>% 
         mutate(across(starts_with('stat_'), ~str_replace_all(.x, "\\[0\\]", ""))) %>%
         mutate(across(starts_with('stat_'), ~str_replace(., "0 \\(0%\\)", "0"))) %>% 
         slice(c(n(), 1:(n()-1)) ) ) %>% 
   modify_table_body(
      ~ .x %>%
         mutate(row_type = ifelse( label %in% label[!str_detect(label, "^All")], 'level','label') ) ) %>% 
   modify_column_alignment(columns = all_stat_cols(), align = "left") %>% 
   bold_labels() %>% 
   modify_footnote(
      all_stat_cols() ~ 
         "Treatment emergent events are defined as events which start on or after the start of treatment.<br>
      Adverse events are coded using MedDRA.<br>
      Percentages are based on the number of subjects in the safety population within each treatment group.<br>
      [AE] represents the total number of times an event was recorded.",
      starts_with('p.value') ~ 
         "P-values are based on Fisher's Exact test for the comparison of placebo versus each active treatment
      group. An asterisk is appended to p-values that are less than 0.15.") %>% 
   modify_caption('**Table 14-5.02<br>Incidence of Treatment Emergent Serious Adverse Events by Treatment Group**')
Table 14-5.02
Incidence of Treatment Emergent Serious Adverse Events by Treatment Group
Placebo
(N = 86)
1
Xanomeline Fisher’s Exact p-values
Low Dose
(N = 84)
1
High Dose
(N = 84)
1
Placebo v
Low Dose
2
Placebo v
High Dose
2
All Any Body System 0 1 (1.2%) [1] 2 (2.4%) [2] 0.494 0.243
All Nervous System Disorders 0 1 (1.2%) [1] 2 (2.4%) [2] 0.494 0.243
    Partial Seizures With Secondary Generalisation 0 0 1 (1.2%) [1]
0.494
    Syncope 0 1 (1.2%) [1] 1 (1.2%) [1] 0.494 0.494
1 Treatment emergent events are defined as events which start on or after the start of treatment.
Adverse events are coded using MedDRA.
Percentages are based on the number of subjects in the safety population within each treatment group.
[AE] represents the total number of times an event was recorded.
2 P-values are based on Fisher’s Exact test for the comparison of placebo versus each active treatment group. An asterisk is appended to p-values that are less than 0.15.

Table 14-6

Table 14-6-01

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbc <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbc.xpt') %>% 
   filter(SAFFL == 'Y' & (AVISITN != 99 | (AVISITN == 99 & AENTMTFL=='Y'))) 

adlbh <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbh.xpt') %>% 
   filter(SAFFL == 'Y' & (AVISITN != 99 | (AVISITN == 99 & AENTMTFL=='Y')) &
             !(PARAM %in% c('Anisocytes', 'Poikilocytes', 'Microcytes', 'Macrocytes', 'Polychromasia')))

# BIND
adlb <- bind_rows(
   CHEMISTRY = adlbc,
   HEMATOLOGY = adlbh,
   .id = 'ORIG') %>% 
   select(ORIG, PARCAT1, PARAM, PARAMCD, USUBJID , TRTA, AVISIT, AVISITN, AVAL, CHG) %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(AVISIT = str_trim(AVISIT)) %>% 
   mutate(AVISIT = factor(AVISIT) %>% 
             fct_reorder(AVISITN) ) 

# LONG
adlb_l <- adlb %>% 
   pivot_longer(cols = c(AVAL, CHG),
                names_to = 'VARIABLE',
                values_to = 'VALUE')

# NEST
adlb_n <- adlb_l %>% 
   nest_by(ORIG, PARCAT1, PARAM, PARAMCD, AVISIT, AVISITN, VARIABLE) 

# FUNCTION
add_by_n <- function(data, variable, by, tbl, ...) {
   data %>%
      select(all_of(c(variable, by))) %>%
      dplyr::group_by(.data[[by]]) %>%
      dplyr::summarise_all(~sum(!is.na(.))) %>%
      rlang::set_names(c("by", "variable")) %>%
      dplyr::left_join(
         tbl$df_by %>% select(by, by_col),
         by = "by"
      ) %>%
      mutate(
         by_col = paste0("add_n_", by_col),
         variable = style_number(variable)
      ) %>%
      select(-by) %>%
      tidyr::pivot_wider(names_from = by_col, 
                         values_from = variable)
}

# TABLE
adlb_t <- adlb_n %>% 
   filter(!str_starts(PARAMCD, "_")) %>% 
   mutate(t = case_when(
      VARIABLE == 'AVAL' ~ list( 
         tbl_summary(
            data = data,
            by = TRTA,
            include = VALUE,
            type = list(VALUE ~ 'continuous'),
            label = list(VALUE ~ str_glue('{AVISIT}')),
            statistic = list(VALUE ~ '{mean} ({sd})'),
            digits = list(VALUE ~ c(1, 2)),
            missing = 'no') %>% 
            add_stat(
               fns = everything() ~ add_by_n) %>% 
            modify_header(starts_with("add_n_stat") ~ "**N**") %>%
            modify_table_body(
               ~ .x %>%
                  dplyr::relocate(add_n_stat_1, .before = stat_1) %>%
                  dplyr::relocate(add_n_stat_2, .before = stat_2) %>% 
                  dplyr::relocate(add_n_stat_3, .before = stat_3)) ),
      .default =  list(
         tbl_summary(
            data = data,
            by = TRTA,
            include = VALUE,
            type = list(VALUE ~ 'continuous'),
            label = list(VALUE ~ str_glue('{AVISIT}')),
            statistic = list(VALUE ~ '{mean} ({sd})'),
            digits = list(VALUE ~ c(1, 2)),
            missing = 'no')
      ))
   )

# MERGE
adlb_t2 <- adlb_t %>% 
   select(-data) %>% 
   pivot_wider(
      names_from = VARIABLE,
      values_from = t) %>% 
   rowwise() %>% 
   mutate(
      BOTH = list(
         tbl_merge( tbls = list(AVAL, CHG ),
                    tab_spanner = FALSE) )
   )

# STACK & PRINT TABLE
#+ results = 'asis'

for( i in unique(adlb_t2$ORIG) ) {
   
   with(adlb_t2 %>% filter(ORIG == i),
        tbl_stack(BOTH,
                  group_header = str_glue('{PARAM}'),
                  quiet = TRUE) ) %>%
      modify_table_body(
         ~.x %>% 
            select(
               tbl_id1,
               groupname_col, label,
               add_n_stat_1_1, stat_1_1, stat_1_2,
               add_n_stat_2_1, stat_2_1, stat_2_2,
               add_n_stat_3_1, stat_3_1, stat_3_2,
               everything() ) %>% 
            mutate(across(all_stat_cols(), ~str_replace_all(., "NA \\(NA\\)", "")))
      ) %>% 
      modify_header(
         label ~ '',
         c(stat_1_1, stat_2_1, stat_3_1) ~ '**Mean (SD)**',
         c(stat_1_2, stat_2_2, stat_3_2) ~ '**Change<br>from Bsln<br>Mean (SD)**'
      ) %>% 
      modify_spanning_header(
         c(add_n_stat_1_1, stat_1_1, stat_1_2) ~ '**Placebo**',
         c(add_n_stat_2_1, stat_2_1, stat_2_2) ~ '**Xanomeline Low**',
         c(add_n_stat_3_1, stat_3_1, stat_3_2) ~ '**Xanomeline High**'
      ) %>% 
      modify_footnote(all_stat_cols() ~ NA) %>% 
      modify_table_styling(
         columns = label,
         rows =  variable == 'VALUE' & label == 'End of Treatment',
         footnote = "Last observed value while on treatment (prior to or at Week 24)"
      ) %>% 
      modify_caption(
         str_glue('**Table 14-6.01 {i}<br>Summary Statistics for Continuous Laboratory Values**')
      ) %>% 
      bstfun::bold_italicize_group_labels(bold = TRUE) %>% 
      knitr::knit_print() %>% 
      cat()
   
}
Table 14-6.01 CHEMISTRY
Summary Statistics for Continuous Laboratory Values
Placebo Xanomeline Low Xanomeline High
N Mean (SD) Change
from Bsln
Mean (SD)
N Mean (SD) Change
from Bsln
Mean (SD)
N Mean (SD) Change
from Bsln
Mean (SD)
Alanine Aminotransferase (U/L)
Baseline 86 17.6 (9.22) 82 18.0 (8.72) 84 19.2 (10.05)
Week 2 83 18.0 (12.53) 0.2 (7.90) 80 20.9 (10.55) 2.8 (8.18) 78 21.0 (8.87) 1.6 (6.83)
Week 4 79 18.7 (12.91) 0.7 (8.66) 72 17.5 (7.66) -0.7 (5.08) 72 21.3 (9.51) 2.1 (7.08)
Week 6 73 17.0 (9.92) -0.3 (7.78) 62 17.0 (7.98) -0.8 (4.87) 66 21.2 (9.49) 1.6 (6.11)
Week 8 72 16.7 (9.34) -1.1 (5.05) 60 17.6 (7.86) 0.2 (5.45) 56 22.8 (17.49) 3.2 (17.24)
Week 12 67 18.0 (9.16) 0.0 (8.07) 51 18.5 (12.68) 0.1 (9.27) 50 21.0 (10.18) 0.7 (8.74)
Week 16 68 17.1 (7.39) -0.8 (7.94) 42 17.3 (7.51) 0.7 (5.80) 37 19.6 (7.61) -0.3 (7.58)
Week 20 65 16.1 (6.56) -1.9 (7.49) 30 16.7 (6.33) 0.9 (4.77) 31 19.6 (6.82) -0.3 (8.63)
Week 24 57 17.9 (15.61) -0.3 (16.62) 26 18.2 (9.17) 1.6 (5.66) 30 21.0 (8.70) 0.2 (8.25)
Week 26 57 16.0 (5.98) -2.1 (7.70) 25 17.8 (9.51) 1.5 (6.26) 27 18.9 (7.02) -2.0 (7.01)
End of Treatment1 84 18.1 (16.74) 0.4 (15.40) 82 18.3 (8.26) 0.3 (7.25) 80 19.5 (7.44) 0.1 (8.08)
Albumin (g/L)
Baseline 86 39.8 (2.81) 82 39.8 (2.56) 84 40.3 (2.84)
Week 2 83 38.9 (3.11) -1.0 (2.49) 80 38.7 (3.17) -1.1 (2.71) 78 38.9 (2.76) -1.4 (2.59)
Week 4 79 38.8 (3.29) -1.0 (2.69) 72 38.6 (2.80) -1.2 (2.66) 72 39.1 (3.05) -1.3 (2.70)
Week 6 73 39.1 (2.56) -1.0 (2.25) 62 38.4 (2.60) -1.2 (2.49) 66 39.5 (2.76) -1.0 (2.60)
Week 8 72 39.8 (3.51) -0.4 (2.70) 60 39.1 (2.93) -0.5 (2.73) 56 39.8 (2.33) -0.9 (2.21)
Week 12 67 39.5 (3.49) -0.5 (2.31) 51 38.9 (2.18) -0.9 (2.19) 50 39.8 (2.45) -0.6 (2.77)
Week 16 68 40.4 (3.02) 0.4 (2.45) 42 39.1 (2.98) -0.4 (2.78) 37 39.9 (1.92) -0.7 (2.76)
Week 20 65 39.6 (3.47) -0.5 (2.86) 30 38.6 (2.66) -1.2 (2.55) 31 39.6 (1.85) -1.4 (2.86)
Week 24 57 39.7 (3.34) -0.2 (2.88) 26 40.4 (2.52) 0.4 (2.40) 30 40.5 (2.10) -0.5 (2.65)
Week 26 57 39.8 (3.02) 0.0 (2.26) 25 39.2 (2.39) -1.0 (2.85) 27 40.0 (2.26) -1.2 (2.78)
End of Treatment1 84 39.6 (3.32) -0.2 (2.69) 82 39.2 (2.97) -0.5 (2.64) 80 39.8 (2.48) -0.6 (2.64)
Alkaline Phosphatase (U/L)
Baseline 86 77.7 (58.11) 81 73.3 (20.72) 83 71.0 (38.85)
Week 2 84 77.7 (69.50) 0.1 (15.51) 80 73.0 (21.87) 0.3 (10.25) 78 72.2 (40.22) -0.1 (8.09)
Week 4 82 78.0 (69.43) 0.4 (14.16) 72 73.2 (23.17) -0.3 (11.12) 72 71.3 (40.65) -0.7 (6.88)
Week 6 75 68.4 (21.53) -0.4 (10.40) 64 72.7 (23.15) -1.0 (10.83) 67 71.5 (43.40) -0.2 (12.65)
Week 8 73 70.0 (27.15) 0.0 (10.67) 60 72.9 (23.84) 0.5 (14.09) 56 74.0 (45.60) 0.8 (13.54)
Week 12 67 72.1 (32.73) 1.8 (17.18) 52 69.5 (20.55) -1.5 (9.41) 50 71.9 (46.65) -1.7 (8.53)
Week 16 68 70.6 (29.49) 0.3 (16.98) 42 69.5 (19.43) -1.7 (8.61) 37 74.7 (54.78) -1.4 (8.29)
Week 20 65 72.6 (37.41) 1.9 (25.77) 31 70.6 (22.20) -2.2 (9.90) 31 73.5 (55.23) -2.7 (8.35)
Week 24 56 80.6 (68.06) 10.1 (58.71) 27 72.0 (21.80) -0.4 (8.93) 30 64.4 (17.63) -2.4 (7.44)
Week 26 57 81.0 (79.33) 10.1 (72.41) 25 68.6 (21.08) -4.1 (10.74) 27 61.9 (16.60) -3.9 (7.65)
End of Treatment1 84 84.6 (84.86) 7.1 (49.37) 82 71.6 (23.80) -1.1 (13.09) 80 70.3 (37.91) -1.6 (12.00)
Aspartate Aminotransferase (U/L)
Baseline 86 23.2 (7.50) 82 23.4 (8.24) 84 23.1 (6.61)
Week 2 83 23.6 (12.35) 0.2 (8.96) 80 24.7 (8.06) 1.6 (6.53) 78 23.4 (5.20) 0.4 (5.63)
Week 4 79 23.9 (14.93) 0.5 (11.33) 72 22.3 (6.75) -1.4 (6.40) 72 23.8 (5.85) 0.6 (5.75)
Week 6 73 22.0 (6.40) -0.9 (6.38) 62 22.1 (6.11) -0.4 (4.19) 66 24.1 (7.84) 0.4 (6.10)
Week 8 72 22.3 (7.05) -1.1 (4.84) 60 22.7 (5.95) 0.3 (4.02) 56 25.7 (13.33) 1.6 (13.98)
Week 12 67 22.8 (7.64) -0.6 (7.06) 51 24.2 (15.87) 1.5 (12.39) 50 23.3 (6.11) -1.2 (6.26)
Week 16 68 22.8 (6.42) -0.6 (6.43) 42 22.4 (10.34) 0.6 (7.62) 37 23.1 (5.78) -0.7 (4.17)
Week 20 65 21.9 (5.90) -1.6 (6.07) 30 20.7 (5.74) 0.4 (4.60) 31 24.0 (6.90) -0.3 (6.18)
Week 24 57 25.2 (21.02) 1.2 (20.43) 26 22.4 (10.78) 2.1 (6.58) 30 24.4 (7.29) -0.2 (5.48)
Week 26 57 21.5 (6.99) -2.5 (7.29) 25 22.1 (11.85) 1.4 (7.69) 27 21.6 (5.71) -3.1 (4.17)
End of Treatment1 84 25.1 (21.33) 1.8 (19.03) 82 23.2 (8.21) -0.3 (8.06) 80 22.7 (6.13) -0.4 (6.36)
Bilirubin (umol/L)
Baseline 86 9.7 (3.96) 82 9.4 (4.01) 84 11.0 (5.35)
Week 2 83 10.8 (12.26) 1.1 (10.29) 79 9.4 (4.16) 0.0 (3.08) 78 10.4 (3.94) -0.6 (3.62)
Week 4 79 11.1 (13.57) 1.4 (11.65) 71 9.2 (3.84) -0.3 (2.70) 72 10.9 (5.62) -0.3 (3.07)
Week 6 73 9.6 (3.78) 0.1 (2.88) 62 9.5 (4.03) 0.0 (2.99) 66 10.9 (4.89) -0.6 (3.18)
Week 8 72 9.4 (3.89) -0.4 (3.43) 60 9.6 (4.72) 0.5 (3.11) 56 10.8 (5.21) -0.7 (3.21)
Week 12 67 9.5 (3.56) -0.1 (2.83) 51 8.8 (4.15) 0.1 (2.64) 50 11.5 (6.16) -0.3 (4.78)
Week 16 68 10.0 (3.63) 0.2 (2.68) 42 8.8 (3.91) 0.4 (2.14) 37 11.6 (4.89) -0.7 (4.28)
Week 20 65 10.1 (5.19) 0.3 (3.58) 30 8.8 (4.51) 0.3 (2.82) 31 11.8 (8.69) -0.2 (6.41)
Week 24 55 9.4 (3.39) 0.1 (2.75) 25 10.1 (4.44) 1.1 (2.84) 30 12.3 (6.52) -0.6 (2.89)
Week 26 57 10.0 (4.73) 0.4 (3.57) 25 10.2 (6.21) 1.4 (3.44) 27 12.2 (6.82) -0.6 (5.31)
End of Treatment1 82 11.2 (13.42) 1.4 (11.28) 80 9.8 (4.29) 0.5 (3.08) 80 11.1 (5.36) 0.0 (3.16)
Blood Urea Nitrogen (mmol/L)
Baseline 86 5.5 (1.39) 82 6.4 (1.97) 84 5.8 (1.88)
Week 2 84 5.8 (1.51) 0.3 (1.17) 80 6.6 (1.82) 0.3 (1.53) 78 6.0 (2.06) 0.3 (1.36)
Week 4 82 5.9 (1.47) 0.3 (1.11) 72 6.4 (1.63) 0.0 (1.37) 72 5.9 (1.87) 0.3 (1.30)
Week 6 75 6.1 (1.42) 0.5 (1.16) 64 6.4 (1.76) -0.1 (1.32) 67 6.0 (2.06) 0.3 (1.43)
Week 8 73 5.6 (1.58) 0.0 (1.22) 60 6.3 (1.92) 0.0 (0.95) 56 5.9 (1.93) 0.1 (1.25)
Week 12 67 5.9 (1.61) 0.2 (1.24) 52 6.2 (1.49) 0.0 (1.45) 50 5.9 (1.76) 0.1 (1.50)
Week 16 68 5.8 (1.60) 0.2 (1.28) 42 6.0 (1.62) -0.2 (1.25) 37 5.7 (1.92) 0.2 (1.29)
Week 20 66 5.8 (1.53) 0.1 (1.28) 31 5.6 (1.57) -0.6 (1.39) 31 5.9 (2.25) 0.2 (1.50)
Week 24 57 5.9 (1.32) 0.3 (1.24) 27 6.0 (2.07) 0.0 (1.37) 30 5.3 (1.92) -0.3 (1.37)
Week 26 57 6.1 (1.62) 0.4 (1.36) 25 5.7 (1.93) -0.3 (1.14) 27 5.9 (2.47) 0.3 (1.40)
End of Treatment1 84 5.9 (1.38) 0.3 (1.20) 82 6.6 (1.95) 0.2 (1.37) 80 5.7 (1.97) 0.0 (1.57)
Calcium (mmol/L)
Baseline 86 2.3 (0.09) 82 2.3 (0.11) 84 2.3 (0.10)
Week 2 84 2.3 (0.09) 0.0 (0.10) 80 2.3 (0.12) 0.0 (0.10) 78 2.3 (0.11) 0.0 (0.11)
Week 4 82 2.3 (0.09) 0.0 (0.09) 72 2.3 (0.10) 0.0 (0.08) 72 2.3 (0.10) 0.0 (0.10)
Week 6 75 2.3 (0.09) 0.0 (0.10) 64 2.3 (0.10) 0.0 (0.08) 67 2.3 (0.09) 0.0 (0.10)
Week 8 73 2.3 (0.09) 0.0 (0.09) 60 2.3 (0.12) 0.0 (0.09) 56 2.3 (0.12) 0.0 (0.11)
Week 12 67 2.3 (0.09) 0.0 (0.08) 52 2.3 (0.10) 0.0 (0.07) 50 2.3 (0.09) 0.0 (0.09)
Week 16 68 2.3 (0.10) 0.0 (0.11) 42 2.3 (0.11) 0.0 (0.08) 37 2.3 (0.11) 0.0 (0.12)
Week 20 66 2.3 (0.09) 0.0 (0.09) 31 2.3 (0.10) 0.0 (0.09) 31 2.3 (0.08) 0.0 (0.09)
Week 24 57 2.3 (0.09) -0.1 (0.10) 27 2.3 (0.11) 0.0 (0.12) 30 2.3 (0.10) -0.1 (0.12)
Week 26 57 2.3 (0.10) 0.0 (0.10) 25 2.3 (0.10) 0.0 (0.08) 27 2.3 (0.09) 0.0 (0.10)
End of Treatment1 84 2.3 (0.09) 0.0 (0.10) 82 2.3 (0.10) 0.0 (0.10) 80 2.3 (0.10) 0.0 (0.11)
Chloride (mmol/L)
Baseline 86 105.7 (3.19) 82 105.8 (3.25) 83 105.4 (3.33)
Week 2 84 106.0 (3.12) 0.3 (3.40) 80 105.6 (3.12) 0.0 (3.44) 77 104.4 (3.49) -0.8 (3.88)
Week 4 82 105.6 (3.53) -0.1 (4.24) 72 105.5 (2.88) -0.3 (3.72) 72 105.0 (3.46) -0.4 (3.94)
Week 6 75 105.5 (3.14) -0.1 (3.61) 64 106.3 (3.07) 0.6 (3.78) 67 105.3 (3.09) -0.3 (3.47)
Week 8 73 106.0 (3.34) 0.3 (3.52) 60 105.6 (3.01) -0.2 (3.47) 56 105.2 (3.57) -0.1 (3.71)
Week 12 67 105.3 (2.93) -0.3 (3.75) 52 105.7 (3.21) -0.4 (3.63) 49 105.2 (2.40) -0.1 (2.64)
Week 16 68 105.5 (3.16) 0.0 (3.57) 42 106.2 (2.93) 0.2 (3.17) 37 105.9 (2.66) 0.6 (2.54)
Week 20 65 106.0 (3.68) 0.6 (4.13) 31 106.3 (2.75) -0.1 (2.41) 31 105.4 (3.03) -0.2 (3.69)
Week 24 57 105.5 (3.14) -0.1 (3.82) 27 105.3 (2.52) -1.2 (2.95) 30 105.3 (3.21) -0.1 (3.06)
Week 26 57 106.2 (2.58) 0.8 (3.33) 25 105.7 (2.35) -0.5 (2.72) 27 105.7 (3.44) 0.4 (2.92)
End of Treatment1 84 105.6 (3.42) -0.1 (3.86) 82 105.5 (3.37) -0.1 (3.31) 80 105.0 (3.20) -0.5 (3.17)
Cholesterol (mmol/L)
Baseline 86 5.8 (1.07) 82 5.7 (1.00) 84 5.8 (1.02)
Week 2 84 5.6 (1.02) -0.1 (0.54) 80 5.6 (0.92) -0.1 (0.50) 78 5.6 (0.93) -0.2 (0.53)
Week 4 82 5.5 (0.94) -0.2 (0.57) 72 5.5 (0.97) -0.2 (0.49) 72 5.5 (1.00) -0.3 (0.51)
Week 6 75 5.6 (0.95) -0.1 (0.67) 64 5.4 (0.95) -0.2 (0.55) 67 5.5 (0.91) -0.3 (0.64)
Week 8 73 5.5 (1.02) -0.2 (0.71) 60 5.5 (0.96) -0.2 (0.49) 56 5.5 (0.94) -0.3 (0.54)
Week 12 67 5.5 (0.92) -0.2 (0.57) 52 5.3 (0.90) -0.3 (0.47) 50 5.4 (0.91) -0.3 (0.63)
Week 16 68 5.6 (0.98) -0.1 (0.58) 42 5.3 (1.00) -0.3 (0.48) 37 5.4 (0.94) -0.2 (0.55)
Week 20 66 5.5 (0.94) -0.2 (0.68) 31 5.2 (0.86) -0.4 (0.46) 31 5.3 (0.85) -0.3 (0.49)
Week 24 57 5.5 (1.01) -0.3 (0.68) 27 5.4 (0.94) -0.2 (0.65) 30 5.3 (0.89) -0.3 (0.50)
Week 26 57 5.5 (0.94) -0.3 (0.62) 25 5.2 (0.76) -0.4 (0.64) 27 5.4 (0.90) -0.2 (0.81)
End of Treatment1 84 5.5 (1.02) -0.3 (0.76) 82 5.4 (0.96) -0.3 (0.56) 80 5.4 (0.89) -0.4 (0.60)
Creatine Kinase (U/L)
Baseline 86 86.9 (43.71) 82 100.6 (68.87) 84 104.0 (71.75)
Week 2 83 90.3 (66.11) 2.4 (57.72) 80 106.1 (83.92) 6.0 (84.97) 78 93.8 (53.19) -12.0 (52.92)
Week 4 79 96.9 (124.45) 8.3 (116.54) 72 93.2 (51.67) -7.8 (55.54) 72 100.6 (58.39) -4.8 (53.80)
Week 6 73 88.0 (42.36) -0.3 (40.55) 62 89.0 (44.29) -13.7 (59.25) 66 123.5 (227.36) 17.8 (187.17)
Week 8 72 93.8 (45.84) 1.2 (46.26) 60 90.0 (39.16) -8.3 (61.09) 56 91.8 (52.25) -9.3 (59.25)
Week 12 67 101.8 (78.66) 8.9 (67.77) 51 94.7 (53.59) -7.2 (65.59) 50 96.6 (60.55) -2.8 (66.47)
Week 16 68 104.9 (66.37) 12.7 (52.13) 42 97.5 (56.39) -6.9 (72.02) 37 86.1 (47.70) -1.6 (24.32)
Week 20 65 93.8 (46.56) 1.0 (42.07) 30 111.6 (137.04) 21.5 (136.75) 31 97.5 (68.57) 6.0 (28.57)
Week 24 57 127.4 (207.98) 32.6 (200.00) 26 83.6 (38.60) 0.8 (21.99) 30 90.9 (53.97) -1.1 (23.40)
Week 26 57 94.1 (51.03) 2.0 (46.93) 25 69.9 (23.70) -15.0 (17.45) 27 93.0 (59.94) 1.4 (43.90)
End of Treatment1 84 112.6 (173.34) 24.6 (165.91) 82 98.2 (61.12) -1.9 (67.09) 80 95.1 (56.32) -9.9 (60.94)
Creatinine (umol/L)
Baseline 86 97.7 (17.78) 82 103.5 (20.01) 84 103.7 (19.37)
Week 2 84 99.0 (17.51) 1.4 (8.06) 80 106.2 (21.26) 2.3 (10.51) 78 106.3 (21.18) 2.8 (9.50)
Week 4 82 98.6 (18.56) 1.1 (11.40) 72 105.1 (19.83) 2.0 (8.13) 72 105.8 (20.66) 2.6 (10.43)
Week 6 75 101.7 (18.46) 3.3 (12.14) 64 104.4 (19.85) 1.7 (9.31) 67 105.8 (20.87) 3.2 (9.94)
Week 8 73 99.1 (16.07) 0.0 (9.88) 60 104.3 (21.27) 1.8 (7.54) 56 106.1 (22.24) 3.2 (9.14)
Week 12 67 101.5 (16.53) 2.4 (9.68) 52 101.8 (17.72) 1.8 (7.99) 50 108.2 (20.48) 5.8 (10.95)
Week 16 68 100.4 (16.43) 1.2 (10.57) 42 98.5 (16.63) -1.3 (9.29) 37 102.5 (17.86) 2.2 (9.18)
Week 20 66 100.3 (17.92) 1.3 (11.31) 31 97.2 (17.23) -1.8 (8.17) 31 103.5 (19.66) 3.1 (11.76)
Week 24 57 99.3 (15.85) 0.8 (11.37) 27 99.2 (16.14) 1.7 (7.08) 30 100.2 (19.61) -0.6 (10.36)
Week 26 57 100.0 (18.46) 2.0 (9.30) 25 99.7 (18.84) 2.9 (8.10) 27 101.8 (18.73) 0.3 (8.31)
End of Treatment1 84 98.5 (16.13) 0.8 (10.77) 82 105.6 (19.79) 1.8 (8.60) 80 104.6 (20.50) 1.4 (10.38)
Gamma Glutamyl Transferase (U/L)
Baseline 86 24.9 (49.75) 82 22.2 (15.57) 84 22.8 (17.71)
Week 2 84 24.2 (47.03) -0.8 (5.52) 80 22.6 (15.52) 0.2 (9.48) 78 23.7 (18.17) 0.6 (8.96)
Week 4 82 24.5 (48.68) -0.8 (5.69) 72 21.8 (13.82) -1.1 (7.51) 72 23.3 (16.08) 0.1 (9.29)
Week 6 75 19.9 (15.55) 0.3 (6.11) 64 21.9 (13.72) -0.6 (6.88) 67 21.6 (13.48) -0.5 (6.74)
Week 8 73 20.6 (16.38) 0.1 (6.76) 60 23.6 (15.55) 1.1 (11.36) 56 26.8 (29.96) 4.0 (24.63)
Week 12 67 21.4 (15.04) 0.5 (5.70) 52 21.9 (13.97) -0.7 (8.80) 50 24.7 (21.26) 1.2 (8.19)
Week 16 68 20.8 (13.00) -0.1 (5.65) 42 22.8 (14.86) -0.1 (6.38) 37 23.9 (17.79) -1.3 (6.16)
Week 20 66 20.4 (13.90) -0.5 (5.42) 31 24.1 (17.52) -0.4 (5.90) 31 22.2 (11.28) -0.5 (5.98)
Week 24 57 22.0 (16.68) 0.5 (10.90) 27 23.7 (14.75) 0.5 (7.28) 30 25.5 (22.64) -0.9 (6.48)
Week 26 57 21.4 (13.91) -0.4 (7.08) 25 22.0 (15.31) -1.0 (8.44) 27 23.4 (18.73) -3.4 (7.97)
End of Treatment1 84 25.7 (48.78) 0.7 (9.60) 82 22.4 (14.03) 0.3 (10.36) 80 22.3 (15.66) -0.6 (9.81)
Glucose (mmol/L)
Baseline 86 5.6 (2.14) 82 5.4 (0.94) 84 5.4 (1.34)
Week 2 83 5.6 (1.87) 0.0 (1.37) 80 5.6 (1.75) 0.1 (1.50) 78 6.1 (2.92) 0.7 (2.06)
Week 4 79 5.6 (1.87) 0.0 (1.55) 70 5.4 (1.41) -0.1 (1.09) 72 5.9 (1.84) 0.4 (1.25)
Week 6 73 5.7 (2.22) 0.1 (1.37) 62 5.3 (1.34) -0.1 (1.12) 66 6.0 (2.80) 0.5 (2.20)
Week 8 72 5.5 (1.35) -0.1 (2.09) 59 5.5 (1.76) 0.1 (1.34) 56 5.8 (2.15) 0.2 (1.68)
Week 12 67 6.1 (1.97) 0.4 (1.97) 51 5.9 (3.18) 0.4 (2.78) 49 6.0 (2.28) 0.4 (1.64)
Week 16 68 5.5 (1.42) -0.2 (1.69) 42 5.3 (0.83) -0.2 (0.89) 37 5.9 (2.30) 0.2 (1.74)
Week 20 65 5.8 (1.50) 0.1 (2.08) 30 5.7 (1.73) 0.1 (1.39) 31 5.8 (1.61) 0.2 (1.47)
Week 24 57 5.7 (1.83) -0.1 (2.68) 26 5.7 (1.26) 0.2 (0.82) 30 6.0 (1.92) 0.5 (1.94)
Week 26 57 5.8 (1.85) 0.0 (1.60) 25 5.5 (1.72) 0.1 (1.35) 27 5.6 (1.01) 0.1 (1.66)
End of Treatment1 84 5.6 (1.61) 0.0 (2.26) 82 5.4 (1.07) -0.1 (1.03) 80 5.9 (2.15) 0.5 (1.62)
Phosphate (mmol/L)
Baseline 86 1.2 (0.15) 81 1.2 (0.11) 83 1.2 (0.15)
Week 2 84 1.2 (0.15) 0.0 (0.16) 80 1.2 (0.15) 0.0 (0.16) 78 1.2 (0.18) 0.0 (0.19)
Week 4 82 1.1 (0.16) 0.0 (0.17) 71 1.1 (0.14) 0.0 (0.14) 72 1.2 (0.18) 0.0 (0.16)
Week 6 75 1.2 (0.15) 0.0 (0.17) 64 1.2 (0.17) 0.0 (0.17) 67 1.2 (0.15) 0.0 (0.16)
Week 8 73 1.1 (0.14) 0.0 (0.13) 60 1.1 (0.16) 0.0 (0.16) 56 1.2 (0.16) 0.0 (0.18)
Week 12 67 1.2 (0.16) 0.0 (0.18) 52 1.1 (0.14) 0.0 (0.18) 50 1.1 (0.15) 0.0 (0.17)
Week 16 68 1.1 (0.14) 0.0 (0.15) 42 1.1 (0.17) 0.0 (0.19) 37 1.2 (0.17) 0.0 (0.17)
Week 20 65 1.2 (0.17) 0.0 (0.15) 31 1.1 (0.13) 0.0 (0.14) 31 1.2 (0.15) 0.0 (0.16)
Week 24 56 1.2 (0.15) 0.0 (0.16) 27 1.2 (0.19) 0.1 (0.19) 30 1.1 (0.17) 0.0 (0.17)
Week 26 57 1.2 (0.15) 0.0 (0.18) 25 1.1 (0.17) 0.0 (0.17) 27 1.2 (0.19) 0.0 (0.20)
End of Treatment1 84 1.2 (0.17) 0.0 (0.18) 82 1.2 (0.17) 0.0 (0.18) 80 1.2 (0.16) 0.0 (0.17)
Potassium (mmol/L)
Baseline 86 4.3 (0.43) 81 4.3 (0.34) 82 4.3 (0.41)
Week 2 84 4.2 (0.41) 0.0 (0.37) 80 4.3 (0.41) 0.0 (0.38) 77 4.3 (0.40) 0.0 (0.46)
Week 4 82 4.2 (0.37) -0.1 (0.38) 71 4.3 (0.40) -0.1 (0.36) 72 4.2 (0.37) -0.1 (0.46)
Week 6 75 4.3 (0.33) 0.0 (0.41) 64 4.3 (0.39) -0.1 (0.41) 67 4.2 (0.32) -0.1 (0.44)
Week 8 73 4.2 (0.37) -0.1 (0.43) 60 4.3 (0.39) 0.0 (0.38) 56 4.2 (0.39) 0.0 (0.50)
Week 12 67 4.2 (0.41) 0.0 (0.46) 52 4.2 (0.42) -0.2 (0.40) 49 4.2 (0.26) -0.1 (0.39)
Week 16 68 4.2 (0.41) 0.0 (0.46) 42 4.3 (0.33) -0.1 (0.35) 37 4.3 (0.27) 0.0 (0.35)
Week 20 64 4.3 (0.46) 0.0 (0.44) 31 4.3 (0.36) 0.0 (0.44) 31 4.3 (0.37) 0.0 (0.40)
Week 24 56 4.3 (0.44) 0.1 (0.44) 27 4.3 (0.41) 0.0 (0.35) 30 4.2 (0.42) 0.0 (0.50)
Week 26 57 4.2 (0.38) 0.0 (0.34) 25 4.3 (0.40) 0.0 (0.36) 27 4.3 (0.39) 0.0 (0.54)
End of Treatment1 84 4.3 (0.43) 0.0 (0.41) 82 4.3 (0.43) -0.1 (0.39) 80 4.2 (0.36) -0.1 (0.47)
Protein (g/L)
Baseline 86 70.5 (4.72) 82 70.4 (4.29) 84 71.0 (4.24)
Week 2 84 69.2 (4.93) -1.3 (4.53) 80 69.5 (4.48) -0.9 (3.63) 78 69.5 (4.31) -1.6 (3.91)
Week 4 82 69.0 (4.18) -1.6 (3.93) 72 68.8 (4.30) -1.5 (3.97) 72 69.8 (4.54) -1.4 (3.90)
Week 6 75 69.6 (3.95) -1.2 (4.01) 64 68.8 (4.33) -1.3 (4.17) 67 70.2 (4.28) -1.1 (4.10)
Week 8 73 70.5 (5.20) -0.4 (4.35) 60 69.2 (3.95) -0.9 (3.71) 56 70.6 (3.78) -1.0 (3.57)
Week 12 67 70.0 (4.28) -0.6 (3.86) 52 69.3 (4.98) -0.8 (3.62) 50 70.4 (3.83) -1.0 (4.11)
Week 16 68 71.2 (4.57) 0.4 (4.39) 42 69.3 (4.79) -0.5 (4.27) 37 70.5 (4.93) -1.0 (4.57)
Week 20 66 70.0 (4.92) -1.1 (4.53) 31 68.7 (4.82) -1.3 (3.88) 31 69.5 (4.15) -1.8 (4.13)
Week 24 57 70.1 (4.33) -0.8 (4.81) 27 70.9 (6.11) 0.7 (4.03) 30 70.7 (3.47) -0.7 (3.83)
Week 26 57 70.4 (4.48) -0.5 (4.56) 25 69.4 (5.29) -1.1 (4.26) 27 69.4 (4.89) -2.5 (4.01)
End of Treatment1 84 70.2 (4.26) -0.3 (4.35) 82 70.1 (4.95) -0.4 (3.97) 80 70.4 (3.91) -0.7 (3.63)
Sodium (mmol/L)
Baseline 86 140.3 (2.74) 82 140.0 (2.61) 83 140.0 (3.11)
Week 2 84 140.4 (2.62) 0.0 (3.35) 80 139.6 (2.47) -0.3 (2.86) 77 139.1 (2.74) -0.6 (2.87)
Week 4 82 139.9 (2.74) -0.4 (3.58) 72 140.1 (2.40) 0.2 (3.16) 72 139.7 (2.75) -0.3 (3.39)
Week 6 75 140.3 (2.58) 0.0 (3.08) 64 140.6 (2.69) 0.8 (3.25) 67 140.1 (2.61) -0.2 (3.23)
Week 8 73 140.7 (2.48) 0.4 (3.33) 60 140.5 (2.58) 0.6 (3.33) 56 140.5 (3.15) 0.3 (3.79)
Week 12 67 140.4 (2.39) 0.2 (2.98) 52 141.1 (2.77) 1.1 (3.06) 49 140.2 (2.39) -0.1 (2.79)
Week 16 68 141.1 (2.37) 0.9 (3.10) 42 141.0 (2.62) 1.2 (2.93) 37 141.6 (2.96) 1.4 (3.18)
Week 20 65 141.2 (2.53) 1.1 (3.01) 31 141.4 (2.53) 1.6 (3.42) 31 141.8 (3.87) 1.2 (3.61)
Week 24 57 141.7 (2.23) 1.6 (3.27) 27 141.5 (2.12) 1.2 (3.12) 30 141.6 (2.99) 1.0 (3.10)
Week 26 57 142.6 (2.25) 2.5 (2.90) 25 142.1 (2.08) 1.9 (3.13) 27 142.4 (3.07) 1.9 (3.43)
End of Treatment1 84 141.5 (2.74) 1.1 (3.51) 82 141.1 (2.65) 1.3 (3.09) 80 140.5 (3.22) 0.5 (3.50)
Urate (umol/L)
Baseline 86 285.0 (74.45) 82 300.7 (77.78) 84 302.2 (78.01)
Week 2 84 284.0 (68.72) -1.8 (50.16) 80 305.8 (74.25) 1.0 (40.56) 78 301.7 (81.96) -2.7 (33.85)
Week 4 82 285.6 (69.22) -0.6 (35.59) 72 299.5 (79.44) -3.7 (38.80) 72 291.4 (79.28) -9.5 (42.17)
Week 6 75 288.0 (68.73) -1.1 (38.24) 64 298.1 (74.46) -6.2 (30.89) 67 291.6 (74.82) -9.8 (34.48)
Week 8 73 285.6 (65.63) -5.1 (36.28) 60 290.9 (71.15) -15.6 (30.49) 56 291.6 (76.76) -11.2 (35.13)
Week 12 67 291.1 (70.07) 3.2 (40.20) 52 290.3 (63.20) -11.5 (31.83) 50 294.2 (78.84) -9.0 (36.29)
Week 16 68 285.8 (68.72) -3.6 (41.33) 42 288.8 (58.78) -12.9 (39.98) 37 284.1 (80.47) -19.9 (36.13)
Week 20 66 301.9 (73.43) 12.1 (40.18) 31 279.7 (58.71) -16.5 (44.34) 31 295.1 (85.89) -9.6 (43.81)
Week 24 57 293.5 (73.47) 4.6 (42.98) 27 274.9 (57.72) -19.2 (31.26) 30 288.5 (88.37) -19.4 (50.03)
Week 26 57 291.9 (74.43) 5.1 (44.39) 25 279.3 (60.01) -13.6 (39.12) 27 301.1 (86.31) -12.6 (47.68)
End of Treatment1 84 290.5 (73.86) 4.7 (46.43) 82 298.3 (80.54) -3.1 (37.11) 80 292.4 (80.83) -10.5 (42.35)
1 Last observed value while on treatment (prior to or at Week 24)
Table 14-6.01 HEMATOLOGY
Summary Statistics for Continuous Laboratory Values
Placebo Xanomeline Low Xanomeline High
N Mean (SD) Change
from Bsln
Mean (SD)
N Mean (SD) Change
from Bsln
Mean (SD)
N Mean (SD) Change
from Bsln
Mean (SD)
Basophils (GI/L)
Baseline 85 0.1 (0.04) 81 0.1 (0.03) 81 0.1 (0.02)
Week 2 83 0.0 (0.02) 0.0 (0.03) 80 0.1 (0.03) 0.0 (0.03) 80 0.0 (0.03) 0.0 (0.02)
Week 4 79 0.0 (0.02) 0.0 (0.03) 71 0.0 (0.03) 0.0 (0.03) 71 0.0 (0.02) 0.0 (0.02)
Week 6 73 0.0 (0.03) 0.0 (0.03) 62 0.0 (0.03) 0.0 (0.03) 64 0.1 (0.03) 0.0 (0.03)
Week 8 72 0.0 (0.02) 0.0 (0.03) 59 0.1 (0.02) 0.0 (0.04) 56 0.1 (0.03) 0.0 (0.02)
Week 12 66 0.0 (0.02) 0.0 (0.04) 50 0.0 (0.02) 0.0 (0.03) 50 0.1 (0.03) 0.0 (0.02)
Week 16 68 0.0 (0.03) 0.0 (0.04) 42 0.0 (0.02) 0.0 (0.03) 37 0.1 (0.03) 0.0 (0.02)
Week 20 65 0.0 (0.03) 0.0 (0.03) 30 0.0 (0.02) 0.0 (0.03) 31 0.0 (0.02) 0.0 (0.03)
Week 24 58 0.0 (0.03) 0.0 (0.03) 25 0.0 (0.03) 0.0 (0.02) 30 0.1 (0.02) 0.0 (0.02)
Week 26 57 0.0 (0.02) 0.0 (0.03) 25 0.0 (0.03) 0.0 (0.02) 27 0.1 (0.03) 0.0 (0.03)
End of Treatment1 84 0.0 (0.03) 0.0 (0.03) 82 0.0 (0.03) 0.0 (0.03) 81 0.1 (0.02) 0.0 (0.02)
Eosinophils (GI/L)
Baseline 85 0.1 (0.12) 81 0.1 (0.12) 81 0.1 (0.10)
Week 2 83 0.1 (0.10) 0.0 (0.09) 80 0.2 (0.17) 0.0 (0.14) 80 0.1 (0.11) 0.0 (0.07)
Week 4 79 0.1 (0.13) 0.0 (0.09) 71 0.2 (0.14) 0.0 (0.11) 71 0.2 (0.15) 0.0 (0.13)
Week 6 73 0.1 (0.13) 0.0 (0.11) 62 0.2 (0.21) 0.1 (0.16) 64 0.2 (0.28) 0.1 (0.25)
Week 8 72 0.1 (0.10) 0.0 (0.10) 59 0.3 (0.26) 0.2 (0.23) 56 0.2 (0.25) 0.1 (0.19)
Week 12 66 0.1 (0.09) 0.0 (0.11) 50 0.3 (0.21) 0.1 (0.19) 50 0.2 (0.17) 0.1 (0.14)
Week 16 68 0.1 (0.08) 0.0 (0.10) 42 0.2 (0.16) 0.1 (0.13) 37 0.2 (0.21) 0.1 (0.17)
Week 20 65 0.1 (0.08) 0.0 (0.10) 30 0.2 (0.19) 0.1 (0.16) 31 0.1 (0.11) 0.0 (0.12)
Week 24 58 0.1 (0.08) 0.0 (0.09) 25 0.2 (0.14) 0.0 (0.12) 30 0.2 (0.15) 0.0 (0.13)
Week 26 57 0.1 (0.10) 0.0 (0.11) 25 0.2 (0.15) 0.0 (0.13) 27 0.1 (0.10) 0.0 (0.09)
End of Treatment1 84 0.1 (0.08) 0.0 (0.10) 82 0.2 (0.17) 0.0 (0.14) 81 0.2 (0.20) 0.1 (0.18)
Ery. Mean Corpuscular HGB Concentration (mmol/L)
Baseline 85 20.6 (0.87) 80 20.2 (0.98) 80 20.5 (0.87)
Week 2 83 20.3 (0.77) -0.3 (1.00) 80 20.3 (0.85) 0.0 (1.04) 80 20.4 (0.74) 0.0 (0.98)
Week 4 77 20.2 (0.78) -0.4 (1.01) 69 20.2 (0.72) 0.0 (0.94) 70 20.5 (0.71) -0.1 (0.90)
Week 6 72 20.5 (0.77) -0.2 (0.98) 60 20.1 (0.84) -0.2 (0.99) 65 20.4 (0.89) -0.2 (1.00)
Week 8 71 20.3 (0.68) -0.2 (0.85) 56 20.2 (0.83) 0.0 (0.92) 56 20.5 (0.76) -0.1 (0.93)
Week 12 65 20.3 (0.74) -0.3 (0.96) 49 20.3 (0.68) 0.0 (0.99) 50 20.5 (0.76) -0.1 (1.03)
Week 16 68 20.2 (0.75) -0.4 (1.02) 42 20.0 (0.82) -0.2 (1.02) 37 20.4 (0.83) -0.2 (1.11)
Week 20 64 20.3 (0.80) -0.3 (0.94) 30 20.0 (0.85) -0.4 (0.91) 31 20.3 (0.88) -0.3 (1.22)
Week 24 58 20.1 (0.89) -0.5 (1.01) 25 20.3 (0.91) -0.3 (0.95) 30 20.4 (0.85) -0.3 (1.29)
Week 26 56 20.3 (0.99) -0.4 (1.16) 25 19.9 (0.94) -0.6 (1.09) 27 20.2 (0.92) -0.6 (1.12)
End of Treatment1 83 20.2 (0.85) -0.4 (0.99) 82 20.2 (0.81) 0.0 (0.98) 81 20.4 (0.78) -0.1 (1.08)
Ery. Mean Corpuscular Hemoglobin (fmol(Fe))
Baseline 85 1.9 (0.12) 81 1.9 (0.09) 81 1.9 (0.13)
Week 2 83 1.9 (0.11) 0.0 (0.07) 80 1.9 (0.09) 0.0 (0.07) 80 1.9 (0.13) 0.0 (0.07)
Week 4 79 1.9 (0.12) 0.0 (0.07) 71 1.9 (0.09) 0.0 (0.07) 71 1.9 (0.13) 0.0 (0.06)
Week 6 73 1.9 (0.13) 0.0 (0.08) 62 1.9 (0.08) 0.0 (0.05) 65 1.9 (0.13) 0.0 (0.08)
Week 8 72 1.9 (0.12) 0.0 (0.06) 59 1.9 (0.09) 0.0 (0.06) 56 1.9 (0.10) 0.0 (0.07)
Week 12 66 1.9 (0.12) 0.0 (0.06) 50 1.9 (0.09) 0.0 (0.05) 50 1.9 (0.09) 0.0 (0.07)
Week 16 68 1.9 (0.12) 0.0 (0.05) 42 1.9 (0.10) 0.0 (0.07) 37 1.9 (0.11) 0.0 (0.08)
Week 20 65 1.9 (0.11) 0.0 (0.06) 30 1.9 (0.08) 0.0 (0.06) 31 1.9 (0.12) 0.0 (0.08)
Week 24 58 1.9 (0.12) 0.0 (0.06) 25 1.9 (0.09) 0.0 (0.06) 30 1.9 (0.13) 0.0 (0.09)
Week 26 57 1.9 (0.12) 0.0 (0.07) 25 1.9 (0.08) 0.0 (0.08) 27 1.9 (0.12) 0.0 (0.08)
End of Treatment1 84 1.9 (0.12) 0.0 (0.07) 82 1.9 (0.09) 0.0 (0.06) 81 1.9 (0.13) 0.0 (0.07)
Ery. Mean Corpuscular Volume (fL)
Baseline 85 92.9 (5.69) 80 94.6 (4.90) 80 93.3 (5.91)
Week 2 83 94.1 (5.31) 1.2 (3.93) 80 94.6 (4.12) 0.2 (3.57) 80 93.4 (5.93) 0.2 (3.91)
Week 4 77 94.6 (5.87) 1.7 (3.47) 69 95.1 (4.22) 0.6 (3.69) 70 93.0 (5.52) 0.0 (3.69)
Week 6 72 94.2 (6.21) 1.3 (3.79) 60 95.2 (4.51) 0.7 (3.68) 65 94.0 (6.61) 0.6 (3.99)
Week 8 71 94.3 (5.51) 1.5 (3.27) 56 94.9 (3.97) 0.6 (3.35) 56 94.4 (4.77) 0.4 (4.03)
Week 12 65 94.2 (5.66) 1.6 (3.81) 49 95.1 (4.17) 0.9 (3.91) 50 94.9 (4.90) 0.8 (4.20)
Week 16 68 94.1 (6.01) 1.7 (4.07) 42 95.8 (5.17) 1.2 (4.18) 37 94.7 (6.27) 0.6 (5.05)
Week 20 64 93.3 (5.58) 1.3 (4.14) 30 95.4 (5.43) 2.0 (3.29) 31 95.1 (5.91) 0.9 (5.12)
Week 24 58 93.9 (5.90) 1.6 (3.85) 25 94.3 (3.85) 0.8 (3.94) 30 95.0 (5.99) 0.7 (4.97)
Week 26 56 93.9 (6.40) 1.7 (4.48) 25 95.8 (4.33) 2.6 (3.70) 27 95.4 (6.19) 1.2 (3.93)
End of Treatment1 83 94.4 (5.93) 1.4 (4.07) 82 95.0 (3.98) 0.6 (3.83) 81 93.8 (6.14) 0.7 (4.40)
Erythrocytes (TI/L)
Baseline 85 4.5 (0.45) 81 4.5 (0.42) 81 4.7 (0.47)
Week 2 83 4.4 (0.40) -0.1 (0.22) 80 4.4 (0.43) -0.1 (0.27) 80 4.6 (0.47) -0.1 (0.22)
Week 4 79 4.4 (0.45) -0.1 (0.25) 71 4.4 (0.38) -0.2 (0.30) 71 4.5 (0.49) -0.1 (0.25)
Week 6 73 4.4 (0.37) -0.1 (0.24) 62 4.3 (0.38) -0.2 (0.27) 65 4.5 (0.50) -0.1 (0.24)
Week 8 72 4.4 (0.41) -0.1 (0.26) 59 4.4 (0.34) -0.2 (0.24) 56 4.5 (0.45) -0.1 (0.19)
Week 12 66 4.4 (0.44) -0.1 (0.26) 50 4.3 (0.32) -0.2 (0.20) 50 4.6 (0.47) -0.1 (0.27)
Week 16 68 4.5 (0.41) 0.0 (0.27) 42 4.3 (0.36) -0.1 (0.24) 37 4.6 (0.44) -0.2 (0.26)
Week 20 65 4.5 (0.41) -0.1 (0.30) 30 4.3 (0.32) -0.1 (0.21) 31 4.5 (0.50) -0.2 (0.30)
Week 24 58 4.4 (0.45) -0.1 (0.23) 25 4.4 (0.38) -0.1 (0.19) 30 4.6 (0.58) -0.1 (0.24)
Week 26 57 4.4 (0.42) 0.0 (0.25) 25 4.4 (0.35) -0.1 (0.20) 27 4.5 (0.47) -0.2 (0.27)
End of Treatment1 84 4.4 (0.43) -0.1 (0.27) 82 4.4 (0.38) -0.2 (0.25) 81 4.6 (0.49) -0.1 (0.22)
Hematocrit
Baseline 85 0.4 (0.04) 80 0.4 (0.04) 80 0.4 (0.04)
Week 2 83 0.4 (0.04) 0.0 (0.03) 80 0.4 (0.04) 0.0 (0.03) 80 0.4 (0.04) 0.0 (0.03)
Week 4 77 0.4 (0.04) 0.0 (0.03) 69 0.4 (0.04) 0.0 (0.03) 70 0.4 (0.04) 0.0 (0.03)
Week 6 72 0.4 (0.04) 0.0 (0.03) 60 0.4 (0.04) 0.0 (0.03) 65 0.4 (0.04) 0.0 (0.03)
Week 8 71 0.4 (0.04) 0.0 (0.03) 56 0.4 (0.03) 0.0 (0.02) 56 0.4 (0.03) 0.0 (0.03)
Week 12 65 0.4 (0.04) 0.0 (0.03) 49 0.4 (0.03) 0.0 (0.02) 50 0.4 (0.04) 0.0 (0.03)
Week 16 68 0.4 (0.04) 0.0 (0.03) 42 0.4 (0.03) 0.0 (0.03) 37 0.4 (0.03) 0.0 (0.04)
Week 20 64 0.4 (0.04) 0.0 (0.03) 30 0.4 (0.03) 0.0 (0.02) 31 0.4 (0.04) 0.0 (0.04)
Week 24 58 0.4 (0.04) 0.0 (0.03) 25 0.4 (0.04) 0.0 (0.02) 30 0.4 (0.04) 0.0 (0.04)
Week 26 56 0.4 (0.04) 0.0 (0.03) 25 0.4 (0.04) 0.0 (0.03) 27 0.4 (0.04) 0.0 (0.03)
End of Treatment1 83 0.4 (0.04) 0.0 (0.03) 82 0.4 (0.04) 0.0 (0.03) 81 0.4 (0.04) 0.0 (0.03)
Hemoglobin (mmol/L)
Baseline 85 8.6 (0.83) 81 8.6 (0.77) 81 8.9 (0.78)
Week 2 83 8.4 (0.76) -0.2 (0.42) 80 8.4 (0.76) -0.2 (0.46) 80 8.7 (0.83) -0.2 (0.42)
Week 4 79 8.4 (0.82) -0.2 (0.44) 71 8.4 (0.78) -0.3 (0.50) 71 8.6 (0.78) -0.2 (0.45)
Week 6 73 8.4 (0.77) -0.2 (0.44) 62 8.3 (0.76) -0.3 (0.46) 65 8.6 (0.75) -0.3 (0.44)
Week 8 72 8.5 (0.76) -0.2 (0.46) 59 8.3 (0.71) -0.3 (0.39) 56 8.7 (0.73) -0.3 (0.39)
Week 12 66 8.4 (0.83) -0.2 (0.42) 50 8.3 (0.69) -0.3 (0.34) 50 8.8 (0.79) -0.3 (0.42)
Week 16 68 8.5 (0.73) -0.1 (0.46) 42 8.3 (0.69) -0.2 (0.42) 37 8.7 (0.70) -0.3 (0.47)
Week 20 65 8.4 (0.80) -0.2 (0.51) 30 8.2 (0.63) -0.2 (0.46) 31 8.7 (0.79) -0.4 (0.46)
Week 24 58 8.3 (0.82) -0.2 (0.40) 25 8.4 (0.69) -0.2 (0.36) 30 8.9 (0.85) -0.3 (0.36)
Week 26 57 8.4 (0.80) -0.2 (0.47) 25 8.3 (0.67) -0.3 (0.39) 27 8.7 (0.74) -0.5 (0.39)
End of Treatment1 84 8.4 (0.78) -0.2 (0.44) 82 8.4 (0.69) -0.2 (0.44) 81 8.7 (0.79) -0.2 (0.36)
Leukocytes (GI/L)
Baseline 85 6.9 (1.76) 81 6.6 (1.95) 81 6.5 (1.54)
Week 2 83 6.5 (1.55) -0.4 (1.40) 80 6.9 (1.99) 0.2 (1.37) 80 6.5 (1.59) -0.1 (1.18)
Week 4 79 6.4 (1.63) -0.4 (1.17) 71 6.8 (2.12) 0.1 (1.53) 71 6.6 (1.42) 0.1 (1.21)
Week 6 73 6.5 (1.57) -0.4 (1.36) 62 6.9 (1.99) 0.1 (1.14) 65 7.0 (1.84) 0.4 (1.65)
Week 8 72 6.5 (1.70) -0.3 (1.28) 59 7.1 (1.88) 0.3 (1.26) 56 6.9 (1.95) 0.4 (1.64)
Week 12 66 6.3 (1.28) -0.5 (1.09) 50 6.8 (2.03) 0.2 (0.97) 50 6.6 (1.51) 0.0 (1.21)
Week 16 68 6.4 (1.50) -0.5 (1.29) 42 6.6 (2.11) 0.2 (1.44) 37 6.8 (1.72) -0.1 (1.37)
Week 20 65 6.5 (1.77) -0.5 (1.40) 30 6.5 (1.94) 0.1 (1.24) 31 6.6 (1.67) -0.2 (1.21)
Week 24 58 6.7 (1.77) -0.2 (1.37) 25 6.3 (1.84) 0.0 (1.16) 30 6.7 (1.80) -0.1 (1.16)
Week 26 57 6.4 (1.47) -0.4 (1.24) 25 6.1 (1.93) -0.3 (1.15) 27 6.7 (1.78) -0.2 (1.42)
End of Treatment1 84 6.6 (1.80) -0.2 (1.32) 82 6.8 (2.17) 0.1 (1.35) 81 6.7 (1.76) 0.2 (1.37)
Lymphocytes (GI/L)
Baseline 85 1.8 (0.57) 81 1.8 (0.57) 81 1.7 (0.52)
Week 2 83 1.7 (0.50) -0.1 (0.37) 80 1.8 (0.66) 0.0 (0.45) 80 1.7 (0.50) 0.0 (0.38)
Week 4 79 1.7 (0.56) 0.0 (0.38) 71 1.8 (0.67) 0.0 (0.49) 71 1.7 (0.50) 0.0 (0.44)
Week 6 73 1.8 (0.58) 0.0 (0.41) 62 1.9 (0.64) 0.1 (0.47) 64 1.7 (0.48) 0.0 (0.44)
Week 8 72 1.8 (0.67) 0.0 (0.44) 59 1.9 (0.65) 0.1 (0.45) 56 1.7 (0.50) 0.0 (0.36)
Week 12 66 1.7 (0.58) -0.1 (0.42) 50 1.8 (0.58) -0.1 (0.45) 50 1.6 (0.47) 0.0 (0.37)
Week 16 68 1.7 (0.58) -0.1 (0.42) 42 1.8 (0.63) -0.1 (0.34) 37 1.7 (0.62) 0.0 (0.41)
Week 20 65 1.7 (0.50) -0.1 (0.45) 30 1.7 (0.59) -0.1 (0.50) 31 1.7 (0.59) 0.0 (0.36)
Week 24 58 1.8 (0.65) 0.0 (0.48) 25 1.8 (0.56) -0.1 (0.37) 30 1.7 (0.56) 0.0 (0.37)
Week 26 57 1.8 (0.59) 0.0 (0.43) 25 1.7 (0.62) -0.1 (0.39) 27 1.8 (0.61) 0.1 (0.50)
End of Treatment1 84 1.8 (0.59) 0.0 (0.46) 82 1.8 (0.62) 0.0 (0.46) 81 1.6 (0.54) 0.0 (0.38)
Monocytes (GI/L)
Baseline 85 0.4 (0.15) 81 0.5 (0.16) 81 0.4 (0.12)
Week 2 83 0.4 (0.15) 0.0 (0.14) 80 0.5 (0.16) 0.0 (0.13) 80 0.4 (0.15) 0.0 (0.12)
Week 4 79 0.4 (0.12) 0.0 (0.11) 71 0.5 (0.17) 0.0 (0.15) 71 0.4 (0.13) 0.0 (0.13)
Week 6 73 0.4 (0.15) 0.0 (0.13) 62 0.5 (0.19) 0.0 (0.16) 64 0.5 (0.15) 0.0 (0.13)
Week 8 72 0.4 (0.14) 0.0 (0.14) 59 0.5 (0.14) 0.0 (0.14) 56 0.5 (0.15) 0.0 (0.14)
Week 12 66 0.4 (0.13) 0.0 (0.13) 50 0.5 (0.15) 0.0 (0.12) 50 0.4 (0.15) 0.0 (0.11)
Week 16 68 0.5 (0.15) 0.0 (0.17) 42 0.5 (0.16) 0.0 (0.12) 37 0.5 (0.16) 0.0 (0.14)
Week 20 65 0.5 (0.16) 0.0 (0.15) 30 0.5 (0.13) 0.0 (0.13) 31 0.5 (0.17) 0.0 (0.18)
Week 24 58 0.5 (0.17) 0.0 (0.17) 25 0.5 (0.19) 0.0 (0.11) 30 0.5 (0.18) 0.0 (0.12)
Week 26 57 0.4 (0.14) 0.0 (0.14) 25 0.4 (0.17) 0.0 (0.12) 27 0.5 (0.14) 0.0 (0.13)
End of Treatment1 84 0.5 (0.17) 0.0 (0.17) 82 0.5 (0.17) 0.0 (0.13) 81 0.5 (0.15) 0.0 (0.12)
Platelet (GI/L)
Baseline 84 250.3 (65.52) 79 233.8 (58.58) 81 227.7 (54.74)
Week 2 83 246.2 (57.55) -4.4 (36.21) 78 247.4 (59.37) 9.1 (46.36) 80 238.2 (59.77) 8.4 (31.55)
Week 4 78 243.8 (54.30) -7.4 (40.04) 70 239.2 (57.80) 5.7 (39.75) 70 238.3 (49.60) 9.8 (29.21)
Week 6 73 250.8 (58.97) -3.3 (36.82) 62 238.3 (55.74) 3.5 (43.39) 63 239.1 (56.79) 9.2 (37.82)
Week 8 72 246.0 (66.61) -6.2 (42.35) 59 245.4 (60.21) 5.1 (46.18) 55 236.9 (70.88) 11.0 (48.13)
Week 12 65 241.9 (53.71) -12.8 (42.62) 50 238.8 (49.49) 1.9 (24.69) 49 236.1 (53.03) 9.1 (34.47)
Week 16 68 241.8 (55.65) -13.7 (43.45) 41 244.5 (57.60) 2.8 (32.93) 37 230.9 (58.00) 5.3 (25.87)
Week 20 65 248.4 (60.70) -8.4 (30.42) 30 240.7 (64.07) 7.3 (42.65) 30 235.1 (65.46) 2.3 (22.80)
Week 24 57 238.8 (51.89) -11.3 (35.06) 24 249.7 (63.44) 1.8 (33.48) 29 238.3 (67.53) 4.5 (26.74)
Week 26 56 247.6 (60.11) -2.7 (41.73) 25 241.3 (57.16) -1.9 (38.84) 27 237.4 (67.14) 0.3 (26.99)
End of Treatment1 84 241.5 (59.49) -8.5 (35.55) 82 236.7 (63.56) 1.1 (36.31) 81 233.8 (60.79) 4.1 (35.89)
1 Last observed value while on treatment (prior to or at Week 24)
Caution

The order of items within Chemistry and Hematology is currently alphabetical, based on laboratory names. However, we can adopt alternative sorting methods if preferred

Table 14-6-02

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbc_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbc.xpt')  

adlbh_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbh.xpt')

# COMBINE & SUBSEST
adlb <- bind_rows(
   CHEMISTRY = adlbc_orig,
   HEMATOLOGY = adlbh_orig,
   .id = 'ORIG') %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y', AVISITN != 99) %>% 
   select(ORIG, PARCAT1, PARAM, PARAMCD, USUBJID , TRTA, AVISIT, AVISITN, AVAL, CHG, LBNRIND) %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(LBNRIND = factor(LBNRIND,
                           levels = c('LOW','NORMAL','HIGH'),
                           labels = c('Low','Normal','High')) ) %>% 
   mutate(PARAM = str_remove_all(PARAM, "\\(.*?\\)") %>% str_trim() ) %>% 
   filter(!is.na(PARAM), !is.na(TRTA), !is.na(LBNRIND)) %>%
   filter(LBNRIND %in% c('Low','Normal','High')) %>% 
   filter(!str_starts(PARAMCD, '_') )

# NEST
adlb_n <- adlb %>% 
   nest_by(ORIG) 

# TABLES
adlb_t <- adlb_n %>% 
   mutate(t_cross   = list(
      tbl_strata(
         data = data %>% set_variable_labels(PARAM = str_glue('{ORIG}')),
         strata = PARAM,
         .tbl_fun = 
            ~.x %>% 
            tbl_cross(row = TRTA,
                      col = LBNRIND,
                      margin = NULL,
                      percent = 'row',
                      digits = c(0, 0)) %>% 
            add_p(),
         .quiet = TRUE,
         .combine_with = 'tbl_stack'
      )
   ),
   t_summary = list(
      tbl_strata(
         data = data %>% set_variable_labels(PARAM = str_glue('{ORIG}')),
         strata = TRTA,
         .tbl_fun = 
            ~.x %>% 
            tbl_summary(
               by = LBNRIND,
               include = PARAM,
               percent = 'row',
               digits = everything() ~ c(0, 0) ) %>%
            add_p() %>% 
            modify_header(all_stat_cols() ~ '**{level}**'),
         .header = '**{strata}**',
         .quiet = TRUE
      )
   ),
   t_combine = list(
      t_summary %>% 
         modify_column_hide(columns = c(test_name_1, test_result_1, p.value_1,
                                        test_name_2, test_result_2, p.value_2)) %>% 
         modify_table_body(
            ~.x %>% 
               mutate(across(starts_with('stat_'), ~str_replace(., "0 \\(0%\\)", "0"))) %>% 
               select(-test_name_3, -test_result_3, -p.value_3) %>% 
               left_join(
                  t_cross$table_body %>% 
                     filter(!is.na(alternative)) %>% 
                     mutate(variable = 'PARAM',
                            var_label = str_glue('{ORIG}'),
                            row_type = 'level',
                            label = groupname_col,
                            test_name_3 = test_name,
                            test_results_3 = test_result,
                            p.value_3 = p.value) %>% 
                     select(variable, var_label, row_type, label, test_name_3, test_results_3,  p.value_3)
               ) 
         ) 
   )
   )

# STACK & PRINT
tbl_stack(tbls = adlb_t$t_combine) %>%
   bold_labels() %>%
   modify_column_alignment(
      all_stat_cols(), 'left') %>% 
   modify_spanning_header(
      c(p.value_3) ~ '**&nbsp;**') %>% 
   modify_fmt_fun(
      update = p.value_3 ~ function(x) style_pvalue(x, digits = 3)) %>% 
   modify_header(
      label  ~ '' ) %>% 
   modify_footnote(
      all_stat_cols() ~ 
         'Percentages are based on the number of subjects with non-missing assessments (i.e., the total of the
subjects in the low, normal, and high categories) within each treatment group') %>% 
   modify_caption(
      '**Table 14-6.02<br>Frequency of Normal and Abnormal (Beyond Normal Range) Laboratory Values During Treatment**' )
Table 14-6.02
Frequency of Normal and Abnormal (Beyond Normal Range) Laboratory Values During Treatment
Placebo Low Dose High Dose  
Low1 Normal1 High1 Low1 Normal1 High1 Low1 Normal1 High1 p-value
CHEMISTRY









    Alanine Aminotransferase 1 (1%) 74 (88%) 9 (11%) 5 (6%) 68 (83%) 9 (11%) 0 70 (88%) 10 (13%) 0.185
    Albumin 16 (19%) 65 (77%) 3 (4%) 15 (18%) 66 (80%) 1 (1%) 5 (6%) 74 (93%) 1 (1%) 0.042
    Alkaline Phosphatase 4 (5%) 72 (86%) 8 (10%) 2 (2%) 74 (90%) 6 (7%) 3 (4%) 73 (91%) 4 (5%) 0.776
    Aspartate Aminotransferase 0 74 (88%) 10 (12%) 0 72 (88%) 10 (12%) 0 72 (90%) 8 (10%) 0.907
    Bilirubin 0 78 (93%) 6 (7%) 0 79 (98%) 2 (2%) 0 75 (94%) 5 (6%) 0.402
    Blood Urea Nitrogen 0 75 (89%) 9 (11%) 0 60 (73%) 22 (27%) 0 68 (85%) 12 (15%) 0.023
    Calcium 5 (6%) 78 (93%) 1 (1%) 7 (9%) 72 (88%) 3 (4%) 7 (9%) 71 (89%) 2 (3%) 0.789
    Chloride 0 74 (88%) 10 (12%) 0 74 (90%) 8 (10%) 0 78 (98%) 2 (3%) 0.058
    Cholesterol 5 (6%) 73 (87%) 6 (7%) 6 (7%) 72 (88%) 4 (5%) 6 (8%) 70 (88%) 4 (5%) 0.962
    Creatine Kinase 1 (1%) 66 (79%) 17 (20%) 1 (1%) 68 (83%) 13 (16%) 0 67 (84%) 13 (16%) 0.816
    Creatinine 0 80 (95%) 4 (5%) 0 76 (93%) 6 (7%) 0 73 (91%) 7 (9%) 0.572
    Gamma Glutamyl Transferase 4 (5%) 73 (87%) 7 (8%) 4 (5%) 69 (84%) 9 (11%) 1 (1%) 71 (89%) 8 (10%) 0.689
    Glucose 0 82 (98%) 2 (2%) 0 81 (99%) 1 (1%) 0 77 (96%) 3 (4%) 0.534
    Phosphate 1 (1%) 83 (99%) 0 0 80 (98%) 2 (2%) 1 (1%) 78 (98%) 1 (1%) 0.518
    Potassium 3 (4%) 80 (95%) 1 (1%) 3 (4%) 79 (96%) 0 2 (3%) 78 (98%) 0 >0.999
    Protein 1 (1%) 76 (90%) 7 (8%) 2 (2%) 75 (91%) 5 (6%) 1 (1%) 77 (96%) 2 (3%) 0.536
    Sodium 4 (5%) 75 (89%) 5 (6%) 3 (4%) 74 (90%) 5 (6%) 7 (9%) 62 (78%) 11 (14%) 0.177
    Urate 3 (4%) 77 (92%) 4 (5%) 1 (1%) 75 (91%) 6 (7%) 4 (5%) 69 (86%) 7 (9%) 0.564
HEMATOLOGY









    Basophils 0 84 (100%) 0 0 82 (100%) 0 0 81 (100%) 0 >0.999
    Eosinophils 0 84 (100%) 0 0 71 (87%) 11 (13%) 0 74 (91%) 7 (9%) <0.001
    Ery. Mean Corpuscular Hemoglobin ) 0 81 (96%) 3 (4%) 0 81 (99%) 1 (1%) 1 (1%) 75 (93%) 5 (6%) 0.186
    Ery. Mean Corpuscular HGB Concentration 15 (18%) 68 (82%) 0 17 (21%) 65 (79%) 0 9 (11%) 72 (89%) 0 0.231
    Ery. Mean Corpuscular Volume 1 (1%) 53 (64%) 29 (35%) 0 64 (78%) 18 (22%) 1 (1%) 64 (79%) 16 (20%) 0.077
    Erythrocytes 13 (15%) 71 (85%) 0 18 (22%) 64 (78%) 0 11 (14%) 68 (84%) 2 (2%) 0.238
    Hematocrit 5 (6%) 74 (89%) 4 (5%) 9 (11%) 72 (88%) 1 (1%) 1 (1%) 78 (96%) 2 (2%) 0.052
    Hemoglobin 14 (17%) 68 (81%) 2 (2%) 10 (12%) 72 (88%) 0 5 (6%) 73 (90%) 3 (4%) 0.093
    Leukocytes 7 (8%) 73 (87%) 4 (5%) 6 (7%) 68 (83%) 8 (10%) 4 (5%) 74 (91%) 3 (4%) 0.462
    Lymphocytes 6 (7%) 73 (87%) 5 (6%) 4 (5%) 69 (84%) 9 (11%) 4 (5%) 76 (94%) 1 (1%) 0.103
    Monocytes 2 (2%) 80 (95%) 2 (2%) 1 (1%) 77 (94%) 4 (5%) 0 79 (98%) 2 (2%) 0.626
    Platelet 0 81 (96%) 3 (4%) 2 (2%) 77 (94%) 3 (4%) 2 (2%) 77 (95%) 2 (2%) 0.681
1 Percentages are based on the number of subjects with non-missing assessments (i.e., the total of the subjects in the low, normal, and high categories) within each treatment group

Table 14-6-03

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbc_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbc.xpt')  

adlbh_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbh.xpt')


# COMBINE & SUBSEST
adlb <- bind_rows(
   CHEMISTRY = adlbc_orig,
   HEMATOLOGY = adlbh_orig,
   .id = 'ORIG') %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y', AVISITN != 99) %>% 
   select(ORIG, PARCAT1, PARAM, PARAMCD, USUBJID , TRTA, AVISIT, AVISITN, AVAL, CHG, LBNRIND) %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(LBNRIND = factor(LBNRIND,
                           levels = c('LOW','NORMAL','HIGH'),
                           labels = c('Low','Normal','High')) ) %>% 
   mutate(PARAM = str_remove_all(PARAM, '\\(.*?\\)') %>% str_trim() ) %>% 
   mutate(PARAM = str_remove_all(PARAM, 'change from previous visit, relative to normal range') %>% str_trim() ) %>% 
   mutate(PARAM = str_remove_all(PARAM, 'change from previous visit, relative to normal rang') %>% str_trim() ) %>% 
   filter(!is.na(PARAM), !is.na(TRTA), !is.na(LBNRIND)) %>%
   filter(LBNRIND %in% c('Low','Normal','High')) %>% 
   filter(str_starts(PARAMCD, '_') )

# NEST
adlb_n <- adlb %>% 
   nest_by(ORIG) 

# TABLES
adlb_t <- adlb_n %>% 
   mutate(t_cross   = list(
      tbl_strata(
         data = data %>% set_variable_labels(PARAM = str_glue('{ORIG}')),
         strata = PARAM,
         .tbl_fun = 
            ~.x %>% 
            tbl_cross(row = TRTA,
                      col = LBNRIND,
                      margin = NULL,
                      percent = 'row',
                      digits = c(0, 0)) %>% 
            add_p(),
         .quiet = TRUE,
         .combine_with = 'tbl_stack'
      )
   ),
   t_summary = list(
      tbl_strata(
         data = data %>% set_variable_labels(PARAM = str_glue('{ORIG}')),
         strata = TRTA,
         .tbl_fun = 
            ~.x %>% 
            tbl_summary(
               by = LBNRIND,
               include = PARAM,
               percent = 'row',
               digits = everything() ~ c(0, 0) ) %>%
            add_p() %>% 
            modify_header(all_stat_cols() ~ '**{level}**'),
         .header = '**{strata}**',
         .quiet = TRUE
      )
   ),
   t_combine = list(
      t_summary %>% 
         modify_column_hide(columns = c(test_name_1, test_result_1, p.value_1,
                                        test_name_2, test_result_2, p.value_2)) %>% 
         modify_table_body(
            ~.x %>% 
               mutate(across(starts_with('stat_'), ~str_replace(., '0 \\(0%\\)', '0'))) %>% 
               select(-test_name_3, -test_result_3, -p.value_3) %>% 
               left_join(
                  t_cross$table_body %>% 
                     filter(!is.na(alternative)) %>% 
                     mutate(variable = 'PARAM',
                            var_label = str_glue('{ORIG}'),
                            row_type = 'level',
                            label = groupname_col,
                            test_name_3 = test_name,
                            test_results_3 = test_result,
                            p.value_3 = p.value) %>% 
                     select(variable, var_label, row_type, label, test_name_3, test_results_3,  p.value_3)
               ) 
         ) 
   )
   )

# STACK & PRINT
tbl_stack(tbls = adlb_t$t_combine) %>%
   bold_labels() %>%
   modify_column_alignment(
      all_stat_cols(), 'left') %>% 
   modify_spanning_header(
      c(p.value_3) ~ '**&nbsp;**') %>% 
   modify_fmt_fun(
      update = p.value_3 ~ function(x) style_pvalue(x, digits = 3)) %>% 
   modify_header(
      label  ~ '') %>% 
   modify_footnote(
      all_stat_cols() ~ 
         'Percentages are based on the number of subjects with non-missing assessments (i.e., the total of the
      subjects in the low, normal, and high categories) within each treatment group') %>% 
   modify_caption(
      '**Table 14-6.03<br>Frequency of Normal and Abnormal (Clinically Significant Change from Previous Visit) Laboratory Values During Treatment**')
Table 14-6.03
Frequency of Normal and Abnormal (Clinically Significant Change from Previous Visit) Laboratory Values During Treatment
Placebo Low Dose High Dose  
Low1 Normal1 High1 Low1 Normal1 High1 Low1 Normal1 High1 p-value
CHEMISTRY









    Alanine Aminotransferase 1 (1%) 78 (93%) 5 (6%) 2 (2%) 73 (89%) 7 (9%) 0 74 (93%) 6 (8%) 0.743
    Albumin 9 (11%) 74 (88%) 1 (1%) 10 (12%) 71 (87%) 1 (1%) 3 (4%) 77 (96%) 0 0.152
    Alkaline Phosphatase 1 (1%) 75 (89%) 8 (10%) 1 (1%) 76 (94%) 4 (5%) 1 (1%) 74 (93%) 5 (6%) 0.891
    Aspartate Aminotransferase 0 76 (90%) 8 (10%) 0 76 (93%) 6 (7%) 0 73 (91%) 7 (9%) 0.884
    Bilirubin 0 80 (95%) 4 (5%) 0 81 (100%) 0 0 77 (96%) 3 (4%) 0.150
    Blood Urea Nitrogen 0 77 (92%) 7 (8%) 0 67 (82%) 15 (18%) 0 73 (91%) 7 (9%) 0.094
    Calcium 2 (2%) 82 (98%) 0 3 (4%) 79 (96%) 0 4 (5%) 75 (94%) 1 (1%) 0.538
    Chloride 0 77 (92%) 7 (8%) 0 75 (91%) 7 (9%) 0 78 (98%) 2 (3%) 0.223
    Cholesterol 3 (4%) 78 (93%) 3 (4%) 4 (5%) 75 (91%) 3 (4%) 4 (5%) 75 (94%) 1 (1%) 0.883
    Creatine Kinase 0 73 (87%) 11 (13%) 1 (1%) 72 (88%) 9 (11%) 0 72 (90%) 8 (10%) 0.850
    Creatinine 0 80 (95%) 4 (5%) 0 77 (94%) 5 (6%) 0 78 (98%) 2 (3%) 0.593
    Gamma Glutamyl Transferase 2 (2%) 76 (90%) 6 (7%) 2 (2%) 74 (90%) 6 (7%) 1 (1%) 72 (90%) 7 (9%) 0.983
    Glucose 0 83 (99%) 1 (1%) 0 82 (100%) 0 0 77 (96%) 3 (4%) 0.127
    Phosphate 1 (1%) 82 (98%) 1 (1%) 0 79 (98%) 2 (2%) 0 80 (100%) 0 0.697
    Potassium 1 (1%) 83 (99%) 0 2 (2%) 79 (98%) 0 1 (1%) 79 (99%) 0 0.846
    Protein 1 (1%) 79 (94%) 4 (5%) 1 (1%) 79 (96%) 2 (2%) 0 79 (99%) 1 (1%) 0.673
    Sodium 3 (4%) 76 (90%) 5 (6%) 1 (1%) 78 (95%) 3 (4%) 2 (3%) 74 (93%) 4 (5%) 0.859
    Urate 1 (1%) 80 (95%) 3 (4%) 1 (1%) 76 (93%) 5 (6%) 1 (1%) 76 (95%) 3 (4%) 0.942
HEMATOLOGY









    Basophils 0 84 (100%) 0 0 82 (100%) 0 0 80 (100%) 0 >0.999
    Eosinophils 0 84 (100%) 0 0 73 (89%) 9 (11%) 0 76 (95%) 4 (5%) 0.003
    Ery. Mean Corpuscular Hemoglobin ) 0 82 (98%) 2 (2%) 0 81 (99%) 1 (1%) 1 (1%) 75 (94%) 4 (5%) 0.251
    Ery. Mean Corpuscular HGB Concentration 11 (13%) 72 (87%) 0 5 (6%) 76 (94%) 0 1 (1%) 79 (99%) 0 0.009
    Ery. Mean Corpuscular Volume 1 (1%) 61 (73%) 21 (25%) 0 70 (86%) 11 (14%) 1 (1%) 67 (84%) 12 (15%) 0.145
    Erythrocytes 7 (8%) 77 (92%) 0 16 (20%) 66 (80%) 0 8 (10%) 71 (89%) 1 (1%) 0.081
    Hematocrit 3 (4%) 79 (95%) 1 (1%) 6 (7%) 75 (93%) 0 1 (1%) 77 (96%) 2 (3%) 0.172
    Hemoglobin 6 (7%) 77 (92%) 1 (1%) 9 (11%) 73 (89%) 0 5 (6%) 74 (93%) 1 (1%) 0.697
    Leukocytes 4 (5%) 77 (92%) 3 (4%) 5 (6%) 72 (88%) 5 (6%) 2 (3%) 75 (94%) 3 (4%) 0.734
    Lymphocytes 4 (5%) 77 (92%) 3 (4%) 3 (4%) 75 (91%) 4 (5%) 3 (4%) 76 (95%) 1 (1%) 0.765
    Monocytes 2 (2%) 78 (93%) 4 (5%) 0 78 (95%) 4 (5%) 0 79 (99%) 1 (1%) 0.241
    Platelet 0 84 (100%) 0 1 (1%) 79 (96%) 2 (2%) 2 (3%) 76 (95%) 2 (3%) 0.342
1 Percentages are based on the number of subjects with non-missing assessments (i.e., the total of the subjects in the low, normal, and high categories) within each treatment group

Table 14-6-04

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbc_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbc.xpt')  

adlbh_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbh.xpt')

# COMBINE & SUBSEST
adlb <- bind_rows(
   CHEMISTRY = adlbc_orig,
   HEMATOLOGY = adlbh_orig,
   .id = 'ORIG') %>% 
   filter(VISIT != 'SCREENING 1') %>% 
   filter(SAFFL == 'Y', AVISITN != 99) %>% 
   filter(!is.na(VISIT), !is.na(TRTA), !is.na(BNRIND), !is.na(ANRIND), !is.na(PARAM)) %>%
   mutate(TRTP = factor(TRTP, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Low Dose','High Dose'))) %>% 
   mutate(ANRIND = factor(ANRIND,
                          levels = c('N','H'),
                          labels = c('Normal','High')) ) %>% 
   mutate(BNRIND = factor(BNRIND,
                          levels = c('N','H'),
                          labels = c('Normal','High')) ) %>%
   mutate(VISIT = factor(VISIT) %>% fct_reorder(AVISITN)) %>% 
   mutate(PARAM = str_remove_all(PARAM, '\\(.*?\\)') %>% str_trim() ) %>% 
   select(ORIG, PARAM, PARAMCD, USUBJID , TRTP, VISIT, ANRIND, BNRIND) %>% 
   filter(!str_starts(PARAMCD, '_') ) %>% 
   filter(PARAM %in% c('Alanine Aminotransferase', 'Albumin', 'Basophils', 'Eosinophils'))

# NEST
adlb_n <- adlb %>% 
   nest_by(ORIG, PARAM, PARAMCD, TRTP, VISIT)

# TABLE
#+ message = FALSE
adlb_t <- adlb_n %>% 
   mutate(t_cross = list(
      tbl_cross(
         data = data,
         col = ANRIND,
         row = BNRIND,
         label = list(BNRIND ~ str_glue('{VISIT}') ),
         percent = 'row',
         margin = 'row',
         margin_text = 'n',
         missing = 'no') %>% 
         modify_table_body(
            ~.x %>% 
               arrange(row_type, var_type) ) )
   )

# SUMMARISE: STACK
adlb_w <- adlb_t %>% 
   select(-data) %>% 
   pivot_wider(names_from = TRTP,
               values_from = t_cross) %>%
   rowwise() %>% 
   mutate(t_cross = 
             tbl_merge(tbls = list(Placebo, `Low Dose`, `High Dose`),
                       tab_spanner  = c('**Placebo**','**Low Dose**','**High Dose**')) %>% list()
   ) %>% 
   group_by(ORIG, PARAM, PARAMCD) %>% 
   summarise(t_cross = tbl_stack(t_cross, group_header = NULL, quiet = TRUE) %>% list() ) %>% 
   group_by(ORIG) %>% 
   summarise(t_cross = tbl_stack(t_cross, group_header = PARAM, quiet = TRUE) %>% list() ) 

# PRINT
#+ results = 'asis'
for(i in 1:2){
   adlb_w$t_cross[[i]] %>% 
      modify_table_body(
         ~.x %>% 
            mutate(row_type = ifelse(label == 'n', 'level', row_type)) %>% 
            mutate(across(starts_with('stat_'), ~str_replace_all(.x, '0 \\(0%\\)|0 \\(NA%\\)', '0'))) %>% 
            mutate(across(starts_with('stat_'), 
                          ~case_when(
                             label == 'n' ~ str_replace_all(.x, ' \\(.*?\\)', ''),
                             .default = .x) ) ) ) %>% 
      modify_header(
         starts_with('stat_1_') ~ '**Normal at<br>Baseline**',
         starts_with('stat_2_') ~ '**High at<br>Baseline**') %>% 
      modify_footnote(
         all_stat_cols() ~ 'Only subjects with baseline results are included in the summary.<br>
                            There were no subjects with abnormal low values at baseline.') %>% 
      modify_column_alignment(
         all_stat_cols(), 'left') %>% 
      modify_caption(
         str_glue('**Table 14-6.04 {adlb_w$ORIG[[i]]}<br>Shifts of Laboratory Values During Treatment, Categorized Based on Threshold Ranges, by Visit**')) %>% 
      bstfun::bold_italicize_group_labels(bold = TRUE) %>% 
      knitr::knit_print() %>% 
      cat()
}
Table 14-6.04 CHEMISTRY
Shifts of Laboratory Values During Treatment, Categorized Based on Threshold Ranges, by Visit
Placebo Low Dose High Dose
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Alanine Aminotransferase
WEEK 2





    Normal 81 (100%) 0 77 (100%) 0 78 (100%) 0
    High 0 2 (100%) 0 1 (100%) 0 0
    n 81 2 77 1 78 0
WEEK 4





    Normal 77 (100%) 0 69 (100%) 0 72 (100%) 0
    High 1 (50%) 1 (50%) 0 1 (100%) 0 0
    n 78 1 69 1 72 0
WEEK 6





    Normal 71 (99%) 1 (1.4%) 59 (100%) 0 66 (100%) 0
    High 1 (100%) 0 0 1 (100%) 0 0
    n 72 1 59 1 66 0
WEEK 8





    Normal 71 (100%) 0 57 (100%) 0 54 (96%) 2 (3.6%)
    High 0 1 (100%) 0 1 (100%) 0 0
    n 71 1 57 1 54 2
WEEK 12





    Normal 65 (98%) 1 (1.5%) 48 (98%) 1 (2.0%) 49 (98%) 1 (2.0%)
    High 1 (100%) 0 1 (100%) 0 0 0
    n 66 1 49 1 49 1
WEEK 16





    Normal 67 (100%) 0 40 (100%) 0 37 (100%) 0
    High 1 (100%) 0 0 0 0 0
    n 68 0 40 0 37 0
WEEK 20





    Normal 64 (100%) 0 29 (100%) 0 31 (100%) 0
    High 1 (100%) 0 0 0 0 0
    n 65 0 29 0 31 0
WEEK 24





    Normal 55 (98%) 1 (1.8%) 25 (100%) 0 30 (100%) 0
    High 1 (100%) 0 0 0 0 0
    n 56 1 25 0 30 0
WEEK 26





    Normal 56 (100%) 0 23 (96%) 1 (4.2%) 27 (100%) 0
    High 1 (100%) 0 0 0 0 0
    n 57 0 23 1 27 0
Albumin
WEEK 2





    Normal 83 (100%) 0 78 (100%) 0 78 (100%) 0
    High 0 0 0 0 0 0
    n 83 0 78 0 78 0
WEEK 4





    Normal 79 (100%) 0 70 (100%) 0 72 (100%) 0
    High 0 0 0 0 0 0
    n 79 0 70 0 72 0
WEEK 6





    Normal 73 (100%) 0 60 (100%) 0 66 (100%) 0
    High 0 0 0 0 0 0
    n 73 0 60 0 66 0
WEEK 8





    Normal 72 (100%) 0 58 (100%) 0 56 (100%) 0
    High 0 0 0 0 0 0
    n 72 0 58 0 56 0
WEEK 12





    Normal 67 (100%) 0 50 (100%) 0 50 (100%) 0
    High 0 0 0 0 0 0
    n 67 0 50 0 50 0
WEEK 16





    Normal 68 (100%) 0 40 (100%) 0 37 (100%) 0
    High 0 0 0 0 0 0
    n 68 0 40 0 37 0
WEEK 20





    Normal 65 (100%) 0 29 (100%) 0 31 (100%) 0
    High 0 0 0 0 0 0
    n 65 0 29 0 31 0
WEEK 24





    Normal 57 (100%) 0 25 (100%) 0 30 (100%) 0
    High 0 0 0 0 0 0
    n 57 0 25 0 30 0
WEEK 26





    Normal 57 (100%) 0 24 (100%) 0 27 (100%) 0
    High 0 0 0 0 0 0
    n 57 0 24 0 27 0
1 Only subjects with baseline results are included in the summary.
There were no subjects with abnormal low values at baseline.
Table 14-6.04 HEMATOLOGY
Shifts of Laboratory Values During Treatment, Categorized Based on Threshold Ranges, by Visit
Placebo Low Dose High Dose
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Basophils
WEEK 2





    Normal 82 (100%) 0 77 (100%) 0 77 (100%) 0
    High 0 0 0 0 0 0
    n 82 0 77 0 77 0
WEEK 4





    Normal 78 (100%) 0 69 (100%) 0 69 (100%) 0
    High 0 0 0 0 0 0
    n 78 0 69 0 69 0
WEEK 6





    Normal 72 (100%) 0 59 (100%) 0 62 (100%) 0
    High 0 0 0 0 0 0
    n 72 0 59 0 62 0
WEEK 8





    Normal 71 (100%) 0 56 (100%) 0 55 (100%) 0
    High 0 0 0 0 0 0
    n 71 0 56 0 55 0
WEEK 12





    Normal 65 (100%) 0 48 (100%) 0 49 (100%) 0
    High 0 0 0 0 0 0
    n 65 0 48 0 49 0
WEEK 16





    Normal 67 (100%) 0 39 (100%) 0 36 (100%) 0
    High 0 0 0 0 0 0
    n 67 0 39 0 36 0
WEEK 20





    Normal 64 (100%) 0 29 (100%) 0 31 (100%) 0
    High 0 0 0 0 0 0
    n 64 0 29 0 31 0
WEEK 24





    Normal 57 (100%) 0 24 (100%) 0 29 (100%) 0
    High 0 0 0 0 0 0
    n 57 0 24 0 29 0
WEEK 26





    Normal 56 (100%) 0 24 (100%) 0 26 (100%) 0
    High 0 0 0 0 0 0
    n 56 0 24 0 26 0
Eosinophils
WEEK 2





    Normal 82 (100%) 0 74 (97%) 2 (2.6%) 77 (100%) 0
    High 0 0 1 (100%) 0 0 0
    n 82 0 75 2 77 0
WEEK 4





    Normal 78 (100%) 0 69 (100%) 0 68 (99%) 1 (1.4%)
    High 0 0 0 0 0 0
    n 78 0 69 0 68 1
WEEK 6





    Normal 72 (100%) 0 59 (100%) 0 58 (94%) 4 (6.5%)
    High 0 0 0 0 0 0
    n 72 0 59 0 58 4
WEEK 8





    Normal 71 (100%) 0 53 (95%) 3 (5.4%) 52 (95%) 3 (5.5%)
    High 0 0 0 0 0 0
    n 71 0 53 3 52 3
WEEK 12





    Normal 65 (100%) 0 46 (96%) 2 (4.2%) 49 (100%) 0
    High 0 0 0 0 0 0
    n 65 0 46 2 49 0
WEEK 16





    Normal 67 (100%) 0 39 (100%) 0 35 (97%) 1 (2.8%)
    High 0 0 0 0 0 0
    n 67 0 39 0 35 1
WEEK 20





    Normal 64 (100%) 0 29 (100%) 0 31 (100%) 0
    High 0 0 0 0 0 0
    n 64 0 29 0 31 0
WEEK 24





    Normal 57 (100%) 0 24 (100%) 0 29 (100%) 0
    High 0 0 0 0 0 0
    n 57 0 24 0 29 0
WEEK 26





    Normal 56 (100%) 0 24 (100%) 0 26 (100%) 0
    High 0 0 0 0 0 0
    n 56 0 24 0 26 0
1 Only subjects with baseline results are included in the summary.
There were no subjects with abnormal low values at baseline.
Caution

For the upcoming tables, we will only display 2 chemistry and 2 hematology variables to highlight the code without making the table excessively long.

Table 14-6-05

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbc_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbc.xpt')  

adlbh_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbh.xpt')

# COMBINE & SUBSEST
adlb <- bind_rows(
   CHEMISTRY  = adlbc_orig,
   HEMATOLOGY = adlbh_orig,
   .id = 'ORIG') %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y') %>% 
   filter(!is.na(PARAM), !is.na(TRTP), !is.na(BNRIND), !is.na(ANRIND), AVISITN != 99) 

adlb_n <- adlb %>% 
   mutate(ANRIND = factor(ANRIND, levels = c('N','H'), labels = c('Normal','High') ) ,
          BNRIND = factor(BNRIND, levels = c('N','H'), labels = c('Normal','High')),
          TRTP = factor(TRTP, levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose')) ) %>% 
   select(ORIG, PARAM, PARAMCD, USUBJID, TRTP, ANRIND, BNRIND) %>% 
   drop_na(ANRIND, BNRIND) %>% 
   mutate(ALL = 1) %>% 
   filter(!str_starts(PARAMCD, '_') ) %>% 
   mutate(PARAM = str_remove_all(PARAM, '\\(.*?\\)') %>% str_trim() ) %>% 
   filter(PARAM %in% c('Alanine Aminotransferase', 
                       'Albumin', 
                       'Alkaline Phosphatase',
                       'Aspartate Aminotransferase',
                       'Bilirubin',
                       'Basophils', 
                       'Eosinophils',
                       'Hemoglobin',
                       'Lymphocytes')) %>% 
   nest_by(ORIG, PARAM, PARAMCD)

# CMH FUNCTION
my_cmh <- function(data, variable, by, ...) {
   table( data[['ANRIND']],
          data[['TRTP']],
          data[['BNRIND']] ) %>% 
      mantelhaen.test() %>% 
      broom::tidy() %>% 
      select(method, p.value)
}

# SUMMARISE: STACK
adlb_t <- adlb_n %>% 
   mutate(
      t = list(
         tbl_summary(
            data = data %>% mutate(inter = interaction(BNRIND, TRTP) ),
            by = inter,
            label = list(ALL ~ 'n'),
            include = c(ALL, ANRIND),
            statistic = list(
               ALL ~ '{n}',
               ANRIND ~ '{n} ({p}%)'),
            digits = list(
               ANRIND ~ c(0, 0)) ) %>% 
            add_stat(fns = list( ALL ~ my_cmh) ) )
   ) %>% 
   group_by(ORIG) %>% 
   summarise(t_orig = tbl_stack(tbls = t, group_header = PARAM, quiet = TRUE) %>% list() )

# PRINT
#+ results = 'asis'
for(i in 1){
   adlb_t$t_orig[[1]]  %>% 
      modify_table_body(
         ~.x %>% 
            filter(!(row_type == 'label' & label != 'n')) %>% 
            mutate(across(starts_with('stat_'), ~str_replace_all(.x, '0 \\(0%\\)|0 \\(NA%\\)', '0'))) %>% 
            janitor::remove_empty(which = 'cols')
      ) %>% 
      modify_header(
         label ~ ' ',
         c(stat_1, stat_3, stat_5) ~ '**Normal at<br>Baseline**',
         c(stat_2, stat_4, stat_6) ~ '**High at<br>Baseline**',
         p.value ~ '**p-value**'
      ) %>% 
      modify_spanning_header(
         c(stat_1, stat_2) ~ '**Placebo (N=86)**',
         c(stat_3, stat_4) ~ '**Xan. Low (N=84)**',
         c(stat_5, stat_6) ~ '**Xan. High (N=84)**'
      ) %>% 
      modify_column_hide(method) %>% 
      modify_column_alignment(
         all_stat_cols(), 'left'
      ) %>% 
      modify_fmt_fun( c(p.value) ~ function(x) style_pvalue(x, digits = 3) ) %>% 
      modify_footnote(
         all_stat_cols() ~ 
            'NOTES: Only subjects with baseline results are included in the summary.<br>
      There were no subjects with abnormal low values at baseline.<br>
      A subject is counted only once for each analyte. A change will be considered <br>
      shifting from normal at baseline to abnormal or from abnormal at baseline to normal at any visit during the treatment.<br>
      The treatment period is defined as any planned visit after Week 0 (Visit 3), up to and including Week 24 (Visit 12).',
      p.value ~ 
         'CMH test for general association, controlling for status at baseline.') %>% 
      modify_column_indent(columns = label, undo = TRUE) %>% 
      modify_caption(
         '**Table 14-6.05 {adlb_t$ORIG[[i]]} <br>Shifts of Laboratory Values During Treatment, Categorized Based on Threshold Ranges**') %>% 
      bstfun::bold_italicize_group_labels(bold = TRUE) %>% 
      knitr::knit_print() %>% 
      cat()
}
Table 14-6.05 CHEMISTRY
Shifts of Laboratory Values During Treatment, Categorized Based on Threshold Ranges
Placebo (N=86) Xan. Low (N=84) Xan. High (N=84) p-value2
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Alanine Aminotransferase
n 82 2 79 1 80 0 0.828
Normal 80 (98%) 0 78 (99%) 0 78 (98%) 0
High 2 (2%) 2 (100%) 1 (1%) 1 (100%) 2 (3%) 0
Albumin
n 84 0 80 0 80 0
Normal 84 (100%) 0 80 (100%) 0 80 (100%) 0
High 0 0 0 0 0 0
Alkaline Phosphatase
n 82 2 79 0 79 1 0.611
Normal 81 (99%) 0 78 (99%) 0 79 (100%) 0
High 1 (1%) 2 (100%) 1 (1%) 0 0 1 (100%)
Aspartate Aminotransferase
n 82 2 79 1 80 0 0.770
Normal 79 (96%) 1 (50%) 77 (97%) 1 (100%) 78 (98%) 0
High 3 (4%) 1 (50%) 2 (3%) 0 2 (3%) 0
Bilirubin
n 84 0 79 0 79 1
Normal 83 (99%) 0 79 (100%) 0 77 (97%) 0
High 1 (1%) 0 0 0 2 (3%) 1 (100%)
1 NOTES: Only subjects with baseline results are included in the summary.
There were no subjects with abnormal low values at baseline.
A subject is counted only once for each analyte. A change will be considered
shifting from normal at baseline to abnormal or from abnormal at baseline to normal at any visit during the treatment.
The treatment period is defined as any planned visit after Week 0 (Visit 3), up to and including Week 24 (Visit 12).
2 CMH test for general association, controlling for status at baseline.

Table 14-6-06

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adlbhy_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adlbhy.xpt')  

# COMBINE & SUBSEST
adlbhy <-adlbhy_orig  %>% 
   filter(SAFFL == 'Y', PARAMCD %in% c('TRANSHY', 'HYLAW'), !is.na(BASE), AVISITN > 0) %>% 
   group_by(USUBJID) %>%
   filter(AVAL    == max(AVAL)) %>%
   filter(AVISITN == max(AVISITN)) %>%
   ungroup()

adlbhy2 <- adlbhy %>%
   select(USUBJID, PARAMCD, PARAM, TRTP, BASE, AVAL) %>% 
   mutate(AVAL = factor(AVAL, labels = c('Normal','High') ) ,
          BASE = factor(BASE, labels = c('Normal','High')),
          TRTP = factor(TRTP, levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose')) ) %>% 
   mutate(ALL = 1)

adlbhy_n <- adlbhy2 %>% 
   nest_by(PARAM, PARAMCD) %>% 
   arrange(desc(PARAM))

# CMH FUNCTION
my_cmh <- function(data, variable, by, ...) {
   table( data[['BASE']],
          data[['TRTP']],
          data[['AVAL']] ) %>% 
      mantelhaen.test() %>% 
      broom::tidy() %>% 
      select(method, p.value)
}

# SUMMARISE: STACK
adlbhy_t <- adlbhy_n %>% 
   mutate(
      t = list(
         tbl_summary(
            data = data %>% mutate(inter = interaction(BASE, TRTP) ),
            by = inter,
            label = list(ALL ~ 'n'),
            include = c(ALL, AVAL),
            statistic = list(
               ALL ~ '{n}',
               AVAL ~ '{n} ({p}%)'
            ),
            digits = list(
               AVAL ~ c(0, 0))) %>% 
            add_stat(fns = list( ALL ~ my_cmh) ) 
      )
   ) 

# PRINT
with(adlbhy_t,
     tbl_stack(tbls = t, group_header = PARAM, quiet = TRUE) )  %>% 
   modify_table_body(
      ~.x %>% 
         filter(!(row_type == 'label' & label != 'n')) %>% 
         mutate(across(starts_with('stat_'), ~str_replace_all(.x, '0 \\(0%\\)|0 \\(NA%\\)', '0'))) %>% 
         janitor::remove_empty(which = 'cols')
   ) %>% 
   modify_header(
      label ~ ' ',
      c(stat_1, stat_3, stat_5) ~ '**Normal at<br>Baseline**',
      c(stat_2, stat_4, stat_6) ~ '**High at<br>Baseline**',
      p.value ~ '**p-value**'
   ) %>% 
   modify_spanning_header(
      c(stat_1, stat_2) ~ '**Placebo (N=86)**',
      c(stat_3, stat_4) ~ '**Xan. Low (N=84)**',
      c(stat_5, stat_6) ~ '**Xan. High (N=84)**'
   ) %>% 
   modify_column_hide(method) %>% 
   modify_column_alignment(
      all_stat_cols(), 'left'
   ) %>%
   modify_fmt_fun( c(p.value) ~ function(x) style_pvalue(x, digits = 3) ) %>% 
   modify_footnote(
      all_stat_cols() ~ 
         'NOTES: Only subjects with baseline results are included in the summary.<br>
      The single subject with elevated transaminase and elevated bilirubin also had elevated alk phos (>3xULN).<br>
      There were no subjects with abnormal low values at baseline.<br>
      A subject is counted only once for each analyte. A change will be considered shifting from normal at<br>
      baseline to abnormal or from abnormal at baseline to normal at any visit during the treatment.<br> 
      The treatment period is defined as any planned visit after Week 0 (Visit 3), up to and including Week 24 (Visit 12).',
      p.value ~ 'CMH test for general association, controlling for status at baseline.') %>% 
   modify_column_indent(columns = label, undo = TRUE) %>% 
   modify_caption(
      "**Table 14-6.06<br>Shifts of Hy's Law Values During Treatment**") %>% 
   bstfun::bold_italicize_group_labels(bold = TRUE)
Table 14-6.06
Shifts of Hy’s Law Values During Treatment
Placebo (N=86) Xan. Low (N=84) Xan. High (N=84) p-value2
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Normal at
Baseline
1
High at
Baseline
1
Transaminase 1.5 x ULN
n 82 2 78 2 80 0 0.232
Normal 79 (96%) 0 77 (99%) 1 (50%) 77 (96%) 0
High 3 (4%) 2 (100%) 1 (1%) 1 (50%) 3 (4%) 0
Total Bili 1.5 x ULN and Transaminase 1.5 x ULN
n 80 0 78 0 77 0
Normal 79 (99%) 0 78 (100%) 0 77 (100%) 0
High 1 (1%) 0 0 0 0 0
1 NOTES: Only subjects with baseline results are included in the summary.
The single subject with elevated transaminase and elevated bilirubin also had elevated alk phos (>3xULN).
There were no subjects with abnormal low values at baseline.
A subject is counted only once for each analyte. A change will be considered shifting from normal at
baseline to abnormal or from abnormal at baseline to normal at any visit during the treatment.
The treatment period is defined as any planned visit after Week 0 (Visit 3), up to and including Week 24 (Visit 12).
2 CMH test for general association, controlling for status at baseline.

Table 14-7

Table 14-7-01

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
advs_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/advs.xpt')

advs <- advs_orig %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y') %>% 
   mutate(EOTFL = ifelse(AVISIT == 'End of Treatment', 'Y', ''),
          W24FL = ifelse(AVISIT == 'Week 24', 'Y', '') ) %>% 
   filter(EOTFL == 'Y' | W24FL == 'Y' | ABLFL == 'Y') %>%
   filter(PARAM %in% c('Diastolic Blood Pressure (mmHg)',
                       'Pulse Rate (BEATS/MIN)',
                       'Systolic Blood Pressure (mmHg)')) %>% 
   mutate(TRTP = factor(TRTP,
                        levels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'),
                        labels = c('Placebo', 'Xan. Low', 'Xan. High')),
          AVISIT = factor(AVISIT,
                          levels = c('Baseline', 'Week 24', 'End of Treatment'),
                          labels = c('Baseline','Week 24', 'End of Trt.')),
          PARAM = factor(PARAM,
                         levels = c('Systolic Blood Pressure (mmHg)',
                                    'Diastolic Blood Pressure (mmHg)',
                                    'Pulse Rate (BEATS/MIN)'),
                         labels = c('Systolic Blood Pressure (mmHg)',
                                    'Diastolic Blood Pressure (mmHg)',
                                    'Pulse (bpm)'))
   ) %>% 
   select( PARAM, PARAMCD, USUBJID, ATPT, TRTP, AVISIT, AVAL)


# NEST
advs_n <- advs %>% 
   nest_by(PARAM, PARAMCD, ATPT, TRTP, AVISIT)

# STATS
my_stats <- function(data, variable, ...) {
   data %>% 
      select({{variable}}) %>% 
      drop_na() %>% 
      summarise( # n = length(AVAL),
         Mean = mean(AVAL),
         SD = sd(AVAL),
         Median = median(AVAL),
         `Min.` = min(AVAL),
         `Max.` = max(AVAL))
}

# TABLES
advs_t <- advs_n %>% 
   mutate( t = list( 
      tbl_summary(
         data = data,
         include = AVAL,
         label = list(AVAL ~ str_glue('{AVISIT}')),
         statistic = list(everything() ~ '{N_nonmiss}'),
         missing = 'no'
      ) %>% 
         add_stat(
            fns = everything() ~ my_stats
         )
   )
   )

# STACK
t <- with(advs_t,       
          tbl_stack(t, group_header = str_glue('{PARAM}_{ATPT}_{TRTP}'), quiet = TRUE) )

# MODIFY OBJECT TABLE
t$table_body <- t$table_body %>% 
   separate(groupname_col, 
            into = c('groupname_col_1',
                     'groupname_col_2',
                     'groupname_col_3' ),
            sep = '_') %>% 
   mutate(across(starts_with('groupname_col_'), 
                 ~case_when(
                    is.na(lag(.x))             ~ .x,
                    .x == lag(.x) ~ NA_character_, TRUE ~ .x) ) 
   )

# MODIFY OBJECT STYLING HEADER
t$table_styling$header <- 
   t$table_styling$header %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_1', label = 'Measure') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_2', label = 'Position') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_3', label = 'Treatment') ) %>% 
   slice(-1)

# PRINT
t %>% 
   modify_fmt_fun(
      list(
         Mean ~ \(x) style_number(x, digits = 1),
         SD ~ \(x) style_number(x, digits = 2),
         c('Median','Min.','Max.') ~ \(x) style_number(x, digits = 1) ) ) %>% 
   modify_header(
      label ~ '**Planned<br>Relative<br>Time**',
      stat_0 ~ '**n**',
      Mean ~ '**Mean**',              
      SD ~ '**SD**',                 
      Median~ '**Median**',             
      Min. ~ '**Min.**',               
      Max. ~ '**Max.**',  
      groupname_col_1  ~ '**Measure**',
      groupname_col_2 ~ '**Position**',
      groupname_col_3 ~ '**Treatment**') %>% 
   modify_table_styling(
      columns = label,
      rows = label == 'End of Trt.',
      footnote = 'End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).' ) %>% 
   modify_footnote(
      all_stat_cols() ~ NA) %>% 
   modify_column_alignment(where(is.numeric), 'right') %>% 
   modify_caption(
      '**Table 14-7.01<br>Summary of Vital Signs at Baseline and End of Treatment**')
Table 14-7.01
Summary of Vital Signs at Baseline and End of Treatment
Measure Position Treatment Planned
Relative
Time
n Mean SD Median Min. Max.
Systolic Blood Pressure (mmHg) AFTER LYING DOWN FOR 5 MINUTES Placebo Baseline 85 138.6 16.75 140.0 90.0 180.0



Week 24 59 135.8 17.30 131.0 100.0 180.0



End of Trt.1 82 134.9 19.44 132.5 88.0 180.0


Xan. Low Baseline 84 138.8 16.55 138.0 100.0 178.0



Week 24 27 134.1 16.74 136.0 100.0 173.0



End of Trt.1 72 135.5 17.22 133.0 102.0 190.0


Xan. High Baseline 84 140.1 17.82 141.0 100.0 188.0



Week 24 30 132.2 18.18 130.0 101.0 178.0



End of Trt.1 72 130.9 17.48 128.0 104.0 176.0

AFTER STANDING FOR 1 MINUTE Placebo Baseline 85 135.3 17.89 134.0 90.0 180.0



Week 24 59 133.5 19.23 130.0 90.0 199.0



End of Trt.1 82 130.9 17.50 130.0 84.0 172.0


Xan. Low Baseline 84 135.6 18.04 136.0 100.0 186.0



Week 24 27 131.0 17.82 130.0 92.0 168.0



End of Trt.1 72 131.1 18.54 130.0 99.0 180.0


Xan. High Baseline 84 137.3 19.71 138.0 100.0 194.0



Week 24 30 130.4 20.83 128.0 96.0 198.0



End of Trt.1 73 127.3 16.51 124.0 99.0 170.0

AFTER STANDING FOR 3 MINUTES Placebo Baseline 85 136.5 18.77 136.0 80.0 184.0



Week 24 59 134.8 17.35 131.0 100.0 190.0



End of Trt.1 82 131.3 19.30 130.0 80.0 176.0


Xan. Low Baseline 84 136.4 18.11 134.5 104.0 182.0



Week 24 27 131.0 17.92 130.0 100.0 168.0



End of Trt.1 72 132.2 18.56 130.0 98.0 200.0


Xan. High Baseline 84 138.8 18.75 138.0 100.0 186.0



Week 24 30 129.2 16.95 126.0 90.0 172.0



End of Trt.1 73 128.1 16.02 128.0 95.0 179.0
Diastolic Blood Pressure (mmHg) AFTER LYING DOWN FOR 5 MINUTES Placebo Baseline 85 75.7 11.09 76.0 40.0 99.0



Week 24 59 72.9 11.32 74.0 44.0 109.0



End of Trt.1 82 74.0 10.39 74.0 46.0 100.0


Xan. Low Baseline 84 76.3 9.77 76.0 57.0 100.0



Week 24 27 76.1 9.14 76.0 60.0 90.0



End of Trt.1 72 73.6 8.33 72.0 56.0 90.0


Xan. High Baseline 84 77.2 9.80 78.0 51.0 98.0



Week 24 30 73.9 9.23 74.0 60.0 92.0



End of Trt.1 73 73.5 9.58 74.0 50.0 94.0

AFTER STANDING FOR 1 MINUTE Placebo Baseline 85 77.9 10.63 78.0 51.0 104.0



Week 24 59 74.2 12.89 74.0 45.0 117.0



End of Trt.1 82 74.3 11.67 75.0 39.0 100.0


Xan. Low Baseline 84 76.2 10.14 78.0 54.0 98.0



Week 24 27 76.3 10.28 78.0 60.0 98.0



End of Trt.1 72 73.9 9.55 73.0 51.0 92.0


Xan. High Baseline 84 78.1 10.77 78.0 56.0 108.0



Week 24 30 74.9 11.00 76.0 50.0 97.0



End of Trt.1 73 76.2 10.49 77.0 50.0 98.0

AFTER STANDING FOR 3 MINUTES Placebo Baseline 85 77.7 11.00 78.0 46.0 110.0



Week 24 59 74.3 11.38 74.0 51.0 110.0



End of Trt.1 82 73.8 11.17 74.0 49.0 100.0


Xan. Low Baseline 84 76.6 10.93 76.0 48.0 108.0



Week 24 27 76.2 10.18 76.0 57.0 98.0



End of Trt.1 72 74.0 9.57 73.0 57.0 102.0


Xan. High Baseline 84 79.6 10.19 80.0 51.0 104.0



Week 24 30 76.0 10.63 78.5 50.0 98.0



End of Trt.1 73 75.8 10.96 78.0 50.0 99.0
Pulse (bpm) AFTER LYING DOWN FOR 5 MINUTES Placebo Baseline 85 70.4 10.46 70.0 51.0 100.0



Week 24 59 69.1 9.46 68.0 50.0 92.0



End of Trt.1 82 70.2 11.14 68.0 50.0 104.0


Xan. Low Baseline 84 68.8 9.52 68.0 50.0 88.0



Week 24 27 68.1 9.28 68.0 52.0 90.0



End of Trt.1 72 67.1 10.47 66.0 48.0 100.0


Xan. High Baseline 84 70.1 9.27 68.0 52.0 98.0



Week 24 30 69.3 11.88 68.0 47.0 96.0



End of Trt.1 72 67.4 10.30 67.0 47.0 92.0

AFTER STANDING FOR 1 MINUTE Placebo Baseline 85 75.5 12.68 76.0 56.0 133.0



Week 24 59 72.8 8.98 74.0 52.0 88.0



End of Trt.1 82 74.5 10.18 75.0 52.0 108.0


Xan. Low Baseline 84 73.5 10.59 72.0 53.0 100.0



Week 24 27 72.1 9.53 74.0 52.0 88.0



End of Trt.1 72 73.1 11.06 72.0 51.0 104.0


Xan. High Baseline 84 75.0 10.89 72.0 56.0 104.0



Week 24 30 73.4 11.93 72.0 54.0 98.0



End of Trt.1 73 72.7 10.61 72.0 52.0 100.0

AFTER STANDING FOR 3 MINUTES Placebo Baseline 85 74.6 11.94 74.0 54.0 134.0



Week 24 59 72.8 8.73 74.0 56.0 88.0



End of Trt.1 82 74.6 10.00 75.5 52.0 100.0


Xan. Low Baseline 84 72.3 10.99 70.0 51.0 104.0



Week 24 27 70.7 10.78 72.0 52.0 96.0



End of Trt.1 72 71.6 9.94 72.0 53.0 97.0


Xan. High Baseline 84 74.0 10.76 72.0 52.0 100.0



Week 24 30 72.4 11.92 71.5 54.0 96.0



End of Trt.1 73 72.5 10.06 72.0 54.0 100.0
1 End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).
Warning

Table 14-7-01. Difference in values for End of Treatment for all groups. The End of Treatment flag is used as is from the ADaM which indicates there are likely discrepancies for end of treatment between the original CDISC Pilot analysis data and the PHUSE CDISC Pilot replication data.

Table 14-7-02

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
advs_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/advs.xpt')

advs <- advs_orig %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y', !is.na(BASE)) %>% 
   mutate(EOTFL = ifelse(AVISIT == 'End of Treatment', 'Y', ''),
          W24FL = ifelse(AVISIT == 'Week 24', 'Y', '') ) %>% 
   filter(EOTFL == 'Y' | W24FL == 'Y' | ABLFL == 'Y') %>%
   filter(PARAM %in% c('Diastolic Blood Pressure (mmHg)',
                       'Pulse Rate (BEATS/MIN)',
                       'Systolic Blood Pressure (mmHg)')) %>% 
   mutate(TRTP = factor(TRTP,
                        levels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'),
                        labels = c('Placebo', 'Xan. Low', 'Xan. High')),
          AVISIT = factor(AVISIT,
                          levels = c('Baseline', 'Week 24', 'End of Treatment'),
                          labels = c('Baseline','Week 24', 'End of Trt.')),
          PARAM = factor(PARAM,
                         levels = c('Systolic Blood Pressure (mmHg)',
                                    'Diastolic Blood Pressure (mmHg)',
                                    'Pulse Rate (BEATS/MIN)'),
                         labels = c('Systolic Blood Pressure (mmHg)',
                                    'Diastolic Blood Pressure (mmHg)',
                                    'Pulse (bpm)'))) %>% 
   filter(AVISIT != 'Baseline') %>% 
   select( PARAM, PARAMCD, USUBJID, ATPT, TRTP, AVISIT, CHG)

# NEST
advs_n <- advs %>% 
   nest_by(PARAM, PARAMCD, ATPT, TRTP, AVISIT)

# STATS
my_stats <- function(data, variable, ...) {
   data %>% 
      select({{variable}}) %>% 
      drop_na() %>% 
      summarise( # n = length(AVAL),
         Mean = mean(CHG),
         SD = sd(CHG),
         Median = median(CHG),
         `Min.` = min(CHG),
         `Max.` = max(CHG))}

# TABLES
advs_t <- advs_n %>% 
   mutate( t = list( 
      tbl_summary(
         data = data,
         include = CHG,
         label = list(CHG ~ str_glue('{AVISIT}')),
         statistic = list(everything() ~ '{N_nonmiss}'),
         missing = 'no') %>% 
         add_stat(
            fns = everything() ~ my_stats))
   )

# STACK
t <- with(advs_t,       
          tbl_stack(t, group_header = str_glue('{PARAM}_{ATPT}_{TRTP}'), quiet = TRUE))

# MODIFY OBJECT TABLE
t$table_body <- t$table_body %>% 
   separate(groupname_col, 
            into = c('groupname_col_1',
                     'groupname_col_2',
                     'groupname_col_3' ),
            sep = '_') %>% 
   mutate(groupname_col_4 = case_match(
      groupname_col_3,
      'Placebo' ~ '86',
      'Xan. Low' ~ '84',
      'Xan. High' ~ '84 '), .after = groupname_col_3) %>% 
   mutate(across(starts_with('groupname_col_'), 
                 ~case_when(
                    is.na(lag(.x))             ~ .x,
                    .x == lag(.x) ~ NA_character_, TRUE ~ .x)) ) 

# MODIFY OBJECT STYLING HEADER
t$table_styling$header <- 
   t$table_styling$header %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_1', label = 'Measure') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_2', label = 'Position') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_3', label = 'Treatment') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_4', label = 'N') ) %>% 
   slice(-1)

# PRINT
t %>% 
   modify_fmt_fun(
      list(
         Mean ~ \(x) style_number(x, digits = 1),
         SD ~ \(x) style_number(x, digits = 2),
         c('Median','Min.','Max.') ~ \(x) style_number(x, digits = 1))) %>% 
   modify_header(
      label ~ '**Planned<br>Relative<br>Time**',
      stat_0 ~ '**n**',
      Mean ~ '**Mean**',              
      SD ~ '**SD**',                 
      Median~ '**Median**',             
      Min. ~ '**Min.**',               
      Max. ~ '**Max.**',  
      groupname_col_1  ~ '**Measure**',
      groupname_col_2 ~ '**Position**',
      groupname_col_3 ~ '**Treatment**',
      groupname_col_4 ~ '**N**') %>% 
   modify_table_styling(
      columns = label,
      rows = label == 'End of Trt.',
      footnote = 'End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).') %>% 
   modify_footnote(all_stat_cols() ~ NA) %>% 
   modify_column_alignment(where(is.numeric), 'right') %>% 
   modify_caption(
      '**Table 14-7.02<br>Summary of Vital Signs Change from Baseline at End of Treatment**')
Table 14-7.02
Summary of Vital Signs Change from Baseline at End of Treatment
Measure Position Treatment N Planned
Relative
Time
n Mean SD Median Min. Max.
Systolic Blood Pressure (mmHg) AFTER LYING DOWN FOR 5 MINUTES Placebo 86 Week 24 58 -2.1 14.73 -4.0 -28.0 50.0




End of Trt.1 81 -3.7 18.98 -5.0 -46.0 48.0


Xan. Low 84 Week 24 27 -0.3 17.19 2.0 -48.0 30.0




End of Trt.1 72 -3.5 16.69 -4.0 -42.0 34.0


Xan. High 84 Week 24 30 -5.6 17.18 -7.0 -36.0 26.0




End of Trt.1 72 -8.9 16.54 -10.0 -54.0 30.0

AFTER STANDING FOR 1 MINUTE Placebo 86 Week 24 58 -1.7 16.87 0.0 -32.0 40.0




End of Trt.1 81 -4.9 18.42 -2.0 -48.0 48.0


Xan. Low 84 Week 24 27 -0.1 17.73 -1.0 -30.0 48.0




End of Trt.1 72 -4.3 16.61 -3.0 -52.0 40.0


Xan. High 84 Week 24 30 -6.3 19.49 -9.0 -36.0 42.0




End of Trt.1 73 -10.0 18.41 -14.0 -62.0 35.0

AFTER STANDING FOR 3 MINUTES Placebo 86 Week 24 58 -1.0 15.80 -3.5 -36.0 38.0




End of Trt.1 81 -5.5 17.57 -6.0 -50.0 48.0


Xan. Low 84 Week 24 27 -0.1 16.20 0.0 -30.0 30.0




End of Trt.1 72 -3.9 16.83 -4.0 -52.0 60.0


Xan. High 84 Week 24 30 -9.0 16.88 -8.0 -40.0 30.0




End of Trt.1 73 -10.3 16.79 -10.0 -48.0 30.0
Diastolic Blood Pressure (mmHg) AFTER LYING DOWN FOR 5 MINUTES Placebo 86 Week 24 58 -0.8 10.82 -0.5 -18.0 41.0




End of Trt.1 81 -1.5 9.41 -1.0 -24.0 20.0


Xan. Low 84 Week 24 27 -0.9 7.71 -2.0 -20.0 16.0




End of Trt.1 72 -3.5 8.56 -2.0 -30.0 16.0


Xan. High 84 Week 24 30 -2.2 9.20 -1.0 -20.0 21.0




End of Trt.1 73 -3.5 9.20 -4.0 -24.0 20.0

AFTER STANDING FOR 1 MINUTE Placebo 86 Week 24 58 -2.3 10.08 -4.0 -23.0 24.0




End of Trt.1 81 -3.3 9.96 -2.0 -27.0 24.0


Xan. Low 84 Week 24 27 1.0 7.30 2.0 -20.0 18.0




End of Trt.1 72 -2.2 9.20 -2.0 -30.0 20.0


Xan. High 84 Week 24 30 -2.3 10.85 -7.0 -18.0 22.0




End of Trt.1 73 -2.2 11.77 -1.0 -34.0 22.0

AFTER STANDING FOR 3 MINUTES Placebo 86 Week 24 58 -2.3 9.56 -3.5 -22.0 20.0




End of Trt.1 81 -3.9 8.77 -4.0 -26.0 16.0


Xan. Low 84 Week 24 27 -1.6 8.29 0.0 -20.0 10.0




End of Trt.1 72 -3.0 10.19 -2.0 -24.0 38.0


Xan. High 84 Week 24 30 -2.1 9.77 -3.5 -20.0 16.0




End of Trt.1 73 -3.7 10.47 -2.0 -40.0 20.0
Pulse (bpm) AFTER LYING DOWN FOR 5 MINUTES Placebo 86 Week 24 58 -0.3 8.77 -1.0 -24.0 24.0




End of Trt.1 81 0.1 10.48 0.0 -24.0 40.0


Xan. Low 84 Week 24 27 -1.6 10.53 0.0 -24.0 25.0




End of Trt.1 72 -1.4 11.40 -2.0 -24.0 32.0


Xan. High 84 Week 24 30 -2.0 11.16 -2.0 -34.0 20.0




End of Trt.1 72 -2.1 9.13 -2.0 -26.0 24.0

AFTER STANDING FOR 1 MINUTE Placebo 86 Week 24 58 -1.7 11.72 0.5 -53.0 18.0




End of Trt.1 81 -0.9 11.72 -2.0 -45.0 44.0


Xan. Low 84 Week 24 27 -1.3 9.05 0.0 -20.0 12.0




End of Trt.1 72 0.0 13.09 -1.5 -24.0 34.0


Xan. High 84 Week 24 30 -1.8 13.41 -3.0 -36.0 20.0




End of Trt.1 73 -1.6 9.57 -2.0 -28.0 22.0

AFTER STANDING FOR 3 MINUTES Placebo 86 Week 24 58 -1.5 10.47 0.0 -46.0 14.0




End of Trt.1 81 0.1 11.09 0.0 -46.0 32.0


Xan. Low 84 Week 24 27 -2.1 8.77 -2.0 -20.0 16.0




End of Trt.1 72 -0.2 11.71 -1.0 -26.0 29.0


Xan. High 84 Week 24 30 -2.7 11.12 -2.0 -40.0 14.0




End of Trt.1 73 -1.2 9.36 0.0 -32.0 22.0
1 End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).
Warning

Table 14-7-02. Difference in values throughout table. The End of Treatment flag is used as is from the ADaM which indicates there are likely discrepancies for end of treatment between the original CDISC Pilot analysis data and the PHUSE CDISC Pilot replication data.

Table 14-7-03

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
advs_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/advs.xpt')

advs <- advs_orig %>% 
   filter(SAFFL == 'Y', ANL01FL == 'Y', !is.na(BASE)) %>% 
   mutate(EOTFL = ifelse(AVISIT == 'End of Treatment', 'Y', ''),
          W24FL = ifelse(AVISIT == 'Week 24', 'Y', '') ) %>% 
   filter(EOTFL == 'Y' | W24FL == 'Y' | ABLFL == 'Y') %>%
   filter(PARAM %in% c('Weight (kg)')) %>% 
   mutate(TRTP = factor(TRTP,
                        levels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'),
                        labels = c('Placebo', 'Xan. Low', 'Xan. High')),
          AVISIT = factor(AVISIT,
                          levels = c('Baseline', 'Week 24', 'End of Treatment'),
                          labels = c('Baseline','Week 24', 'End of Trt.'))) %>% 
   select( PARAM, PARAMCD, USUBJID, TRTP, AVISIT, AVAL, CHG)

advs_l <- advs %>% 
   pivot_longer( cols = c('AVAL','CHG'),
                 names_to = 'VAR',
                 values_to = 'AVAL') %>% 
   filter(!(VAR == 'CHG' & AVISIT == 'Baseline'))

# NEST
advs_n <- advs_l %>% 
   nest_by(PARAM, PARAMCD, VAR, TRTP, AVISIT)

# STATS
my_stats <- function(data, variable, ...) {
   data %>% 
      select({{variable}}) %>% 
      drop_na() %>% 
      summarise( # n = length(AVAL),
         Mean = mean(AVAL),
         SD = sd(AVAL),
         Median = median(AVAL),
         `Min.` = min(AVAL),
         `Max.` = max(AVAL))
}

# TABLES
advs_t <- advs_n %>% 
   mutate( t = list( 
      tbl_summary(
         data = data,
         include = AVAL,
         label = list(AVAL ~ str_glue('{AVISIT}')),
         statistic = list(everything() ~ '{N_nonmiss}'),
         missing = 'no') %>% 
         add_stat(
            fns = everything() ~ my_stats))
   )

# STACK
t <- with(advs_t,       
          tbl_stack(t, group_header = str_glue('{PARAM}_{VAR}_{TRTP}'), quiet = TRUE))

# MODIFY OBJECT TABLE
t$table_body <- t$table_body %>% 
   separate(groupname_col, 
            into = c('groupname_col_1',
                     'groupname_col_2',
                     'groupname_col_3' ),
            sep = '_') %>% 
   mutate(groupname_col_4 = case_match(
      groupname_col_3,
      'Placebo' ~ '86',
      'Xan. Low' ~ '84',
      'Xan. High' ~ '84 '), .after = groupname_col_3) %>% 
   mutate(across(starts_with('groupname_col_'), 
                 ~case_when(
                    is.na(lag(.x))             ~ .x,
                    .x == lag(.x) ~ NA_character_, TRUE ~ .x)) ) 

# MODIFY OBJECT STYLING HEADER
t$table_styling$header <- 
   t$table_styling$header %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_1', label = 'Measure') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_2', label = 'Position') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_3', label = 'Treatment') ) %>% 
   add_row( t$table_styling$header[1,] %>% mutate(column = 'groupname_col_4', label = 'N') ) %>% 
   slice(-1)

# PRINT
t %>% 
   modify_fmt_fun(
      list(
         Mean ~ \(x) style_number(x, digits = 1),
         SD ~ \(x) style_number(x, digits = 2),
         c('Median','Min.','Max.') ~ \(x) style_number(x, digits = 1))) %>% 
   modify_header(
      label ~ '**Planned<br>Relative<br>Time**',
      stat_0 ~ '**n**',
      Mean ~ '**Mean**',              
      SD ~ '**SD**',                 
      Median~ '**Median**',             
      Min. ~ '**Min.**',               
      Max. ~ '**Max.**',  
      groupname_col_1  ~ '**Measure**',
      groupname_col_2 ~ '**Position**',
      groupname_col_3 ~ '**Treatment**',
      groupname_col_4 ~ '**N**') %>% 
   modify_table_styling(
      columns = label,
      rows = label == 'End of Trt.',
      footnote = 'End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).') %>% 
   modify_footnote(all_stat_cols() ~ NA) %>% 
   modify_column_alignment(where(is.numeric), 'right') %>% 
   modify_caption(
      '**Table 14-7.03<br>Summary of Weight Change from Baseline at End of Treatment**')
Table 14-7.03
Summary of Weight Change from Baseline at End of Treatment
Measure Position Treatment N Planned
Relative
Time
n Mean SD Median Min. Max.
Weight (kg) AVAL Placebo 86 Baseline 86 62.8 12.77 60.6 34.0 86.2




Week 24 59 63.2 12.58 63.5 34.0 86.6




End of Trt.1 82 63.0 12.71 63.1 34.5 87.1


Xan. Low 84 Baseline 83 67.3 14.13 64.9 45.4 106.1




Week 24 27 67.4 14.07 62.6 45.5 106.1




End of Trt.1 71 67.0 14.30 66.2 41.7 104.8


Xan. High 84 Baseline 84 70.0 14.65 69.2 41.7 108.0




Week 24 30 71.1 15.82 68.7 49.9 105.7




End of Trt.1 72 69.3 14.48 68.0 42.2 105.2

CHG Placebo 86 Week 24 59 0.1 2.30 0.0 -4.5 8.2




End of Trt.1 82 0.0 1.89 0.0 -5.0 5.0


Xan. Low 84 Week 24 27 -0.3 2.04 0.0 -5.4 3.2




End of Trt.1 71 -0.3 2.73 0.0 -14.5 5.9


Xan. High 84 Week 24 30 1.0 6.47 -0.2 -4.5 33.3




End of Trt.1 72 0.1 4.82 -0.3 -5.9 37.0
1 End of treatment is the last on-treatment assessment of the specified vital sign (on or before the Week 24 visit).
Warning

Table 14-7-03. Difference in values for End of Treatment for all groups. The End of Treatment flag is used as is from the ADaM which indicates there are likely discrepancies for end of treatment between the original CDISC Pilot analysis data and the PHUSE CDISC Pilot replication data.

Table 14-7-04

Code
##+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven, labelled)
pacman::p_load(gtreg, gtsummary)

# THEME
theme_gtsummary_language('en', big.mark = '')
theme_gtsummary_compact()

# READ
adsl_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adsl.xpt') %>% 
   mutate(ARM = factor(ARM,
                       levels = c('Placebo', 'Xanomeline Low Dose', 'Xanomeline High Dose'),
                       labels = c('Placebo<br>', 'Xanomeline<br>Low Dose', 'Xanomeline<br>High Dose')))

cm_orig <- haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/tabulations/sdtm/cm.xpt')

cm <- left_join(
   cm_orig,
   adsl_orig %>% 
      select(USUBJID, ARM) ) %>% 
   mutate(CMDECOD_ALL = 'Patients receiving at least one concomitant medication') 

t1 <- tbl_ae(
   data = cm,
   id = USUBJID,
   ae = CMDECOD_ALL,
   strata = ARM,
   id_df = adsl_orig,
   statistic = '{n} ({p}%)',
   digits = c(0, 0),
   zero_symbol = '0') %>% 
   modify_header(
      label ~ '**Therapeutic class, n (%)**'
   )

t2 <- tbl_ae(
   data = cm,
   id = USUBJID,
   ae = CMDECOD,
   soc = CMCLAS,
   strata = ARM,
   id_df = adsl_orig,
   statistic = '{n} ({p}%)',
   digits = c(0, 0),
   zero_symbol = '0') 

tbl_stack(tbls = list(t1, t2), quiet = TRUE) %>% 
   modify_header(
      label ~ '**Therapeutic class, n (%)**') %>% 
   modify_caption(
      '**Table 14-7.04<br>Summary of Concomitant Medications (Number of Subjects)**')
Table 14-7.04
Summary of Concomitant Medications (Number of Subjects)
Therapeutic class, n (%) Placebo
, N = 86
Xanomeline
Low Dose
, N = 84
Xanomeline
High Dose
, N = 84
Overall Overall Overall
Patients receiving at least one concomitant medication 77 (90%) 74 (88%) 78 (93%)
ALIMENTARY TRACT AND METABOLISM 12 (14%) 11 (13%) 9 (11%)
    ALGELDRATE 2 (2%) 0 2 (2%)
    CALCIUM 7 (8%) 6 (7%) 3 (4%)
    CALCIUM CARBONATE 0 0 1 (1%)
    CIMETIDINE 0 1 (1%) 0
    LOPERAMIDE HYDROCHLORIDE 1 (1%) 1 (1%) 1 (1%)
    METFORMIN HYDROCHLORIDE 1 (1%) 1 (1%) 0
    NIZATIDINE 1 (1%) 1 (1%) 4 (5%)
    SIMETICONE 0 2 (2%) 0
ANTINEOPLASTIC AND IMMUNOMODULATING AGENTS 1 (1%) 0 1 (1%)
    LEUPRORELIN ACETATE 1 (1%) 0 1 (1%)
BLOOD AND BLOOD FORMING ORGANS 0 1 (1%) 0
    FERROUS SULFATE 0 1 (1%) 0
CARDIOVASCULAR SYSTEM 12 (14%) 12 (14%) 7 (8%)
    AMLODIPINE 8 (9%) 1 (1%) 2 (2%)
    DIGOXIN 0 3 (4%) 2 (2%)
    DILTIAZEM HYDROCHLORIDE 0 0 1 (1%)
    DOXAZOSIN MESILATE 1 (1%) 2 (2%) 1 (1%)
    FELODIPINE 0 1 (1%) 0
    FLUVASTATIN 0 2 (2%) 0
    FUROSEMIDE 2 (2%) 2 (2%) 1 (1%)
    LOSARTAN POTASSIUM 0 2 (2%) 0
    NIFEDIPINE 2 (2%) 0 0
DERMATOLOGICALS 0 0 1 (1%)
    CLOBETASOL PROPIONATE 0 0 1 (1%)
GENITO URINARY SYSTEM AND SEX HORMONES 6 (7%) 10 (12%) 5 (6%)
    ESTROGENS CONJUGATED 6 (7%) 10 (12%) 5 (6%)
NERVOUS SYSTEM 23 (27%) 14 (17%) 8 (10%)
    ACETYLSALICYLIC ACID 21 (24%) 11 (13%) 6 (7%)
    ALPRAZOLAM 1 (1%) 0 0
    DONEPEZIL HYDROCHLORIDE 1 (1%) 2 (2%) 2 (2%)
    HALOPERIDOL 0 1 (1%) 0
    PAROXETINE HYDROCHLORIDE 0 1 (1%) 0
    SUMATRIPTAN 1 (1%) 0 0
RESPIRATORY SYSTEM 4 (5%) 1 (1%) 4 (5%)
    BUDESONIDE 0 0 1 (1%)
    GUAIFENESIN 1 (1%) 0 0
    IPRATROPIUM BROMIDE 1 (1%) 0 0
    NAPROXEN SODIUM 1 (1%) 0 3 (4%)
    SALBUTAMOL SULFATE 2 (2%) 1 (1%) 0
SYSTEMIC HORMONAL PREPARATIONS, EXCL. 2 (2%) 13 (15%) 8 (10%)
    HYDROCORTISONE 2 (2%) 13 (15%) 8 (10%)
UNCODED 74 (86%) 70 (83%) 77 (92%)
    UNCODED 74 (86%) 70 (83%) 77 (92%)

Figures

Figure 14-1

Code
#+ message = FALSE
# PACKAGES
pacman::p_load(tidyverse, haven)
pacman::p_load(gtsummary)
pacman::p_load(ggsurvfit)

# THEME
theme_gtsummary_compact()

# READ ADSL
adtte_orig <-  haven::read_xpt(
   'https://raw.githubusercontent.com/cdisc-org/sdtm-adam-pilot-project/master/updated-pilot-submission-package/900172/m5/datasets/cdiscpilot01/analysis/adam/datasets/adtte.xpt')

# UPDATE
adtte <- adtte_orig %>% 
   filter(SAFFL == 'Y') %>% 
   filter(PARAMCD == 'TTDE') %>% 
   mutate(TRTA = factor(TRTA, 
                        levels = c('Placebo','Xanomeline Low Dose','Xanomeline High Dose'),
                        labels = c('Placebo', 'Xanomeline Low Dose','Xanomeline High Dose')))

# FIGURE
survfit2(Surv(AVAL, 1-CNSR) ~ TRTA, data = adtte) |>
   ggsurvfit(linewidth = 1) +
   add_quantile(y_value = 0.5, linetype = 'dotted', color = 'grey30', linewidth = 0.8) +
   add_censor_mark(size = 2,
                   show.legend = FALSE) +
   add_confidence_interval(show.legend = FALSE) +
   add_pvalue(caption = 'Logrank {p.value}',
              location = 'annotation',
              x = 15, y = 0.05,
              size = 3) +
   add_risktable(
      risktable_stats = c('n.risk')
   ) +
   add_risktable_strata_symbol(symbol = '\U25AC', size = 16) +
   scale_ggsurvfit() +
   labs(title = 'KM plot for Time to First Dermatologic Event',
        subtitle = 'Safety population',
        y = 'Survival Probability (%)',
        x = 'Time (Days)') +
   guides(colour = ggh4x::guide_stringlegend(ncol = 1)) +
   theme(legend.position = c(0.99, 0.99),
         legend.justification = c('right','top'),
         legend.background = element_rect(fill = 'transparent'))

Code
# TABLE
tbl_merge(
   tbls = list(
      tbl_summary(
         data = adtte,
         include = TRTA,
         statistic = list(all_categorical() ~ '{n}')
      ) %>% 
         modify_header(all_stat_cols() ~ '**No Subjects**<br>{n}'),
      tbl_summary(
         data = adtte %>% mutate(CNSR = factor(CNSR, label = c('Event','Censored'))),
         by = CNSR,
         include = TRTA,
         statistic = list(all_categorical() ~ '{p}% ({n})'),
         percent = 'row'
      ) %>% 
         modify_header(all_stat_cols() ~ '**{level}**<br>{n} ({style_percent(p)}%)'),
      tbl_survfit(
         survfit2(Surv(AVAL, 1-CNSR) ~ TRTA, data = adtte, conf.type = 'log-log'),
         probs = 0.5,
         label_header = '**Median Survival**',
         estimate_fun  = function(x) style_number(x, digits = 1),
      ) %>% 
         modify_header(all_stat_cols() ~ '**Median Survival (95% CL)**')
   ),
   tab_spanner = FALSE) %>%
   modify_header(
      label ~ '') %>% 
   modify_footnote(
      all_stat_cols() ~ NA,
      stat_1_2 ~ 'Dermatologic events were identified as adverse events associated with skin conditions such as rash, pruritus, dermatitis. A full list of adverse event terms is presented in the final study report') %>% 
   modify_table_body(
      ~.x %>% 
         slice(-1) %>% 
         mutate(row_type = 'label'))
No Subjects
254
Event
152 (60%)1
Censored
102 (40%)
Median Survival (95% CL)
Placebo 86 34% (29) 66% (57) — (—; —)
Xanomeline Low Dose 84 74% (62) 26% (22) 33.0 (27.0; 48.0)
Xanomeline High Dose 84 73% (61) 27% (23) 36.0 (23.0; 46.0)
1 Dermatologic events were identified as adverse events associated with skin conditions such as rash, pruritus, dermatitis. A full list of adverse event terms is presented in the final study report

PACKAGES

SESSION INFO

Code
sessionInfo()
R version 4.3.3 (2024-02-29 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 17763)

Matrix products: default


locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

time zone: America/Los_Angeles
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggsurvfit_1.0.0 labelled_2.12.0 mmrm_0.3.11     gtreg_0.3.0    
 [5] tictoc_1.2      emmeans_1.10.0  gt_0.10.1       gtsummary_1.7.2
 [9] haven_2.5.4     lubridate_1.9.3 forcats_1.0.0   stringr_1.5.1  
[13] dplyr_1.1.4     purrr_1.0.2     readr_2.1.5     tidyr_1.3.1    
[17] tibble_3.2.1    ggplot2_3.5.0   tidyverse_2.0.0

loaded via a namespace (and not attached):
 [1] tidyselect_1.2.0     farver_2.1.1         fastmap_1.1.1       
 [4] TH.data_1.1-2        janitor_2.2.0        pacman_0.5.1        
 [7] broom.helpers_1.14.0 digest_0.6.34        estimability_1.5    
[10] timechange_0.3.0     lifecycle_1.0.4      ellipsis_0.3.2      
[13] survival_3.5-8       magrittr_2.0.3       compiler_4.3.3      
[16] rlang_1.1.3          sass_0.4.8           tools_4.3.3         
[19] utf8_1.2.4           yaml_2.3.8           knitr_1.45          
[22] labeling_0.4.3       htmlwidgets_1.6.4    curl_5.2.1          
[25] here_1.0.1           xml2_1.3.6           multcomp_1.4-25     
[28] bstfun_0.5.1.9002    withr_3.0.0          ggh4x_0.2.8         
[31] grid_4.3.3           fansi_1.0.6          xtable_1.8-4        
[34] colorspace_2.1-0     scales_1.3.0         MASS_7.3-60.0.1     
[37] cli_3.6.2            mvtnorm_1.2-4        rmarkdown_2.26      
[40] generics_0.1.3       rstudioapi_0.15.0    tzdb_0.4.0          
[43] commonmark_1.9.1     splines_4.3.3        parallel_4.3.3      
[46] vctrs_0.6.5          Matrix_1.6-5         sandwich_3.1-0      
[49] jsonlite_1.8.8       patchwork_1.2.0      hms_1.1.3           
[52] glue_1.7.0           codetools_0.2-19     stringi_1.8.3       
[55] gtable_0.3.4         munsell_0.5.0        pillar_1.9.0        
[58] htmltools_0.5.7      R6_2.5.1             TMB_1.9.10          
[61] Rdpack_2.6           rprojroot_2.0.4      evaluate_0.23       
[64] lattice_0.22-5       markdown_1.12        rbibutils_2.2.16    
[67] backports_1.4.1      broom_1.0.5          snakecase_0.11.1    
[70] Rcpp_1.0.12          nlme_3.1-164         checkmate_2.3.1     
[73] xfun_0.42            zoo_1.8-12           pkgconfig_2.0.3