--- title: "Reference Group Handling with get_ref_info" date: "`r Sys.Date()`" output: rmarkdown::html_document: theme: "spacelab" highlight: "kate" toc: true toc_float: true vignette: > %\VignetteIndexEntry{Reference Group Handling with get_ref_info} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} editor_options: markdown: wrap: 72 --- ```{r setup, include = FALSE} knitr::opts_chunk$set( echo = TRUE, collapse = TRUE, comment = "#>" ) ``` ## Overview Many clinical tables require statistics computed relative to a reference (control) group — for example, a risk difference versus placebo. In `rtables`, the reference group can be injected automatically via `.ref_group` and `.in_ref_col`, but this breaks down when: - the reference column is nested under a spanning header, - a `split_cols_by_multivar` is present, or - an `add_overall_col` is used alongside treatment splits. `get_ref_info()` solves this by letting the analysis function look up the reference group itself, using an explicit column path (`ref_path`). ```{r load_packages, message=FALSE} library(rtables) library(junco) library(dplyr) ``` ## The `ref_path` Convention `ref_path` is a character vector of alternating split-variable names and their values, following the same convention as `rtables::col_paths()`: ``` c("var1", "level1", "var2", "level2", ...) ``` For a simple layout with `split_cols_by("ARM")`, the placebo reference path is: ```r ref_path <- c("ARM", "B: Placebo") ``` For a layout with a spanning header variable `colspan_trt` above `ARM`, the path must include both levels: ```r ref_path <- c("colspan_trt", " ", "ARM", "B: Placebo") ``` The path must exactly match the column-split hierarchy in the layout. Use `col_paths(build_table(lyt, df))` to inspect the available paths. ## Basic Usage `get_ref_info()` returns a list with two elements: - `in_ref_col`: `TRUE` if the current column matches `ref_path`, `FALSE` otherwise, `NULL` if the reference split variable is not present in the current column hierarchy. - `ref_group`: the subset of the row data belonging to the reference group (a data frame, or a vector if `.var` is supplied). ## Working Example: AE Table with Risk Difference Columns This example demonstrates the standard pattern for a table with spanning headers and risk difference columns — the primary use case for `get_ref_info()`. ```{r data_prep} trtvar <- "TRT01A" ctrl_grp <- "Placebo" adsl <- pharmaverseadamjnj::adsl |> filter(SAFFL == "Y") |> select(STUDYID, USUBJID, all_of(trtvar), SAFFL) |> mutate(!!trtvar := factor( .data[[trtvar]], levels = c("Xanomeline Low Dose", "Xanomeline High Dose", "Placebo") )) adae <- pharmaverseadamjnj::adae |> filter(TRTEMFL == "Y") |> select(USUBJID, TRTEMFL, AEBODSYS, AEDECOD) # Add spanning header and risk difference variables adsl$colspan_trt <- factor( ifelse(adsl[[trtvar]] == ctrl_grp, " ", "Active Study Agent"), levels = c("Active Study Agent", " ") ) adsl$rrisk_header <- "Risk Difference (%) (95% CI)" adsl$rrisk_label <- paste(adsl[[trtvar]], "vs", ctrl_grp) ae <- adae |> right_join(adsl, by = "USUBJID") ``` The `ref_path` must trace the full column-split hierarchy down to the reference group: ```{r ref_path} 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 ) ref_path <- c("colspan_trt", " ", trtvar, ctrl_grp) ``` Now build the layout. The key point: `a_freq_j` uses `get_ref_info()` internally to identify the reference group and compute risk differences in the dedicated difference columns: ```{r layout_and_table} extra_args <- list( denom = "n_altdf", riskdiff = TRUE, ref_path = ref_path, method = "wald", .stats = "count_unique_fraction", .formats = c(rr_ci_3d = jjcsformat_xx("xx.x (xx.x, xx.x)")) ) lyt <- basic_table(show_colcounts = TRUE, colcount_format = "N=xx") |> split_cols_by("colspan_trt", split_fun = trim_levels_to_map(map = colspan_trt_map) ) |> split_cols_by(trtvar) |> split_cols_by("rrisk_header", nested = FALSE) |> split_cols_by(trtvar, labels_var = "rrisk_label", split_fun = remove_split_levels(ctrl_grp) ) |> analyze("TRTEMFL", afun = a_freq_j, extra_args = append(extra_args, list(val = "Y", label = "Subjects with >=1 AE")) ) |> split_rows_by("AEBODSYS", split_label = "System Organ Class", split_fun = trim_levels_in_group("AEDECOD"), label_pos = "topleft", section_div = " ", nested = FALSE ) |> summarize_row_groups("AEBODSYS", cfun = a_freq_j, extra_args = extra_args) |> analyze("AEDECOD", afun = a_freq_j, extra_args = extra_args) |> append_topleft(" Preferred Term, n (%)") result <- build_table(lyt, ae, alt_counts_df = adsl) head(result, 10) ``` ## How `get_ref_info` Works Internally When `a_freq_j` (or any analysis function) calls `get_ref_info(ref_path, .spl_context)`: 1. It checks whether the split variables in `ref_path` are present in the current column hierarchy (using `in_column()` with wildcards). 2. If present, it subsets the row data to the reference group using the pre-computed column facet indices stored in `.spl_context`. 3. It returns `in_ref_col = TRUE/FALSE` indicating whether the current column IS the reference column (so the function can skip computing a difference against itself). When the reference split is absent from the current column hierarchy (e.g. in an `add_overall_col("Total")` column), both `in_ref_col` and `ref_group` are `NULL`. ## Wildcard Matching with `in_column()` `in_column()` — the underlying helper used by `get_ref_info()` — supports `"*"` as a wildcard for any single split variable name or value: ```{r wildcard} # Simulate a split context for ARM == "B: Placebo" under colspan_trt == " " spl_ctx <- data.frame( cur_col_split = I(list(c("colspan_trt", "ARM"))), cur_col_split_val = I(list(c(" ", "B: Placebo"))) ) # Exact match in_column(c("colspan_trt", " ", "ARM", "B: Placebo"), spl_ctx) # Wildcard on spanning header — still TRUE in_column(c("*", "*", "ARM", "B: Placebo"), spl_ctx) # Different ARM level — FALSE in_column(c("*", "*", "ARM", "A: Drug X"), spl_ctx) ``` ## Relationship to `h_get_cur_trt_grp` `get_ref_info()` and `h_get_cur_trt_grp()` serve different purposes: | | `get_ref_info()` | `h_get_cur_trt_grp()` | |---|---|---| | **Returns** | reference-group data + `in_ref_col` flag | current treatment group value (single string) | | **Used for** | obtaining `.ref_group` / `.in_ref_col` for statistics | identifying which treatment group the current column belongs to | | **Typical callers** | `a_freq_j`, `a_eair_j`, `s_ancova_j` | `a_summarize_aval_chg_diff_j`, `a_freq_j`, `a_freq_resp_var_j` | In layouts that combine `split_cols_by_multivar` with difference columns, `h_get_cur_trt_grp()` resolves the current treatment group from the interleaved column path, and `get_ref_info()` obtains the reference-group data. ## Inspecting Column Paths When constructing `ref_path`, inspect the column paths of a built table: ```{r col_paths} col_paths(result) ``` Each path corresponds to a leaf column. The `ref_path` should match one of these paths exactly (or a prefix thereof when the reference column has further nested splits below it).