--- title: "Standard Column Structures" date: "2026-05-04" output: rmarkdown::html_document: theme: "spacelab" highlight: "kate" toc: true toc_float: true vignette: > %\VignetteIndexEntry{Standard Column Structures} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} editor_options: markdown: wrap: 72 --- ```{r setup, include = FALSE} knitr::opts_chunk$set( echo = TRUE, collapse = TRUE, comment = "#>" ) ``` # Structures With Difference Columns Many standard tables call for a set of difference columns to be added alongside the main tabulation; this, combined with the spanning column labels favored by Johnson&Johnson shells, results in a complex final column structure. To ease the creation of these structures, particularly in the presence of virtual *combination* arms, we provide the `grouped_cols_w_diffs` function. It provides the flexibility to be used in standard template scripts, as we will see below. ## Basic Usage At it's most basic, `grouped_cols_w_diffs` takes a column-span treatment map, as created by `create_colspan_map`. This creates a column structure with spanning labels over active and non-active treatment groups, as well as difference columns comparing each active treatments individually against each non-active treatment: ```{r} library(junco) adsl <- create_colspan_var(pharmaverseadamjnj::adsl) adae <- create_colspan_var(pharmaverseadamjnj::adae) adlb <- create_colspan_var(pharmaverseadamjnj::adlb) trt_map <- create_colspan_map(adsl) print(trt_map) ``` ```{r} lyt <- basic_table() |> grouped_cols_w_diffs(trt_map) build_table(lyt, adsl) ``` ### A Note About Types Of Differences `grouped_cols_w_diffs` creates only the *column* structure; the contents of the difference columns are calculated by the analysis function used in the layout. The *structure* for the various types of difference columns (risk, mean, eair, etc) differs only in the spanning label for the difference columns. `grouped_cols_w_diffs` accepts `diffs_label` for this purpose: ```{r} lyt2 <- basic_table() |> grouped_cols_w_diffs(trt_map, diffs_label = "Mean Differences") build_table(lyt2, adsl) ``` Care should be taken to choose the correct analysis function to create the types of differences required by your shell. ### Turning Off The Difference Columns Despite the function's name, we can turn off difference columns when using `grouped_cols_w_diffs` via the `diff_cols` argument. This allows us to use the function unconditionally in our layouts while retaining the ability to turn off difference columns , e.g., in shells where they are optional. ```{r} lyt_nodiff <- basic_table() |> grouped_cols_w_diffs(trt_map, diff_cols = FALSE) build_table(lyt_nodiff, adsl) ``` ## Advanced Usage Beyond the basic usage, `grouped_cols_w_diffs` offers fine-grained control over both the arms (notably the addition of combination arms) and comparisons between arms displayed in your table. ### Including Virtual Combination Arms Combination levels are declared via a "combo data.frame" similar to those accepted by `rtables::add_combo_levels`. In particular, we use a data.frame with the following columns: - **`valname`** - (`character`) Name of the combination level (as it will appear in paths) - **`label`** - (`character`) Label for the combination level (as it will appear in the table) - **`levelcombo`** - (`list` of `character`s) The levels which should be combined into the combination level - **`exargs`** - (`list`) Extra arguments to be passed to a/cfuns associated with the combination level - **`is_control`** *Optional* (`logical`) When `TRUE`, the combination level will be added to the second (non-active) group of treatments. Assumed `FALSE` if not specified. - **`compare_against`** *Optional* (`list` of `character`s) The levels the combination level should be compared against (as the first/active element). Assumed to be `select_all_levels` if not specified. Thus at its most basic, we use this argument like so: ```{r} library(tibble) combodf1 <- tribble( ~valname, ~label, ~levelcombo, ~exargs, "all_active", "All Xanomeline", c("Xanomeline High Dose", "Xanomeline Low Dose"), list() ) lyt3 <- basic_table() |> grouped_cols_w_diffs(trt_map, combo_map_df = combodf1) build_table(lyt3, adsl) ``` We can instead add a second virtual control or referene group by including the `is_control` column and setting it to `TRUE` for some or all of our combination levels: ```{r} combodf2 <- tribble( ~valname, ~label, ~levelcombo, ~exargs, ~is_control, "all_active", "All Xanomeline", c("Xanomeline High Dose", "Xanomeline Low Dose"), list(), FALSE, "placebo_redux", "Double Placebo!!", c("Placebo"), list(), TRUE ) lyt4 <- basic_table() |> grouped_cols_w_diffs(trt_map, combo_map_df = combodf2) build_table(lyt4, adsl) ``` As we can see, this allows us full flexibility to include combination levels as both active and non-active virtual arms, including using them in both sides of comparisons (even simultaneously). ### Direct Control Of Comparisons Finally, we have complete control over which comparisons appear in our table via the `comp_map` argument. When non-null, the exact set of comparisons specified in `comp_map` will appear in our table. `comp_map` accepts a data.frame with the columns `active`, and `comparator`, as well optional `active_is_combo` and `comparator_is_combo` columns. The values in `active` and `comparator` should be the *names* (as opposed to labels) of the respective arms: ```{r} comp_map1 <- tribble( ~active, ~comparator, "Xanomeline High Dose", "Placebo", "Xanomeline Low Dose", "placebo_redux", "all_active", "placebo_redux" ) lyt5 <- basic_table() |> grouped_cols_w_diffs(trt_map, combo_map_df = combodf2, comp_map = comp_map1) build_table(lyt5, adsl) ``` We can also use `comp_map` to compare one active treatment to another: ```{r} comp_map2 <- tribble( ~active, ~comparator, "Xanomeline High Dose", "Xanomeline Low Dose", "Xanomeline High Dose", "Placebo", "Xanomeline Low Dose", "Placebo" ) lyt6 <- basic_table() |> grouped_cols_w_diffs(trt_map, comp_map = comp_map2) build_table(lyt6, adsl) ``` # Other Standard Column Structures ## Subgroup Column Structures The `grouped_cols_w_subgrps` function generates columns structures, with or without overarching spanning column headers, wherein treatment arms are further split by the values of a subgrouping variable. By default, a Total column is added to the left of the subgroups within each treatment, though this can be disabled. Similar to `grouped_cols_w_diffs`, we can specify a `colspan_trt_map`, which will define both the spanning labels and the treatment variable: ```{r} lyt_subgrp <- basic_table() |> grouped_cols_w_subgrps(trt_map, subgrp_var = "SEX") build_table(lyt_subgrp, adsl) ``` Similar to `grouped_cols_w_diffs`, we can get a structure without spanning labels by specifying `trtvar` instead of `colspan_trt_map`, though this does make the name of the function something of a misnomer, as the treatments are no longer grouped: ```{r} lyt_subgrp2 <- basic_table() |> grouped_cols_w_subgrps(trtvar = "TRT01A", subgrp_var = "SEX") build_table(lyt_subgrp2, adsl) ``` `grouped_cols_w_subgrps` also accepts `combo_map_df`, with usage similar to that explored for `grouped_cols_w_diffs` above. ### Quartiles As A Special Case Of Subgrouping For pre-computed quartile (or any other binning) groups, we can simply use `grouped_cols_w_subgrps` with `total_facet = FALSE`: ```{r} lyt_ageqrtls <- basic_table() |> grouped_cols_w_subgrps(colspan_trt_map = trt_map, subgrp_var = "AGEGR1", subgrp_lbl = "Age Quartiles", total_facet = FALSE) build_table(lyt_ageqrtls, adsl) ``` ## Shift Table Column Structure We can create the column structure for a standard Shift Table via the `shift_tbl_col_struct` function (assuming `BNRIND` is a classification at baseline and `ANRIND` is the same classification applied at analysis date): ```{r} param <- levels(adlb$PARAMCD)[1] lyt_shift <- basic_table() |> shift_tbl_col_struct(var = "BNRIND", span_lbl = paste("Baseline", param)) build_table(lyt_shift, adlb) ``` ## Some Vs All Column Structure The "some vs all" case, motivated by comparing counts of AEs to counts of treatment-related AEs, is similar to the subgroup column structures, with the exception that the groups being compared are not mutually exclusive. In light of this, `some_v_all_col_struct` creates two subgroup facets within each treatment group, one containing one or more levels of a specified subgrouping variable, the other being all levels of the subgrouping variable. We control this behavior by the combination of `subgrp_var`, declaring the variable, and `subgrp_lvls` defining which levels should be included in the smaller group. ```{r} lyt <- basic_table() |> some_v_all_col_struct(trt_map, subgrp_var = "AETOXGR", subgrp_lvls = c("4", "5"), all_lbl = "All Grades", subgrp_lbl = "High Toxicity") build_table(lyt, adae) ```