# Program Name: tsfecg01# Prep Environmentlibrary(envsetup)library(tern)library(dplyr)library(rtables)library(junco)# Define script level parameters:tblid <-"TSFECG01"fileid <- tblidtitles <-get_titles_from_file(input_path ='../../_data/', tblid)string_map <- default_str_mappopfl <-"SAFFL"trtvar <-"TRT01A"ctrl_grp <-"Placebo"# Note on ancova parameter# when ancova = TRUE# ancova model will be used to calculate all mean/mean change columns# not just those from the Difference column# model specificationsumm_vars <-list(arm = trtvar, covariates =NULL)# when ancova = FALSE, all mean/mean change columns will be from descriptive stats# for the difference column descriptive stats will be based upon two-sample t-testancova <-FALSEcomp_btw_group <-TRUEselparamcd <-c("EGHRMN", "PRAG", "QRSAG", "QTC", "QTCBAG", "QTCFAG", "RRAG")# see further, an alternative method to identify all non-unscheduled visits based upon dataselvisit <-c("Baseline","Month 1","Month 3","Month 6","Month 9","Month 12","Month 15","Month 18","Month 24")# Process Data:adsl <- pharmaverseadamjnj::adsl %>%filter(.data[[popfl]] =="Y") %>%select( STUDYID, USUBJID,all_of(c(popfl, trtvar)), SEX, AGEGR1, RACE, ETHNIC, AGE )adsl$colspan_trt <-factor(ifelse(adsl[[trtvar]] == ctrl_grp, " ", "Active Study Agent"),levels =c("Active Study Agent", " "))adsl$rrisk_header <-"Difference in Mean Change (95% CI)"adsl$rrisk_label <-paste(adsl[[trtvar]], paste("vs", ctrl_grp))adeg00 <- pharmaverseadamjnj::adeg %>%select( USUBJID, AVISITN, AVISIT, PARAMCD, PARAM, AVAL, BASE, CHG,starts_with("ANL"), ABLFL, APOBLFL# ,EGSTAT ) %>%inner_join(adsl)# selection of all non-unscheduled visits from datavisits <- adeg00 %>%select(AVISIT) %>%filter(!grepl("UNSCHEDULED", toupper(AVISIT)))visits$AVISIT <-droplevels(visits$AVISIT)selvisit_data <-levels(visits$AVISIT)### if preferred to get it from data, rather than hardcoded list of visits# selvisit <- selvisit_data## retrieve the precision of AVAL on the input dataset## review outcome and make updates manually if needed## the precision variable will be used for the parameter-based formats in layout## decimal = 4 is a cap in this derivation: if decimal precision of variable > decimal, the result will end up as decimal## eg if AVAL has precision of 6 for parameter x, and decimal = 4, the resulting decimal value for parameter x is 4## note that precision is on the raw values, as we are presenting mean/ci, and extra digit will be added## eg precision = 2 will result in mean/ci format xx.xxx (xx.xxx, xx.xxx)eg_precision <- tidytlg:::make_precision_data(df = adeg00,decimal =4,precisionby ="PARAMCD",precisionon ="AVAL")### data preparationfiltered_adeg <- adeg00 %>%filter(PARAMCD %in% selparamcd) %>%filter(AVISIT %in% selvisit) %>%### unique record per timepoint:filter(ANL02FL =="Y"& (ABLFL =="Y"| APOBLFL =="Y"))## issue with sysntethic data for Xanomeline Low Dose : 600 records rather than 300check1 <- filtered_adeg %>%group_by(TRT01A, PARAM, AVISIT) %>%summarize(n_rec =n(), n_sub =n_distinct(USUBJID))#### perform check on unique record per subject/param/timepointcheck_unique <- filtered_adeg %>%group_by(USUBJID, PARAMCD, AVISIT) %>%mutate(n_recsub =n()) %>%filter(n_recsub >1)if (nrow(check_unique) >0) {stop("Your input dataset needs extra attention, as some subjects have more than one record per parameter/visit" )### you will run into issues with fraction portion in count_denom_fraction, as count > denom, and fraction > 1 if you don't adjust your input dataset# Possible extra derivation - just to ensure program can run without issues### Study team is responsible for adding this derivation onto ADaM dataset and ensure proper derivation rule for ANL02FL is implemented !!!!!!!!!! filtered_adegx <- adeg00 %>%filter(PARAMCD %in% selparamcd) %>%filter(AVISIT %in% selvisit) %>%### unique record per timepoint:filter(ANL02FL =="Y"& (ABLFL =="Y"| APOBLFL =="Y")) %>%group_by(USUBJID, PARAM, AVISIT) %>%mutate(n_sub =n()) %>%arrange(USUBJID, PARAM, AVISIT, ADT) %>%mutate(i = vctrs::vec_group_id(ADT)) %>%mutate(ANL02FL =case_when( n_sub ==1~"Y", i ==1~"Y" ) ) %>%select(-c(i, n_sub)) %>%ungroup() filtered_adeg <- filtered_adegx %>%filter(PARAMCD %in% selparamcd) %>%filter(AVISIT %in% selvisit) %>%### unique record per timepoint:filter(ANL02FL =="Y"& (ABLFL =="Y"| APOBLFL =="Y"))## now your data should contain 1 record per subject per parameter}### for denominator per timepoint: all records from adeg on this timepoint: ignoring anl01fl/anl02fl/paramfiltered_adeg_timepoints <-unique( adeg00 %>%filter(AVISIT %in% selvisit) %>%select(USUBJID, AVISITN, AVISIT)) %>%inner_join(adsl)params <-unique(filtered_adeg %>%select(PARAMCD, PARAM))filtered_adeg_timepoints <- filtered_adeg_timepoints %>%mutate(dummy_join =1) %>%full_join( params %>%mutate(dummy_join =1),relationship ="many-to-many" ) %>%select(-dummy_join)### identify subjects in filtered_advs_timepoints and not in filtered_advsextra_sub <-anti_join(filtered_adeg_timepoints, filtered_adeg)### only add these extra_sub to### this will ensure we still meet the one record per subject per timepoint### this will ensure length(x) can be used for the denominator derivation inside summarize_aval_chg_diff functionfiltered_adeg <-bind_rows(filtered_adeg, extra_sub) %>%arrange(USUBJID, PARAM, AVISITN)filtered_adeg <- filtered_adeg %>%inner_join(eg_precision, by ="PARAMCD")### important: previous actions lost the label of variables### in order to be able to use obj_label(filtered_advs$PARAM) in layout, need to redefine the labelfiltered_adeg <-var_relabel_list(filtered_adeg, var_labels(adeg00, fill = T))check1 <- filtered_adeg %>%group_by(TRT01A, PARAM, AVISIT) %>%summarize(n_rec =n(), n_sub =n_distinct(USUBJID))colspan_trt_map <-create_colspan_map( adsl,non_active_grp = ctrl_grp,non_active_grp_span_lbl =" ",active_grp_span_lbl ="Active Study Agent",colspan_var ="colspan_trt",trt_var = trtvar)# Define layout and build table:summ_vars <-list(arm = trtvar, covariates =NULL)ref_path <-c("colspan_trt", " ", trtvar, ctrl_grp)multivars <-c("AVAL", "AVAL", "CHG")extra_args_3col <-list(format_na_str =rep("NA", 3),d ="decimal",variables = summ_vars,ref_path = ref_path,ancova = ancova,comp_btw_group = comp_btw_group,multivars = multivars)lyt <-basic_table(show_colcounts =FALSE, colcount_format ="N=xx") %>%### first columnssplit_cols_by("colspan_trt",split_fun =trim_levels_to_map(map = colspan_trt_map) ) %>%split_cols_by(trtvar, show_colcounts =TRUE, colcount_format ="N=xx") %>%split_rows_by("PARAM",label_pos ="topleft",split_label =obj_label(filtered_adeg$PARAM),section_div =" ",split_fun = drop_split_levels ) %>%## note the child_labels = hidden for AVISIT, these labels will be taken care off by## applying function summarize_aval_chg_diff further in the layoutsplit_rows_by("AVISIT",label_pos ="topleft",split_label ="Study Visit",split_fun = drop_split_levels,child_labels ="hidden" ) %>%## set up a 3 column splitsplit_cols_by_multivar( multivars,varlabels =c("n/N (%)","Mean (95% CI)","Mean Change From Baseline (95% CI)" ) ) %>%### restart for the rrisk_header columns - note the nested = FALSE option### also note the child_labels = "hidden" in both PARAM and AVISITsplit_cols_by("rrisk_header", nested =FALSE) %>%split_cols_by( trtvar,split_fun =remove_split_levels(ctrl_grp),labels_var ="rrisk_label",show_colcounts =TRUE,colcount_format ="N=xx" ) %>%### difference columns : just 1 column & analysis needs to be done on changesplit_cols_by_multivar(multivars[3], varlabels =c(" ")) %>%### the variable passed here in analyze is not used (STUDYID), it is a dummy var passing,### the function summarize_aval_chg_diff grabs the required vars from cols_by_multivar callsanalyze("STUDYID",afun = a_summarize_aval_chg_diff_j,extra_args = extra_args_3col )result <-build_table(lyt, filtered_adeg, alt_counts_df = adsl)# Post-Processing:remove_col_count2 <-function(result, string =paste("vs", ctrl_grp)) { mcdf <-make_col_df(result, visible_only =FALSE) mcdfsel <- mcdf %>%filter(stringr::str_detect(toupper(label), toupper(string))) %>%pull(path)for (i inseq_along(mcdfsel)) {facet_colcount(result, mcdfsel[[i]]) <-NA }return(result)}result <-remove_col_count2(result)# Add titles and footnotes:result <-set_titles(result, titles)# Convert to tbl file and output tablett_to_tlgrtf(string_map = string_map, tt = result,file = fileid,nosplitin =list(cols =c(trtvar, "rrisk_header")),orientation ="landscape")
TSFECG01:Mean Change From Baseline for ECG Data Over Time; Safety Analysis Set (Study jjcs - core)
---title: TSFECG01subtitle: Mean Change From Baseline for ECG Data Over Time---------------------------------------------------------------------------{{< include ../../_utils/envir_hook.qmd >}}```{r setup, echo = FALSE, warning = FALSE, message = FALSE}options(docx.add_datetime = FALSE, tidytlg.add_datetime = FALSE)envsetup_config_name <- "default"# Path to the combined config fileenvsetup_file_path <- file.path("../..", "envsetup.yml")Sys.setenv(ENVSETUP_ENVIRON = '')library(envsetup)loaded_config <- config::get(config = envsetup_config_name, file = envsetup_file_path)envsetup::rprofile(loaded_config)dpscomp <- compounddpspdr <- paste(protocol,dbrelease,rpteff,sep="__")aptcomp <- compoundaptpdr <- paste(protocol,dbrelease,rpteff,sep="__")###### Study specific updates (formerly in envre)dpscomp <- "standards"dpspdr <- "jjcs__NULL__jjcs - core"apt <- FALSElibrary(junco)default_str_map <- rbind(default_str_map, c("&ctcae", "5.0"))```## Output:::: panel-tabset## {{< fa regular file-lines sm fw >}} Preview```{r variant1, results='hide', warning = FALSE, message = FALSE}# Program Name: tsfecg01# Prep Environmentlibrary(envsetup)library(tern)library(dplyr)library(rtables)library(junco)# Define script level parameters:tblid <- "TSFECG01"fileid <- tblidtitles <- get_titles_from_file(input_path = '../../_data/', tblid)string_map <- default_str_mappopfl <- "SAFFL"trtvar <- "TRT01A"ctrl_grp <- "Placebo"# Note on ancova parameter# when ancova = TRUE# ancova model will be used to calculate all mean/mean change columns# not just those from the Difference column# model specificationsumm_vars <- list(arm = trtvar, covariates = NULL)# when ancova = FALSE, all mean/mean change columns will be from descriptive stats# for the difference column descriptive stats will be based upon two-sample t-testancova <- FALSEcomp_btw_group <- TRUEselparamcd <- c("EGHRMN", "PRAG", "QRSAG", "QTC", "QTCBAG", "QTCFAG", "RRAG")# see further, an alternative method to identify all non-unscheduled visits based upon dataselvisit <- c( "Baseline", "Month 1", "Month 3", "Month 6", "Month 9", "Month 12", "Month 15", "Month 18", "Month 24")# Process Data:adsl <- pharmaverseadamjnj::adsl %>% filter(.data[[popfl]] == "Y") %>% select( STUDYID, USUBJID, all_of(c(popfl, trtvar)), SEX, AGEGR1, RACE, ETHNIC, AGE )adsl$colspan_trt <- factor( ifelse(adsl[[trtvar]] == ctrl_grp, " ", "Active Study Agent"), levels = c("Active Study Agent", " "))adsl$rrisk_header <- "Difference in Mean Change (95% CI)"adsl$rrisk_label <- paste(adsl[[trtvar]], paste("vs", ctrl_grp))adeg00 <- pharmaverseadamjnj::adeg %>% select( USUBJID, AVISITN, AVISIT, PARAMCD, PARAM, AVAL, BASE, CHG, starts_with("ANL"), ABLFL, APOBLFL # ,EGSTAT ) %>% inner_join(adsl)# selection of all non-unscheduled visits from datavisits <- adeg00 %>% select(AVISIT) %>% filter(!grepl("UNSCHEDULED", toupper(AVISIT)))visits$AVISIT <- droplevels(visits$AVISIT)selvisit_data <- levels(visits$AVISIT)### if preferred to get it from data, rather than hardcoded list of visits# selvisit <- selvisit_data## retrieve the precision of AVAL on the input dataset## review outcome and make updates manually if needed## the precision variable will be used for the parameter-based formats in layout## decimal = 4 is a cap in this derivation: if decimal precision of variable > decimal, the result will end up as decimal## eg if AVAL has precision of 6 for parameter x, and decimal = 4, the resulting decimal value for parameter x is 4## note that precision is on the raw values, as we are presenting mean/ci, and extra digit will be added## eg precision = 2 will result in mean/ci format xx.xxx (xx.xxx, xx.xxx)eg_precision <- tidytlg:::make_precision_data( df = adeg00, decimal = 4, precisionby = "PARAMCD", precisionon = "AVAL")### data preparationfiltered_adeg <- adeg00 %>% filter(PARAMCD %in% selparamcd) %>% filter(AVISIT %in% selvisit) %>% ### unique record per timepoint: filter(ANL02FL == "Y" & (ABLFL == "Y" | APOBLFL == "Y"))## issue with sysntethic data for Xanomeline Low Dose : 600 records rather than 300check1 <- filtered_adeg %>% group_by(TRT01A, PARAM, AVISIT) %>% summarize(n_rec = n(), n_sub = n_distinct(USUBJID))#### perform check on unique record per subject/param/timepointcheck_unique <- filtered_adeg %>% group_by(USUBJID, PARAMCD, AVISIT) %>% mutate(n_recsub = n()) %>% filter(n_recsub > 1)if (nrow(check_unique) > 0) { stop( "Your input dataset needs extra attention, as some subjects have more than one record per parameter/visit" ) ### you will run into issues with fraction portion in count_denom_fraction, as count > denom, and fraction > 1 if you don't adjust your input dataset # Possible extra derivation - just to ensure program can run without issues ### Study team is responsible for adding this derivation onto ADaM dataset and ensure proper derivation rule for ANL02FL is implemented !!!!!!!!!! filtered_adegx <- adeg00 %>% filter(PARAMCD %in% selparamcd) %>% filter(AVISIT %in% selvisit) %>% ### unique record per timepoint: filter(ANL02FL == "Y" & (ABLFL == "Y" | APOBLFL == "Y")) %>% group_by(USUBJID, PARAM, AVISIT) %>% mutate(n_sub = n()) %>% arrange(USUBJID, PARAM, AVISIT, ADT) %>% mutate(i = vctrs::vec_group_id(ADT)) %>% mutate( ANL02FL = case_when( n_sub == 1 ~ "Y", i == 1 ~ "Y" ) ) %>% select(-c(i, n_sub)) %>% ungroup() filtered_adeg <- filtered_adegx %>% filter(PARAMCD %in% selparamcd) %>% filter(AVISIT %in% selvisit) %>% ### unique record per timepoint: filter(ANL02FL == "Y" & (ABLFL == "Y" | APOBLFL == "Y")) ## now your data should contain 1 record per subject per parameter}### for denominator per timepoint: all records from adeg on this timepoint: ignoring anl01fl/anl02fl/paramfiltered_adeg_timepoints <- unique( adeg00 %>% filter(AVISIT %in% selvisit) %>% select(USUBJID, AVISITN, AVISIT)) %>% inner_join(adsl)params <- unique(filtered_adeg %>% select(PARAMCD, PARAM))filtered_adeg_timepoints <- filtered_adeg_timepoints %>% mutate(dummy_join = 1) %>% full_join( params %>% mutate(dummy_join = 1), relationship = "many-to-many" ) %>% select(-dummy_join)### identify subjects in filtered_advs_timepoints and not in filtered_advsextra_sub <- anti_join(filtered_adeg_timepoints, filtered_adeg)### only add these extra_sub to### this will ensure we still meet the one record per subject per timepoint### this will ensure length(x) can be used for the denominator derivation inside summarize_aval_chg_diff functionfiltered_adeg <- bind_rows(filtered_adeg, extra_sub) %>% arrange(USUBJID, PARAM, AVISITN)filtered_adeg <- filtered_adeg %>% inner_join(eg_precision, by = "PARAMCD")### important: previous actions lost the label of variables### in order to be able to use obj_label(filtered_advs$PARAM) in layout, need to redefine the labelfiltered_adeg <- var_relabel_list(filtered_adeg, var_labels(adeg00, fill = T))check1 <- filtered_adeg %>% group_by(TRT01A, PARAM, AVISIT) %>% summarize(n_rec = n(), n_sub = n_distinct(USUBJID))colspan_trt_map <- create_colspan_map( adsl, non_active_grp = ctrl_grp, non_active_grp_span_lbl = " ", active_grp_span_lbl = "Active Study Agent", colspan_var = "colspan_trt", trt_var = trtvar)# Define layout and build table:summ_vars <- list(arm = trtvar, covariates = NULL)ref_path <- c("colspan_trt", " ", trtvar, ctrl_grp)multivars <- c("AVAL", "AVAL", "CHG")extra_args_3col <- list( format_na_str = rep("NA", 3), d = "decimal", variables = summ_vars, ref_path = ref_path, ancova = ancova, comp_btw_group = comp_btw_group, multivars = multivars)lyt <- basic_table(show_colcounts = FALSE, colcount_format = "N=xx") %>% ### first columns split_cols_by( "colspan_trt", split_fun = trim_levels_to_map(map = colspan_trt_map) ) %>% split_cols_by(trtvar, show_colcounts = TRUE, colcount_format = "N=xx") %>% split_rows_by( "PARAM", label_pos = "topleft", split_label = obj_label(filtered_adeg$PARAM), section_div = " ", split_fun = drop_split_levels ) %>% ## note the child_labels = hidden for AVISIT, these labels will be taken care off by ## applying function summarize_aval_chg_diff further in the layout split_rows_by( "AVISIT", label_pos = "topleft", split_label = "Study Visit", split_fun = drop_split_levels, child_labels = "hidden" ) %>% ## set up a 3 column split split_cols_by_multivar( multivars, varlabels = c( "n/N (%)", "Mean (95% CI)", "Mean Change From Baseline (95% CI)" ) ) %>% ### restart for the rrisk_header columns - note the nested = FALSE option ### also note the child_labels = "hidden" in both PARAM and AVISIT split_cols_by("rrisk_header", nested = FALSE) %>% split_cols_by( trtvar, split_fun = remove_split_levels(ctrl_grp), labels_var = "rrisk_label", show_colcounts = TRUE, colcount_format = "N=xx" ) %>% ### difference columns : just 1 column & analysis needs to be done on change split_cols_by_multivar(multivars[3], varlabels = c(" ")) %>% ### the variable passed here in analyze is not used (STUDYID), it is a dummy var passing, ### the function summarize_aval_chg_diff grabs the required vars from cols_by_multivar calls analyze( "STUDYID", afun = a_summarize_aval_chg_diff_j, extra_args = extra_args_3col )result <- build_table(lyt, filtered_adeg, alt_counts_df = adsl)# Post-Processing:remove_col_count2 <- function(result, string = paste("vs", ctrl_grp)) { mcdf <- make_col_df(result, visible_only = FALSE) mcdfsel <- mcdf %>% filter(stringr::str_detect(toupper(label), toupper(string))) %>% pull(path) for (i in seq_along(mcdfsel)) { facet_colcount(result, mcdfsel[[i]]) <- NA } return(result)}result <- remove_col_count2(result)# Add titles and footnotes:result <- set_titles(result, titles)# Convert to tbl file and output tablett_to_tlgrtf(string_map = string_map, tt = result, file = fileid, nosplitin = list(cols = c(trtvar, "rrisk_header")), orientation = "landscape")``````{r result1, echo=FALSE, message=FALSE, warning=FALSE, test = list(result_v1 = "result")}tt_to_flextable_j(result, tblid, string_map = string_map) ```[Download RTF file](`r paste0(tolower(tblid), '.rtf')`)::::