diff --git a/DESCRIPTION b/DESCRIPTION index 611c7918..7c2bf9df 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Imports: formatters (>= 0.5.6), ggplot2 (>= 3.4.0), gt, + gtreg, gtsummary, lubridate, magrittr, diff --git a/NAMESPACE b/NAMESPACE index b19cae96..242892b2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,6 +18,7 @@ export(make_table_09) export(make_table_09_gtsum) export(make_table_09_tplyr) export(make_table_10) +export(make_table_10_gtsum) export(make_table_11) export(make_table_12) export(make_table_13) diff --git a/R/fda-table_10.R b/R/fda-table_10.R index e7711a80..bf425d0c 100644 --- a/R/fda-table_10.R +++ b/R/fda-table_10.R @@ -4,7 +4,7 @@ #' @details #' * `adae` must contain the variables `AEBODSYS`, `AESER`, and the variables specified by #' `arm_var`, `id_var`, `saffl_var`, `fmqsc_var`, and `fmqnam_var`. -#' * If specified, `alt_counts_df` must contain `USUBJID` and the variables specified by `arm_var` and `saffl_var`. +#' * If specified, `alt_counts_df` must contain `id_var` and the variables specified by `arm_var` and `saffl_var`. #' * `fmqsc_var` must contain "BROAD" or "NARROW" values, one of which will be displayed in the table. Narrow is #' selected by default (see `fmq_scope` argument). #' * Flag variables (i.e. `XXXFL`) are expected to have two levels: `"Y"` (true) and `"N"` (false). Missing values in @@ -42,6 +42,7 @@ make_table_10 <- function(adae, id_var = "USUBJID", arm_var = "ARM", saffl_var = "SAFFL", + soc_var = "AEBODSYS", fmqsc_var = "FMQ01SC", fmqnam_var = "FMQ01NAM", fmq_scope = "NARROW", @@ -50,8 +51,8 @@ make_table_10 <- function(adae, prune_0 = TRUE, na_level = "", annotations = NULL) { - assert_subset(c( - "AEBODSYS", "AESER", arm_var, id_var, saffl_var, fmqsc_var, fmqnam_var + checkmate::assert_subset(c( + id_var, soc_var, "AESER", arm_var, saffl_var, fmqsc_var, fmqnam_var ), names(adae)) assert_flag_variables(adae, c(saffl_var, "AESER")) assert_subset(toupper(fmq_scope), c("NARROW", "BROAD")) @@ -66,7 +67,7 @@ make_table_10 <- function(adae, lyt <- basic_table_annot(show_colcounts, annotations) %>% split_cols_by_arm(arm_var, lbl_overall, risk_diff) %>% split_rows_by( - "AEBODSYS", + soc_var, label_pos = "topleft", split_label = obj_label(adae$AEBODSYS) ) %>% @@ -82,3 +83,150 @@ make_table_10 <- function(adae, tbl } + + +#' @describeIn make_table_10 Create FDA table 10 using functions from `gtsummary`. +#' +#' @inheritParams argument_convention +#' @param saffl_var (`character`)\cr safety population flag variable from `adae` to include in the table. +#' @param ser_var (`character`)\cr serious Event variable from `adae` to include in the table. +#' @param soc_var (`character`)\cr system organ class variable from `adae` to include in the table. +#' @param lbl_soc_var (`character`)\cr label corresponding to system organ class variable +#' `soc_var` to print in the table. +#' @param annotations (named `list` of `character`)\cr list of annotations to add to the table. Valid +#' annotation types are `title`, `subtitles`, and a list of characters called `footnotes`. +#' Each name-value pair should use the annotation type as name and the desired string as the value. +#' @param risk_diff (`list` of `character` vectors)\cr List of character vectors. Each vector must be +#' of length 2 and contain the name of treatment arms to calculate risk difference and its 95% CI for. Those names +#' must exist in the `arm_var` column of the dataset specified in `adae`. +#' +#' @return +#' * `make_table_10_gtsum` returns a `gt_tbl` object. +#' +#' @examples +#' tbl <- make_table_10_gtsum(adae = adae, alt_counts_df = adsl) +#' tbl +#' +#' annotations <- list( +#' title = "Table 10. Patients With Serious Adverse Events ", +#' subtitle = "by System Organ Class and FDA Medical Query (Narrow), +#' Safety Population, Pooled Analyses", +#' footnotes = list( +#' "Duration = [e.g., X week double-blind treatment period or median and a range +#' indicating pooled trial durations]." +#' ) +#' ) +#' tbl <- make_table_10_gtsum(adae, +#' alt_counts_df = adsl, +#' annotations = annotations +#' ) +#' tbl +#' +#' @export +make_table_10_gtsum <- function(adae, + alt_counts_df = NULL, + show_colcounts = TRUE, + saffl_var = "SAFFL", + ser_var = "AESER", + arm_var = "ARM", + id_var = "USUBJID", + soc_var = "AEBODSYS", + fmqsc_var = "FMQ01SC", + fmqnam_var = "FMQ01NAM", + fmq_scope = "NARROW", + lbl_soc_var = formatters::var_labels(adae, fill = TRUE)[soc_var], + lbl_overall = NULL, + annotations = NULL) { + checkmate::assert_data_frame(adae) + checkmate::assert_subset(c(saffl_var, id_var, soc_var, fmqnam_var, arm_var), names(adae)) + assert_flag_variables(adae, saffl_var) + checkmate::assert_factor(adae[[arm_var]]) + checkmate::assert_logical(show_colcounts) + if (!is.null(alt_counts_df)) { + checkmate::assert_data_frame(alt_counts_df) + checkmate::assert_subset(c(id_var, arm_var), names(alt_counts_df)) + } + + adae <- adae %>% + filter(.data[[saffl_var]] == "Y", .data[[ser_var]] == "Y", .data[[fmqsc_var]] == fmq_scope) + + gt_table <- + # build primary table + gtsummary::tbl_strata2( + data = adae, + strata = soc_var, + ~ gtreg::tbl_ae( + data = .x |> dplyr::mutate(!!soc_var := .y), + strata = arm_var, + id = id_var, + soc = soc_var, + ae = fmqnam_var, + statistic = "{n} ({p}%)", + id_df = alt_counts_df + ) %>% + { + if (!is.null(lbl_overall)) gtreg::add_overall(., across = "strata") else . + }, + .combine_with = "tbl_stack", + .combine_args = list(group_header = NULL) + ) |> # remove stats from SOC row + gtsummary::modify_table_body( + ~ .x |> + dplyr::mutate( + dplyr::across( + gtsummary::all_stat_cols(), + function(x) ifelse(.data$variable %in% "soc", "", x) + ) + ) + ) |> + gtsummary::modify_spanning_header(gtreg::all_ae_cols() ~ NA) |> + # bold SOC rows + gtsummary::modify_table_styling( + columns = "label", + rows = variable %in% "soc", + text_format = "bold" + ) + + # update column headers + if (show_colcounts) { + gt_table <- gt_table |> gtsummary::modify_header(gtreg::all_ae_cols() ~ "**{strata}** \nN = {n}") + } else { + gt_table <- gt_table |> gtsummary::modify_header(gtreg::all_ae_cols() ~ "**{strata}**") + } + + # update overall column headers + if (!is.null(lbl_overall)) { + gt_table$table_styling$header <- gt_table$table_styling$header %>% + mutate(label = ifelse(stringr::str_detect(column, "^stat_1") & + stringr::str_detect(label, "\\*\\*Overall\\*\\*"), + stringr::str_replace(label, "Overall", lbl_overall), label + )) + } + + # update column headers and annotations + gt_table <- gt_table %>% + gtsummary::as_gt() %>% + gt::cols_label(label = gt::md(paste0( + "**System Organ Class**
", + "FMQ (", tools::toTitleCase(tolower(fmq_scope)), ")" + ))) + + if (!is.null(annotations)) { + if (!is.null(annotations[["title"]])) { + gt_table <- gt_table %>% + gt::tab_header(annotations[["title"]]) + } + if (!is.null(annotations[["subtitle"]])) { + gt_table <- gt_table %>% + gt::tab_header(annotations[["title"]], subtitle = annotations[["subtitle"]]) + } + if (!is.null(annotations[["footnotes"]])) { + lapply(annotations[["footnotes"]], function(x) { + gt_table <<- gt_table %>% + gt::tab_footnote(x) + }) + } + } + + gt_table +} diff --git a/man/make_table_10.Rd b/man/make_table_10.Rd index d7cc0fa7..704d1a16 100644 --- a/man/make_table_10.Rd +++ b/man/make_table_10.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/fda-table_10.R \name{make_table_10} \alias{make_table_10} +\alias{make_table_10_gtsum} \title{FDA Table 10: Patients With Serious Adverse Events by System Organ Class and FDA Medical Query (Narrow), Safety Population, Pooled Analyses} \usage{ @@ -12,6 +13,7 @@ make_table_10( id_var = "USUBJID", arm_var = "ARM", saffl_var = "SAFFL", + soc_var = "AEBODSYS", fmqsc_var = "FMQ01SC", fmqnam_var = "FMQ01NAM", fmq_scope = "NARROW", @@ -21,6 +23,23 @@ make_table_10( na_level = "", annotations = NULL ) + +make_table_10_gtsum( + adae, + alt_counts_df = NULL, + show_colcounts = TRUE, + saffl_var = "SAFFL", + ser_var = "AESER", + arm_var = "ARM", + id_var = "USUBJID", + soc_var = "AEBODSYS", + fmqsc_var = "FMQ01SC", + fmqnam_var = "FMQ01NAM", + fmq_scope = "NARROW", + lbl_soc_var = formatters::var_labels(adae, fill = TRUE)[soc_var], + lbl_overall = NULL, + annotations = NULL +) } \arguments{ \item{adae}{(\code{data.frame})\cr dataset (typically ADAE) required to build table.} @@ -33,7 +52,9 @@ make_table_10( \item{arm_var}{(\code{character})\cr Name of the treatment arm variable used to split table into columns.} -\item{saffl_var}{(\code{character})\cr flag variable used to indicate inclusion in safety population.} +\item{saffl_var}{(\code{character})\cr safety population flag variable from \code{adae} to include in the table.} + +\item{soc_var}{(\code{character})\cr system organ class variable from \code{adae} to include in the table.} \item{fmqsc_var}{(\code{character})\cr FMQ scope variable to use in table.} @@ -44,29 +65,29 @@ make_table_10( \item{lbl_overall}{(\code{character})\cr if specified, an overall column will be added to the table with the given value as the column label.} -\item{risk_diff}{(named \code{list})\cr list of settings to apply to add one or more risk difference columns to the table. -Defaults to \code{NULL} (no risk difference column added). See \code{\link[tern:add_riskdiff]{tern::add_riskdiff()}} for more details. List should -contain the following elements: -\itemize{ -\item \code{arm_x}: (required) the name of reference arm. -\item \code{arm_y}: (required) the names of the arms to compare to the reference arm. A new column will be added for each -element of \code{arm_y}. -\item \code{col_label}: (optional) labels to use for the risk difference columns. Defaults to -\code{"Risk Difference (\%) (95\% CI)"}. For more than one risk difference column, \code{"arm x vs. arm y"} text will also -be included in the column labels by default. The length of \code{col_label} must be equal to the length of \code{arm_y}. -\item \code{pct}: (optional) whether the output should be returned as percentages. Defaults to \code{TRUE}. -}} +\item{risk_diff}{(\code{list} of \code{character} vectors)\cr List of character vectors. Each vector must be +of length 2 and contain the name of treatment arms to calculate risk difference and its 95\% CI for. Those names +must exist in the \code{arm_var} column of the dataset specified in \code{adae}.} \item{prune_0}{(\code{flag})\cr Whether all-zero rows should be removed from the table. Boolean.} \item{na_level}{(\code{character})\cr string to represent missing values.} \item{annotations}{(named \code{list} of \code{character})\cr list of annotations to add to the table. Valid -annotation types are \code{title}, \code{subtitles}, \code{main_footer}, and \code{prov_footer}. Each name-value pair should -use the annotation type as name and the desired string as value.} +annotation types are \code{title}, \code{subtitles}, and a list of characters called \code{footnotes}. +Each name-value pair should use the annotation type as name and the desired string as the value.} + +\item{ser_var}{(\code{character})\cr serious Event variable from \code{adae} to include in the table.} + +\item{lbl_soc_var}{(\code{character})\cr label corresponding to system organ class variable +\code{soc_var} to print in the table.} } \value{ An \code{rtable} object. + +\itemize{ +\item \code{make_table_10_gtsum} returns a \code{gt_tbl} object. +} } \description{ FDA Table 10: Patients With Serious Adverse Events by System Organ Class and @@ -76,7 +97,7 @@ FDA Medical Query (Narrow), Safety Population, Pooled Analyses \itemize{ \item \code{adae} must contain the variables \code{AEBODSYS}, \code{AESER}, and the variables specified by \code{arm_var}, \code{id_var}, \code{saffl_var}, \code{fmqsc_var}, and \code{fmqnam_var}. -\item If specified, \code{alt_counts_df} must contain \code{USUBJID} and the variables specified by \code{arm_var} and \code{saffl_var}. +\item If specified, \code{alt_counts_df} must contain \code{id_var} and the variables specified by \code{arm_var} and \code{saffl_var}. \item \code{fmqsc_var} must contain "BROAD" or "NARROW" values, one of which will be displayed in the table. Narrow is selected by default (see \code{fmq_scope} argument). \item Flag variables (i.e. \code{XXXFL}) are expected to have two levels: \code{"Y"} (true) and \code{"N"} (false). Missing values in @@ -86,6 +107,11 @@ flag variables are treated as \code{"N"}. \item All-zero rows are removed by default (see \code{prune_0} argument). } } +\section{Functions}{ +\itemize{ +\item \code{make_table_10_gtsum()}: Create FDA table 10 using functions from \code{gtsummary}. + +}} \examples{ library(dplyr) @@ -104,4 +130,22 @@ adae$FMQ01SC[is.na(adae$FMQ01SC)] <- "NARROW" tbl <- make_table_10(adae = adae, alt_counts_df = adsl) tbl +tbl <- make_table_10_gtsum(adae = adae, alt_counts_df = adsl) +tbl + +annotations <- list( + title = "Table 10. Patients With Serious Adverse Events ", + subtitle = "by System Organ Class and FDA Medical Query (Narrow), + Safety Population, Pooled Analyses", + footnotes = list( + "Duration = [e.g., X week double-blind treatment period or median and a range + indicating pooled trial durations]." + ) +) +tbl <- make_table_10_gtsum(adae, + alt_counts_df = adsl, + annotations = annotations +) +tbl + } diff --git a/quarto/table-templates/template-table_10.qmd b/quarto/table-templates/template-table_10.qmd index fc44859f..3d0a7cce 100644 --- a/quarto/table-templates/template-table_10.qmd +++ b/quarto/table-templates/template-table_10.qmd @@ -43,50 +43,133 @@ make_table_10(adae = adae, alt_counts_df = adsl, risk_diff = risk_diff) ### `make_table_10()` ------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Required variables: -- **`adae`**: `USUBJID`, `AEBODSYS`, `AESER`, and the variables specified by `arm_var`, `saffl_var`, `fmqsc_var`, and `fmqnam_var`. +- **`adae`**: `id_var`, `soc_var`, `AESER`, and the variables specified by `arm_var`, `saffl_var`, `fmqsc_var`, and `fmqnam_var`. - **`alt_counts_df`** (if specified): `USUBJID` and the variables specified by `arm_var` and `saffl_var`. -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| Argument | Description | Default | -+:=================+:=============================================================================================================================================================================================================================================================+:==============+ -| `adae` | (`data.frame`) Dataset (typically ADAE) required to build table. | *No default* | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `alt_counts_df` | (`character`) Alternative dataset used only to calculate column counts. | `NULL` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `show_colcounts` | (`flag`) Whether column counts should be printed. | `TRUE` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `arm_var` | (`character`) Arm variable used to split table into columns. | `"ARM"` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `saffl_var` | (`character`) Flag variable used to indicate inclusion in safety population. | `"SAFFL"` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `fmqsc_var` | (`character`) FMQ scope variable to use in table. | `"FMQ01SC"` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `fmqnam_var` | (`character`) FMQ reference name variable to use in table. | `"FMQ01NAM"` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `fmq_scope` | (`character`) FMQ scope, can be '"NARROW"' or '"BROAD"'. | `"NARROW"` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `lbl_overall` | (`character`) If specified, an overall column will be added to the table with the given value as the column label. | `NULL` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `risk_diff` | (named `list`) List of settings to apply to add a risk difference column to the table. See [tern::add_riskdiff()](https://insightsengineering.github.io/tern/main/reference/add_riskdiff.html) for more details. List should contain the following elements: | `NULL` | -| | | | -| | - `arm_x`: (required) the name of reference arm. | | -| | | | -| | - `arm_y`: (required) the name of the arm to compare to the reference arm. | | -| | | | -| | - `col_label`: (optional) label to use for the risk difference column. Defaults to `"Risk Difference (%) (95% CI)"`. | | -| | | | -| | - `pct`: (optional) whether the output should be returned as percentages. Defaults to `TRUE`. | | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `prune_0` | (`flag`) Whether all-zero rows should be removed from the table. | `FALSE` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `na_level` | (`character`) String to represent missing values. | `""` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ -| `annotations` | (named `list` of `character`) List of annotations to add to the table. Valid annotation types are `title`, `subtitles`, `main_footer`, and `prov_footer.` Each name-value pair should use the annotation type as name and the desired string as value. | `NULL` | -+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+ ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| Argument | Description | Default | ++:=================+:================================================================================================================================================================================================+:====================================================+ +| `adae` | (`data.frame`) Dataset (typically ADAE) required to build table. | *No default* | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `alt_counts_df` | (`character`) Alternative dataset used only to calculate column counts. | `NULL` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `show_colcounts` | (`flag`) Whether column counts should be printed. | `TRUE` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `arm_var` | (`character`) Arm variable used to split table into columns. | `"ARM"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `id_var` | (`character`) Variable used as unique subject identifier. | `"USUBJID"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `saffl_var` | (`character`) Flag variable used to indicate inclusion in safety population. | `"SAFFL"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `ser_var` | (`character`) Serious Event variable from `adae` to include in the table. | `"AESER"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `soc_var` | (`character`) System organ class variable to use in table. | `"AEBODSYS"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmqsc_var` | (`character`) FMQ scope variable to use in table. | `"FMQ01SC"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmqnam_var` | (`character`) FMQ reference name variable to use in table. | `"FMQ01NAM"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmq_scope` | (`character`) FMQ scope, can be '"NARROW"' or '"BROAD"'. | `"NARROW"` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `lbl_overall` | (`character`) If specified, an overall column will be added to the table with the given value as the column label. | `NULL` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `lbl_soc_var` | (`character`) If specified, label corresponding to system organ class variable `soc_var` to print in the table. | `formatters::var_labels(adae, fill = TRUE)[soc_var]`| ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `risk_diff` | (named `list`) List of settings to apply to add a risk difference column to the table. See [tern::add_riskdiff()](https://insightsengineering.github.io/tern/main/reference/add_riskdiff.html) | `NULL` | +| | for more details. List should contain the following elements: | | +| | | | +| | - `arm_x`: (required) the name of reference arm. | | +| | | | +| | - `arm_y`: (required) the name of the arm to compare to the reference arm. | | +| | | | +| | - `col_label`: (optional) label to use for the risk difference column. Defaults to `"Risk Difference (%) (95% CI)"`. | | +| | | | +| | - `pct`: (optional) whether the output should be returned as percentages. Defaults to `TRUE`. | | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `prune_0` | (`flag`) Whether all-zero rows should be removed from the table. | `FALSE` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `na_level` | (`character`) String to represent missing values. | `""` | ++------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `annotations` | (named `list` of `character`) List of annotations to add to the table. Valid annotation types are `title`, `subtitles`, `main_footer`, and `prov_footer.` Each name-value pair should use the | `NULL` | +| | annotation type as name and the desired string as value. | | ++------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---+-----------------------------------------------------+ + +Source code for this function is available [here](https://github.com/pharmaverse/falcon/blob/main/R/fda-table_10.R). + +## gt Table + +```{r tbl_gt, message=FALSE, warning=FALSE} +# Load Libraries & Data +library(falcon) + +adsl <- random.cdisc.data::cadsl +adae <- random.cdisc.data::cadae + +# Pre-Processing - Ensure required variables fmqsc_var and fmqnam_var exist in adae +set.seed(1) +adae <- adae %>% + dplyr::rename(FMQ01SC = SMQ01SC) %>% + dplyr::mutate( + AESER = sample(c("Y", "N"), size = nrow(adae), replace = TRUE), + FMQ01NAM = sample(c("FMQ1", "FMQ2", "FMQ3"), size = nrow(adae), replace = TRUE) + ) +adae$FMQ01SC[is.na(adae$FMQ01SC)] <- "NARROW" + +# Output Table +make_table_10_gtsum(adae = adae, alt_counts_df = adsl) +``` +## gt Table Setup + +```{r tbl_gt, eval=FALSE, echo=TRUE} +``` + +## Function Details: `make_table_10_gtsum` + +### `make_table_10_gtsum()` + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +Required variables: + +- **`adae`**: `id_var`, `soc_var`, `AESER`, and the variables specified by `arm_var`, `saffl_var`, `fmqsc_var`, and `fmqnam_var`. +- **`alt_counts_df`** (if specified): `id_var` and the variables specified by `arm_var` and `saffl_var`. + ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| Argument | Description | Default | ++:=================+:=========================================================================================================================================================+:====================================================+ +| `adae` | (`data.frame`) Dataset (typically ADAE) required to build table. | *No default* | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `alt_counts_df` | (`character`) Alternative dataset used only to calculate column counts. | `NULL` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `show_colcounts` | (`flag`) Whether column counts should be printed. | `TRUE` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `arm_var` | (`character`) Arm variable used to split table into columns. | `"ARM"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `id_var` | (`character`) Variable used as unique subject identifier.. | `"USUBJID"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `saffl_var` | (`character`) Flag variable used to indicate inclusion in safety population. | `"SAFFL"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `soc_var` | (`character`) System organ class variable to use in table. | `"AEBODSYS"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmqsc_var` | (`character`) FMQ scope variable to use in table. | `"FMQ01SC"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmqnam_var` | (`character`) FMQ reference name variable to use in table. | `"FMQ01NAM"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `fmq_scope` | (`character`) FMQ scope, can be '"NARROW"' or '"BROAD"'. | `"NARROW"` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `lbl_overall` | (`character`) If specified, an overall column will be added to the table with the given value as the column label. | `NULL` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `lbl_soc_var` | (`character`) If specified, label corresponding to system organ class variable `soc_var` to print in the table. | `formatters::var_labels(adae, fill = TRUE)[soc_var]`| ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `na_level` | (`character`) String to represent missing values. | `""` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ +| `annotations` | (named `list` of `character`) List of annotations to add to the table. Valid annotation types are `title`, `subtitles`, |`NULL` | +| | `main_footer`, and `prov_footer.` Each name-value pair should use the annotation type as name and the desired string as value. | | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------+ Source code for this function is available [here](https://github.com/pharmaverse/falcon/blob/main/R/fda-table_10.R). ::: diff --git a/tests/testthat/_snaps/fda-table_10.md b/tests/testthat/_snaps/fda-table_10.md index 60c0c4ea..61540cce 100644 --- a/tests/testthat/_snaps/fda-table_10.md +++ b/tests/testthat/_snaps/fda-table_10.md @@ -99,3 +99,251 @@ FMQ2 14 (10.4%) 15 (11.2%) 14 (10.6%) 0.7 (-6.7 - 8.2) FMQ3 11 (8.2%) 9 (6.7%) 11 (8.3%) -1.5 (-7.8 - 4.8) +# Table 10 (gt) generation works + + Code + res + Output + $data + # A tibble: 24 x 14 + tbl_id1 variable var_label row_type label var_type_1 stat_1_1 stat_2_1 + + 1 1 soc NA label cl A.1 categorical "" "" + 2 1 ae NA level FMQ1 categorical "17 (13%)" "117 (87%~ + 3 1 ae NA level FMQ2 categorical "23 (17%)" "111 (83%~ + 4 1 ae NA level FMQ3 categorical "20 (15%)" "114 (85%~ + 5 2 soc NA label cl B.1 categorical "" "" + 6 2 ae NA level FMQ1 categorical "8 (6.0%)" "126 (94%~ + 7 2 ae NA level FMQ2 categorical "5 (3.7%)" "129 (96%~ + 8 2 ae NA level FMQ3 categorical "10 (7.5%)" "124 (93%~ + 9 3 soc NA label cl B.2 categorical "" "" + 10 3 ae NA level FMQ1 categorical "13 (9.7%)" "121 (90%~ + 11 3 ae NA level FMQ2 categorical "12 (9.0%)" "122 (91%~ + 12 3 ae NA level FMQ3 categorical "6 (4.5%)" "128 (96%~ + 13 4 soc NA label cl C.2 categorical "" "" + 14 4 ae NA level FMQ1 categorical "9 (6.7%)" "125 (93%~ + 15 4 ae NA level FMQ2 categorical "6 (4.5%)" "128 (96%~ + 16 4 ae NA level FMQ3 categorical "6 (4.5%)" "128 (96%~ + 17 5 soc NA label cl D.1 categorical "" "" + 18 5 ae NA level FMQ1 categorical "23 (17%)" "111 (83%~ + 19 5 ae NA level FMQ2 categorical "22 (16%)" "112 (84%~ + 20 5 ae NA level FMQ3 categorical "15 (11%)" "119 (89%~ + 21 6 soc NA label cl D.2 categorical "" "" + 22 6 ae NA level FMQ1 categorical "8 (6.0%)" "126 (94%~ + 23 6 ae NA level FMQ2 categorical "14 (10%)" "120 (90%~ + 24 6 ae NA level FMQ3 categorical "11 (8.2%)" "123 (92%~ + # i 6 more variables: var_type_2 , stat_1_2 , stat_2_2 , + # var_type_3 , stat_1_3 , stat_2_3 + + $column_label + $column_label[[1]] + [1] "tbl_id1" + attr(,"class") + [1] "from_markdown" + + $column_label[[2]] + [1] "variable" + attr(,"class") + [1] "from_markdown" + + $column_label[[3]] + [1] "var_label" + attr(,"class") + [1] "from_markdown" + + $column_label[[4]] + [1] "row_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[5]] + [1] "**System Organ Class**
FMQ (Narrow)" + attr(,"class") + [1] "from_markdown" + + $column_label[[6]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[7]] + [1] "**A: Drug X** \nN = 134" + attr(,"class") + [1] "from_markdown" + + $column_label[[8]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + $column_label[[9]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[10]] + [1] "**B: Placebo** \nN = 134" + attr(,"class") + [1] "from_markdown" + + $column_label[[11]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + $column_label[[12]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[13]] + [1] "**C: Combination** \nN = 132" + attr(,"class") + [1] "from_markdown" + + $column_label[[14]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + + $header + $header$title + NULL + + $header$subtitle + NULL + + $header$preheader + NULL + + + $footnotes + list() + + +# Table 10 (gt) generation works with custom values + + Code + res + Output + $data + # A tibble: 8 x 17 + tbl_id1 variable row_type label var_label var_type_1_1 stat_1_1_1 stat_2_1_1 + + 1 1 soc label cl B.2 cl B.2 categorical "" "" + 2 1 ae level FMQ1 categorical "8 (6.0%)" "126 (94%~ + 3 1 ae level FMQ2 categorical "12 (9.0%)" "122 (91%~ + 4 1 ae level FMQ3 categorical "8 (6.0%)" "126 (94%~ + 5 2 soc label cl C.1 cl C.1 categorical "" "" + 6 2 ae level FMQ1 categorical "7 (5.2%)" "127 (95%~ + 7 2 ae level FMQ2 categorical "10 (7.5%)" "124 (93%~ + 8 2 ae level FMQ3 categorical "8 (6.0%)" "126 (94%~ + # i 9 more variables: var_type_2_1 , stat_1_2_1 , stat_2_2_1 , + # var_type_3_1 , stat_1_3_1 , stat_2_3_1 , var_type_1_2 , + # stat_1_1_2 , stat_2_1_2 + + $column_label + $column_label[[1]] + [1] "tbl_id1" + attr(,"class") + [1] "from_markdown" + + $column_label[[2]] + [1] "variable" + attr(,"class") + [1] "from_markdown" + + $column_label[[3]] + [1] "row_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[4]] + [1] "**System Organ Class**
FMQ (Broad)" + attr(,"class") + [1] "from_markdown" + + $column_label[[5]] + [1] "var_label" + attr(,"class") + [1] "from_markdown" + + $column_label[[6]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[7]] + [1] "**A: Drug X** \nN = 134" + attr(,"class") + [1] "from_markdown" + + $column_label[[8]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + $column_label[[9]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[10]] + [1] "**B: Placebo** \nN = 134" + attr(,"class") + [1] "from_markdown" + + $column_label[[11]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + $column_label[[12]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[13]] + [1] "**C: Combination** \nN = 132" + attr(,"class") + [1] "from_markdown" + + $column_label[[14]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + $column_label[[15]] + [1] "var_type" + attr(,"class") + [1] "from_markdown" + + $column_label[[16]] + [1] "**Overall** \nN = 400" + attr(,"class") + [1] "from_markdown" + + $column_label[[17]] + [1] "**NOT OBSERVED**" + attr(,"class") + [1] "from_markdown" + + + $header + $header$title + [1] "Table 10. Patients With Serious Adverse Events " + + $header$subtitle + [1] "by System Organ Class and FDA Medical Query (Narrow),\n Safety Population, Pooled Analyses" + + $header$preheader + NULL + + + $footnotes + $footnotes[[1]] + [1] "Duration = [e.g., X week double-blind treatment period or median and a range\n indicating pooled trial durations]." + + + diff --git a/tests/testthat/test-fda-table_10.R b/tests/testthat/test-fda-table_10.R index 7df5fc66..36cb3041 100644 --- a/tests/testthat/test-fda-table_10.R +++ b/tests/testthat/test-fda-table_10.R @@ -59,3 +59,42 @@ test_that("Table 10 generation works with risk difference column", { res <- expect_silent(result) expect_snapshot(res) }) + +test_that("Table 10 (gt) generation works", { + result <- make_table_10_gtsum(adae = adae, alt_counts_df = adsl) + res <- list( + "data" = result[["_data"]], + "column_label" = result[["_boxhead"]][["column_label"]], + "header" = result[["_heading"]], + "footnotes" = result[["_footnotes"]][["footnotes"]] + ) + expect_snapshot(res) +}) + +test_that("Table 10 (gt) generation works with custom values", { + adae <- formatters::var_relabel(adae, AEBODSYS = "Body System or Organ Class(3)") + annotations <- list( + title = "Table 10. Patients With Serious Adverse Events ", + subtitle = "by System Organ Class and FDA Medical Query (Narrow), + Safety Population, Pooled Analyses", + footnotes = list( + "Duration = [e.g., X week double-blind treatment period or median and a range + indicating pooled trial durations]." + ) + ) + result <- make_table_10_gtsum( + adae = adae, + alt_counts_df = adsl, + lbl_overall = "Overall", + fmq_scope = "BROAD", + annotations = annotations, + ) + + res <- list( + "data" = result[["_data"]], + "column_label" = result[["_boxhead"]][["column_label"]], + "header" = result[["_heading"]], + "footnotes" = result[["_footnotes"]][["footnotes"]] + ) + expect_snapshot(res) +})