diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 8aa3f577..8436c233 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -68,7 +68,7 @@ jobs: - name: Upload Docs ⬆️ if: github.event_name != 'push' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: site path: ./_site diff --git a/NAMESPACE b/NAMESPACE index dfff5c37..0b7a8897 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -42,6 +42,8 @@ export(make_table_33) export(make_table_33_gtsum) export(make_table_34) export(make_table_35) +export(make_table_35_gtsummary) +export(make_table_35_rtables) export(make_table_36) export(make_table_38) export(split_cols_by_arm) diff --git a/R/fda-table_35.R b/R/fda-table_35.R index 2da4201b..defe0161 100644 --- a/R/fda-table_35.R +++ b/R/fda-table_35.R @@ -2,58 +2,205 @@ #' Safety Population, Pooled Analysis (or Trial X) #' #' @details -#' * `adae` must contain the variables specified by +#' * `df` must contain the variables specified by #' `arm_var`, `id_var`, `soc_var` and `saffl_var`. -#' * If specified, `alt_counts_df` must contain the variables specified by `arm_var`, `id_var`, and `saffl_var`. -#' * Columns are split by arm. Overall population column is excluded by default (see `lbl_overall` argument). -#' * All-zero rows are removed by default (see `prune_0` argument). +#' * `return_ard` set to `TRUE` (default) or `FALSE`; whether the intermediate ARD object should be returned. #' #' @inheritParams argument_convention +#' @param soc_var (`character`)\cr Name of the variable that contains the SOC to describe. +#' +#' @return A `gtsummary` table and, if `return_ard = TRUE`, an ARD. +#' If `return_ard = TRUE`, they will be returned as a list with named elements `table` and `ard`. +#' +#' @seealso [`tbl_make_table_35`] #' #' @examples #' adsl <- random.cdisc.data::cadsl #' adae <- random.cdisc.data::cadae #' -#' tbl <- make_table_35(adae = adae, alt_counts_df = adsl) +#' tbl <- make_table_35(df = adae, denominator = adsl) #' tbl #' #' @export -make_table_35 <- function(adae, - alt_counts_df = NULL, - show_colcounts = TRUE, +make_table_35 <- function(df, + denominator = NULL, + return_ard = TRUE, id_var = "USUBJID", arm_var = "ARM", saffl_var = "SAFFL", - soc_var = "AESOC", - lbl_soc_var = formatters::var_labels(adae, fill = TRUE)[soc_var], + soc_var = "AEBODSYS", lbl_overall = NULL, - risk_diff = NULL, - prune_0 = FALSE, - annotations = NULL) { - assert_subset(c(soc_var, arm_var, id_var, saffl_var), names(adae)) - assert_flag_variables(adae, saffl_var) + na_level = "") { + tbl <- make_table_35_gtsummary( + df = df, + denominator = denominator, + id_var = id_var, + arm_var = arm_var, + saffl_var = saffl_var, + soc_var = soc_var, + lbl_overall = lbl_overall, + na_level = na_level + ) + + if (return_ard) { + ard <- gather_ard(tbl) + return(list(table = tbl, ard = ard)) + } else { + return(tbl) # nocov + } +} + +#' Pre-Process Data for Table 35 Creation +#' +#' @keywords internal +preproc_df_table_35 <- function(df, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + na_level = "") { + assert_subset(c(soc_var, arm_var, id_var, saffl_var), names(df)) + assert_flag_variables(df, saffl_var) + + df <- df |> + filter(.data[[saffl_var]] == "Y") |> + arrange(soc_var) |> + df_explicit_na(na_level = na_level) - adae <- adae %>% - filter(.data[[saffl_var]] == "Y") %>% - arrange(soc_var) %>% - df_explicit_na() + df +} + +#' Engine-Specific Functions: Table 35 +#' +#' The table engine used by each engine-specific function is identified by its suffix. +#' +#' @inheritParams argument_convention +#' +#' @details +#' * `df` must contain the variables the variables specified by +#' `arm_var`, `id_var`, `saffl_var`, and `soc_var`. +#' * If specified, `denominator` (or `alt_counts_df`) must contain `USUBJID` and the variables specified by `arm_var` +#' and `saffl_var`. +#' * Flag variables (i.e. `XXXFL`) are expected to have two levels: `"Y"` (true) and `"N"` (false). Missing values in +#' flag variables are treated as `"N"`. +#' * Numbers in table represent the absolute numbers of patients and fraction of `N`. +#' * All-zero rows are removed by default by `make_table_35_rtables()` (see `prune_0` argument). +#' +#' @return +#' * `make_table_35_gtsummary()` returns a `gtsummary` object. +#' * `make_table_35_rtables()` returns an `rtable` object. +#' +#' @seealso [make_table_35()] +#' +#' @examples +#' +#' adsl <- random.cdisc.data::cadsl +#' adae <- random.cdisc.data::cadae +#' +#' # gtsummary table -------------- +#' tbl_gtsummary <- make_table_35_gtsummary(df = adae, denominator = adsl) +#' tbl_gtsummary +#' +#' # rtables table ---------------- +#' tbl_rtables <- make_table_35_rtables(df = adae, alt_counts_df = adsl) +#' tbl_rtables +#' +#' @export +#' @name tbl_make_table_35 +make_table_35_gtsummary <- function(df, + denominator = NULL, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + lbl_overall = NULL, + na_level = "") { + df <- preproc_df_table_35(df, id_var, arm_var, saffl_var, soc_var, na_level) + + if (is.null(denominator)) { + denominator <- df # nocov + } else { + denominator <- alt_counts_df_preproc( + denominator, + id_var, + arm_var, + saffl_var + ) + } + + tbl_gts <- tbl_hierarchical( + data = df, + variables = soc_var, + by = arm_var, + denominator = denominator, + id = id_var + ) |> + modify_header(label ~ paste0("**System Organ Class**")) |> + modify_header(all_stat_cols() ~ "**{level}** \nN = {n}") |> + modify_column_alignment(columns = all_stat_cols(), align = "right") + + if (!is.null(lbl_overall)) { + tbl_gts_ovrl <- tbl_hierarchical( + data = df, + variables = soc_var, + denominator = denominator, + id = id_var + ) |> + modify_header(label ~ paste0("**System Organ Class**")) |> + modify_header( + all_stat_cols() ~ paste0("**", lbl_overall, "** \nN = {n}") + ) |> + modify_column_alignment(columns = all_stat_cols(), align = "right") + + tbl_merged <- tbl_merge(list(tbl_gts, tbl_gts_ovrl), tab_spanner = FALSE) + + tbl <- gtsummary::with_gtsummary_theme( + x = gtsummary::theme_gtsummary_compact(), + expr = tbl_merged + ) + } else { + tbl <- with_gtsummary_theme( + x = theme_gtsummary_compact(), + expr = tbl_gts + ) + } + + return(tbl) +} + +#' @export +#' @rdname tbl_make_table_35 +make_table_35_rtables <- function(df, + alt_counts_df = NULL, + show_colcounts = TRUE, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + lbl_soc_var = formatters::var_labels(df, fill = TRUE)[soc_var], + lbl_overall = NULL, + risk_diff = NULL, + prune_0 = FALSE, + na_level = "", + annotations = NULL) { + df <- preproc_df_table_35(df, id_var, arm_var, saffl_var, soc_var, na_level) alt_counts_df <- alt_counts_df_preproc(alt_counts_df, id_var, arm_var, saffl_var) - lyt <- basic_table_annot(show_colcounts, annotations) %>% - split_cols_by_arm(arm_var, lbl_overall, risk_diff) %>% + lyt <- basic_table_annot(show_colcounts, annotations) |> + split_cols_by_arm(arm_var, lbl_overall, risk_diff) |> count_occurrences( vars = soc_var, drop = FALSE, riskdiff = !is.null(risk_diff) - ) %>% + ) |> append_topleft(c("", lbl_soc_var)) - tbl <- build_table(lyt, df = adae, alt_counts_df = alt_counts_df) %>% + tbl <- build_table(lyt, df = df, alt_counts_df = alt_counts_df) |> sort_at_path( path = c(soc_var), - scorefun = score_occurrences_cols(col_names = levels(adae[[arm_var]])) + scorefun = score_occurrences_cols(col_names = levels(df[[arm_var]])) ) if (prune_0) tbl <- prune_table(tbl) diff --git a/R/fda-table_36.R b/R/fda-table_36.R index 3d1a4da0..44e08bce 100644 --- a/R/fda-table_36.R +++ b/R/fda-table_36.R @@ -18,7 +18,7 @@ #' adsl <- random.cdisc.data::cadsl #' adae <- random.cdisc.data::cadae #' -#' tbl <- make_table_35(adae = adae, alt_counts_df = adsl) +#' tbl <- make_table_36(adae = adae, alt_counts_df = adsl) #' tbl #' #' @export diff --git a/man/make_table_35.Rd b/man/make_table_35.Rd index 6556f6d6..c2902461 100644 --- a/man/make_table_35.Rd +++ b/man/make_table_35.Rd @@ -6,26 +6,23 @@ Safety Population, Pooled Analysis (or Trial X)} \usage{ make_table_35( - adae, - alt_counts_df = NULL, - show_colcounts = TRUE, + df, + denominator = NULL, + return_ard = TRUE, id_var = "USUBJID", arm_var = "ARM", saffl_var = "SAFFL", - soc_var = "AESOC", - lbl_soc_var = formatters::var_labels(adae, fill = TRUE)[soc_var], + soc_var = "AEBODSYS", lbl_overall = NULL, - risk_diff = NULL, - prune_0 = FALSE, - annotations = NULL + na_level = "" ) } \arguments{ -\item{adae}{(\code{data.frame})\cr dataset (typically ADAE) required to build table.} +\item{df}{(\code{data.frame})\cr dataset required to build table.} -\item{alt_counts_df}{(\code{character})\cr alternative dataset (typically ADSL) used only to calculate column counts.} +\item{denominator}{(\code{data.frame})\cr alternative dataset (typically ADSL) used only to calculate denominator counts.} -\item{show_colcounts}{(\code{flag})\cr Whether column counts should be printed. Boolean.} +\item{return_ard}{(\code{flag})\cr whether an ARD should be returned. Defaults to \code{TRUE}.} \item{id_var}{(\code{character})\cr variable used as unique subject identifier.} @@ -33,32 +30,16 @@ make_table_35( \item{saffl_var}{(\code{character})\cr flag variable used to indicate inclusion in safety population.} -\item{soc_var}{(\code{character})\cr Name of the system organ class 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.} +\item{soc_var}{(\code{character})\cr Name of the variable that contains the SOC to describe.} \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{prune_0}{(\code{flag})\cr Whether all-zero rows should be removed from the table. Boolean.} - -\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.} +\item{na_level}{(\code{character})\cr string to represent missing values.} +} +\value{ +A \code{gtsummary} table and, if \code{return_ard = TRUE}, an ARD. +If \code{return_ard = TRUE}, they will be returned as a list with named elements \code{table} and \code{ard}. } \description{ Table 35. Patients With Adverse Events by System Organ Class, @@ -66,18 +47,19 @@ Safety Population, Pooled Analysis (or Trial X) } \details{ \itemize{ -\item \code{adae} must contain the variables specified by +\item \code{df} must contain the variables specified by \code{arm_var}, \code{id_var}, \code{soc_var} and \code{saffl_var}. -\item If specified, \code{alt_counts_df} must contain the variables specified by \code{arm_var}, \code{id_var}, and \code{saffl_var}. -\item Columns are split by arm. Overall population column is excluded by default (see \code{lbl_overall} argument). -\item All-zero rows are removed by default (see \code{prune_0} argument). +\item \code{return_ard} set to \code{TRUE} (default) or \code{FALSE}; whether the intermediate ARD object should be returned. } } \examples{ adsl <- random.cdisc.data::cadsl adae <- random.cdisc.data::cadae -tbl <- make_table_35(adae = adae, alt_counts_df = adsl) +tbl <- make_table_35(df = adae, denominator = adsl) tbl } +\seealso{ +\code{\link{tbl_make_table_35}} +} diff --git a/man/make_table_36.Rd b/man/make_table_36.Rd index dd74cfa9..e8cb5ce6 100644 --- a/man/make_table_36.Rd +++ b/man/make_table_36.Rd @@ -86,7 +86,7 @@ and \code{pref_var}. adsl <- random.cdisc.data::cadsl adae <- random.cdisc.data::cadae -tbl <- make_table_35(adae = adae, alt_counts_df = adsl) +tbl <- make_table_36(adae = adae, alt_counts_df = adsl) tbl } diff --git a/man/preproc_df_table_35.Rd b/man/preproc_df_table_35.Rd new file mode 100644 index 00000000..f5d3fa51 --- /dev/null +++ b/man/preproc_df_table_35.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fda-table_35.R +\name{preproc_df_table_35} +\alias{preproc_df_table_35} +\title{Pre-Process Data for Table 35 Creation} +\usage{ +preproc_df_table_35( + df, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + na_level = "" +) +} +\description{ +Pre-Process Data for Table 35 Creation +} +\keyword{internal} diff --git a/man/tbl_make_table_35.Rd b/man/tbl_make_table_35.Rd new file mode 100644 index 00000000..68494945 --- /dev/null +++ b/man/tbl_make_table_35.Rd @@ -0,0 +1,117 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fda-table_35.R +\name{tbl_make_table_35} +\alias{tbl_make_table_35} +\alias{make_table_35_gtsummary} +\alias{make_table_35_rtables} +\title{Engine-Specific Functions: Table 35} +\usage{ +make_table_35_gtsummary( + df, + denominator = NULL, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + lbl_overall = NULL, + na_level = "" +) + +make_table_35_rtables( + df, + alt_counts_df = NULL, + show_colcounts = TRUE, + id_var = "USUBJID", + arm_var = "ARM", + saffl_var = "SAFFL", + soc_var = "AEBODSYS", + lbl_soc_var = formatters::var_labels(df, fill = TRUE)[soc_var], + lbl_overall = NULL, + risk_diff = NULL, + prune_0 = FALSE, + na_level = "", + annotations = NULL +) +} +\arguments{ +\item{df}{(\code{data.frame})\cr dataset required to build table.} + +\item{denominator}{(\code{data.frame})\cr alternative dataset (typically ADSL) used only to calculate denominator counts.} + +\item{id_var}{(\code{character})\cr variable used as unique subject identifier.} + +\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{soc_var}{(\code{character})\cr Name of the system organ class variable from \code{adae} to include in the table.} + +\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{na_level}{(\code{character})\cr string to represent missing values.} + +\item{alt_counts_df}{(\code{character})\cr alternative dataset (typically ADSL) used only to calculate column counts.} + +\item{show_colcounts}{(\code{flag})\cr Whether column counts should be printed. Boolean.} + +\item{lbl_soc_var}{(\code{character})\cr label corresponding to system organ class variable \code{soc_var} to print in the +table.} + +\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{prune_0}{(\code{flag})\cr Whether all-zero rows should be removed from the table. Boolean.} + +\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.} +} +\value{ +\itemize{ +\item \code{make_table_35_gtsummary()} returns a \code{gtsummary} object. +\item \code{make_table_35_rtables()} returns an \code{rtable} object. +} +} +\description{ +The table engine used by each engine-specific function is identified by its suffix. +} +\details{ +\itemize{ +\item \code{df} must contain the variables the variables specified by +\code{arm_var}, \code{id_var}, \code{saffl_var}, and \code{soc_var}. +\item If specified, \code{denominator} (or \code{alt_counts_df}) must contain \code{USUBJID} and the variables specified by \code{arm_var} +and \code{saffl_var}. +\item Flag variables (i.e. \code{XXXFL}) are expected to have two levels: \code{"Y"} (true) and \code{"N"} (false). Missing values in +flag variables are treated as \code{"N"}. +\item Numbers in table represent the absolute numbers of patients and fraction of \code{N}. +\item All-zero rows are removed by default by \code{make_table_35_rtables()} (see \code{prune_0} argument). +} +} +\examples{ + +adsl <- random.cdisc.data::cadsl +adae <- random.cdisc.data::cadae + +# gtsummary table -------------- +tbl_gtsummary <- make_table_35_gtsummary(df = adae, denominator = adsl) +tbl_gtsummary + +# rtables table ---------------- +tbl_rtables <- make_table_35_rtables(df = adae, alt_counts_df = adsl) +tbl_rtables + +} +\seealso{ +\code{\link[=make_table_35]{make_table_35()}} +} diff --git a/quarto/table-templates/template-table_35.qmd b/quarto/table-templates/template-table_35.qmd index 9b59fb81..807433e7 100644 --- a/quarto/table-templates/template-table_35.qmd +++ b/quarto/table-templates/template-table_35.qmd @@ -1,17 +1,110 @@ --- title: FDA Table 35 subtitle: Patients With Adverse Events by System Organ Class, Safety Population, Pooled Analysis (or Trial X) -format: html --- +```{r setup, echo=FALSE} +``` + ::: panel-tabset + ## Spec. Screenshot ![](../assets/images/screenshots/table_35.png){fig-align="center" width="70%"} -## Table +## gtsummary Table + +
-```{r tbl, message=FALSE, warning=FALSE} +gtsummary Table Setup + +```{r tbl1, eval=FALSE, echo=TRUE} +# Load Libraries & Data +library(cardinal) + +adsl <- random.cdisc.data::cadsl +adae <- random.cdisc.data::cadae + +# Output Table +make_table_35(df = adae, denominator = adsl, return_ard = FALSE) +``` + +
+ +
+ +Function Details + +### `make_table_35()` + +------------------------------------------------------------------------ + +Required variables: + +- **`df`**: The variables specified by `id_var`, `arm_var`, `saffl_var`, and `soc_var`. +- **`denominator`** (if specified): `USUBJID` and the variables specified by `arm_var` and `saffl_var` +- If both the ARD (`return_ard = TRUE`) and an overall column (`lbl_overall` not missing) are requested, a list of two ARDs will be returned: one for the table by groups, and the other for the overall column. + ++---------------+------------------------------------------------------------------------------------+---------------+ +| **Argument** | **Description** | **Default** | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `df` | (`data.frame`) Dataset (typically ADSL) required to build table. | *No default* | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `return_ard` | (`flag`) Whether an ARD should be returned. | `TRUE` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `denominator` | (`character`) Alternative dataset used only to calculate column counts. | `NULL` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `id_var` | (`character`) Identifier variable used to count the participants within each flag. | `"USUBJID"` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `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"` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `soc_var` | (`character`) SOC variable from `adae` to include in the table. | `"ARBODSYS"` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `na_level` | (`character`) String to represent missing values. | `""` | ++---------------+------------------------------------------------------------------------------------+---------------+ +| `lbl_overall` | (`character`) String to represent the label of an overall column. | `NULL` | ++---------------+------------------------------------------------------------------------------------+---------------+ + +Source code for this function is available [here](https://github.com/pharmaverse/cardinal/blob/main/R/fda-table_35.R). + +
+ +## ARD + +
+ +ARD Setup + +```{r tbl2, echo=TRUE, message=FALSE, warning=FALSE, results='hide'} +# Load Libraries & Data +library(cardinal) + +adsl <- random.cdisc.data::cadsl +adae <- random.cdisc.data::cadae + +# Create Table and ARD +result <- make_table_35(df = adae, denominator = adsl) + +# Output ARD +result$ard +``` + +```{r tbl2-print, echo=FALSE} +withr::local_options(width = 9999) +print(result$ard, columns = "all") +``` + +
+ +## rtables Table + +
+ +rtables Table Setup + +```{r tbl3, eval=FALSE, echo=TRUE} # Load Libraries & Data library(random.cdisc.data) library(cardinal) @@ -21,34 +114,38 @@ adae <- random.cdisc.data::cadae # Output Table risk_diff <- list(arm_x = "B: Placebo", arm_y = "A: Drug X") # optional -make_table_35(adae = adae, alt_counts_df = adsl, risk_diff = risk_diff) +make_table_35_rtables(df = adae, alt_counts_df = adsl, risk_diff = risk_diff) ``` -## Table Setup +
-```{r tbl, eval=FALSE, echo=TRUE} +```{r tbl3, message=FALSE, warning=FALSE, eval=TRUE} ``` -## Function Details +
-### `make_table_35()` +Function Details + +### `make_table_35_rtables()` ------------------------------------------------------------------------ Required variables: -- **`adae`**: `USUBJID` and the variables specified by `arm_var`, `saffl_var` and `soc_var`. +- **`df`**: `USUBJID` and the variables specified by `arm_var`, `saffl_var` and `soc_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* | +| `df` | (`data.frame`) Dataset (typically ADAE) required to build table. | *No default* | +------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------+ | `alt_counts_df` | (`character`) Alternative dataset (typically ADSL) used only to calculate column counts. | `NULL` | +------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------+ | `show_colcounts` | (`flag`) Whether column counts should be printed. | `TRUE` | +------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------+ +| `id_var` | (`character`) Subject identifier variable . | `USUBJID` | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------+ | `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"` | @@ -75,4 +172,5 @@ Required variables: +------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------+ Source code for this function is available [here](https://github.com/pharmaverse/cardinal/blob/main/R/fda-table_35.R). +
::: diff --git a/tests/testthat/_snaps/fda-table_35.md b/tests/testthat/_snaps/fda-table_35.md index 2633781c..f3f3853e 100644 --- a/tests/testthat/_snaps/fda-table_35.md +++ b/tests/testthat/_snaps/fda-table_35.md @@ -1,15 +1,72 @@ # Table 35 generation works with default values Code - res + as.data.frame(res$table) Output - A: Drug X B: Placebo C: Combination - Primary System Organ Class (N=134) (N=134) (N=132) - ————————————————————————————————————————————————————————————————————— - cl D 96 (71.6%) 90 (67.2%) 98 (74.2%) - cl B 96 (71.6%) 89 (66.4%) 97 (73.5%) - cl A 78 (58.2%) 75 (56.0%) 89 (67.4%) - cl C 67 (50.0%) 75 (56.0%) 79 (59.8%) + **System Organ Class** **A: Drug X** \nN = 609 **B: Placebo** \nN = 622 **C: Combination** \nN = 703 + 1 cl A.1 78 (59%) 75 (58%) 89 (56%) + 2 cl B.1 47 (84%) 49 (82%) 43 (69%) + 3 cl B.2 79 (61%) 74 (54%) 85 (59%) + 4 cl C.1 43 (78%) 46 (73%) 43 (67%) + 5 cl C.2 35 (73%) 48 (91%) 55 (85%) + 6 cl D.1 79 (62%) 67 (63%) 80 (59%) + 7 cl D.2 47 (76%) 58 (81%) 57 (77%) + +--- + + Code + res$ard + Output + $tbl_hierarchical + Message + {cards} data frame: 72 x 13 + Output + group1 group1_level variable variable_level stat_name stat_label stat stat_fmt + 1 ARM A: Drug X AEBODSYS cl A.1 n n 78 78 + 2 ARM A: Drug X AEBODSYS cl A.1 N N 132 132 + 3 ARM A: Drug X AEBODSYS cl A.1 p % 0.591 59 + 4 ARM A: Drug X AEBODSYS cl B.1 n n 47 47 + 5 ARM A: Drug X AEBODSYS cl B.1 N N 56 56 + 6 ARM A: Drug X AEBODSYS cl B.1 p % 0.839 84 + 7 ARM A: Drug X AEBODSYS cl B.2 n n 79 79 + 8 ARM A: Drug X AEBODSYS cl B.2 N N 129 129 + 9 ARM A: Drug X AEBODSYS cl B.2 p % 0.612 61 + 10 ARM A: Drug X AEBODSYS cl C.1 n n 43 43 + Message + i 62 more rows + i Use `print(n = ...)` to see more rows + i 5 more variables: context, fmt_fn, warning, error, gts_column + Output + + +# Table 35 generation works with gtsummary with custom values + + Code + as.data.frame(res) + Output + **System Organ Class** **A: Drug X** \nN = 134 **B: Placebo** \nN = 134 **C: Combination** \nN = 132 + 1 cl A.1 78 (58%) 75 (56%) 89 (67%) + 2 cl B.1 47 (35%) 49 (37%) 43 (33%) + 3 cl B.2 79 (59%) 74 (55%) 85 (64%) + 4 cl C.1 43 (32%) 46 (34%) 43 (33%) + 5 cl C.2 35 (26%) 48 (36%) 55 (42%) + 6 cl D.1 79 (59%) 67 (50%) 80 (61%) + 7 cl D.2 47 (35%) 58 (43%) 57 (43%) + +# Table 35 generation works with gtsummary missing values + + Code + as.data.frame(res) + Output + **System Organ Class** **A: Drug X** \nN = 134 **B: Placebo** \nN = 134 **C: Combination** \nN = 132 + 1 cl A.1 78 (58%) 75 (56%) 89 (67%) + 2 cl B.1 47 (35%) 49 (37%) 43 (33%) + 3 cl B.2 79 (59%) 74 (55%) 84 (64%) + 4 cl C.1 43 (32%) 46 (34%) 42 (32%) + 5 cl C.2 34 (25%) 48 (36%) 55 (42%) + 6 cl D.1 79 (59%) 67 (50%) 79 (60%) + 7 cl D.2 47 (35%) 58 (43%) 57 (43%) + 8 2 (1.5%) 2 (1.5%) 6 (4.5%) # Table 35 generation works with custom values @@ -22,12 +79,15 @@ ——————————————————————————————————————————————————————————————————————————————————— Total A: Drug X B: Placebo C: Combination Population - Primary System Organ Class (N=134) (N=134) (N=132) (N=400) + Body System or Organ Class (N=134) (N=134) (N=132) (N=400) ——————————————————————————————————————————————————————————————————————————————————— - cl D 96 (71.6%) 90 (67.2%) 98 (74.2%) 284 (71.0%) - cl B 96 (71.6%) 89 (66.4%) 97 (73.5%) 282 (70.5%) - cl A 78 (58.2%) 75 (56.0%) 89 (67.4%) 242 (60.5%) - cl C 67 (50.0%) 75 (56.0%) 79 (59.8%) 221 (55.2%) + cl A.1 78 (58.2%) 75 (56.0%) 89 (67.4%) 242 (60.5%) + cl B.2 79 (59.0%) 74 (55.2%) 85 (64.4%) 238 (59.5%) + cl D.1 79 (59.0%) 67 (50.0%) 80 (60.6%) 226 (56.5%) + cl D.2 47 (35.1%) 58 (43.3%) 57 (43.2%) 162 (40.5%) + cl B.1 47 (35.1%) 49 (36.6%) 43 (32.6%) 139 (34.8%) + cl C.2 35 (26.1%) 48 (35.8%) 55 (41.7%) 138 (34.5%) + cl C.1 43 (32.1%) 46 (34.3%) 43 (32.6%) 132 (33.0%) ——————————————————————————————————————————————————————————————————————————————————— Source: [include Applicant source, datasets and/or software tools used]. @@ -48,12 +108,15 @@ res Output A: Drug X B: Placebo C: Combination Risk Difference (%) (95% CI) - Primary System Organ Class (N=134) (N=134) (N=132) (N=268) + Body System or Organ Class (N=134) (N=134) (N=132) (N=268) ———————————————————————————————————————————————————————————————————————————————————————————————————— - cl D 96 (71.6%) 90 (67.2%) 98 (74.2%) -4.5 (-15.5 - 6.5) - cl B 96 (71.6%) 89 (66.4%) 97 (73.5%) -5.2 (-16.3 - 5.8) - cl A 78 (58.2%) 75 (56.0%) 89 (67.4%) -2.2 (-14.1 - 9.6) - cl C 67 (50.0%) 75 (56.0%) 79 (59.8%) 6.0 (-6.0 - 17.9) + cl A.1 78 (58.2%) 75 (56.0%) 89 (67.4%) -2.2 (-14.1 - 9.6) + cl B.2 79 (59.0%) 74 (55.2%) 85 (64.4%) -3.7 (-15.6 - 8.1) + cl D.1 79 (59.0%) 67 (50.0%) 80 (60.6%) -9.0 (-20.8 - 2.9) + cl D.2 47 (35.1%) 58 (43.3%) 57 (43.2%) 8.2 (-3.4 - 19.9) + cl B.1 47 (35.1%) 49 (36.6%) 43 (32.6%) 1.5 (-10.0 - 13.0) + cl C.2 35 (26.1%) 48 (35.8%) 55 (41.7%) 9.7 (-1.3 - 20.7) + cl C.1 43 (32.1%) 46 (34.3%) 43 (32.6%) 2.2 (-9.0 - 13.5) # Table 35 generation works with some NA values @@ -61,12 +124,16 @@ res Output A: Drug X B: Placebo C: Combination - Primary System Organ Class (N=134) (N=134) (N=132) + Body System or Organ Class (N=134) (N=134) (N=132) ————————————————————————————————————————————————————————————————————— - cl D 96 (71.6%) 90 (67.2%) 98 (74.2%) - cl B 96 (71.6%) 89 (66.4%) 97 (73.5%) - cl A 78 (58.2%) 75 (56.0%) 89 (67.4%) - cl C 67 (50.0%) 75 (56.0%) 79 (59.8%) + cl A.1 78 (58.2%) 75 (56.0%) 89 (67.4%) + cl B.2 79 (59.0%) 74 (55.2%) 84 (63.6%) + cl D.1 79 (59.0%) 67 (50.0%) 79 (59.8%) + cl D.2 47 (35.1%) 58 (43.3%) 57 (43.2%) + cl B.1 47 (35.1%) 49 (36.6%) 43 (32.6%) + cl C.2 34 (25.4%) 48 (35.8%) 55 (41.7%) + cl C.1 43 (32.1%) 46 (34.3%) 42 (31.8%) + 2 (1.5%) 2 (1.5%) 6 (4.5%) # Table 35 generation works with custom values (SOC variable and label) @@ -91,9 +158,13 @@ res Output A: Drug X B: Placebo C: Combination - Primary System Organ Class (N=134) (N=134) (N=132) + Body System or Organ Class (N=134) (N=134) (N=132) ————————————————————————————————————————————————————————————————————— - cl A 95 (70.9%) 99 (73.9%) 109 (82.6%) - cl D 96 (71.6%) 90 (67.2%) 98 (74.2%) - cl B 96 (71.6%) 89 (66.4%) 97 (73.5%) + cl A.1 78 (58.2%) 75 (56.0%) 89 (67.4%) + cl B.2 79 (59.0%) 74 (55.2%) 85 (64.4%) + cl D.1 79 (59.0%) 67 (50.0%) 80 (60.6%) + cl D.2 47 (35.1%) 58 (43.3%) 57 (43.2%) + cl B.1 47 (35.1%) 49 (36.6%) 43 (32.6%) + cl C.2 35 (26.1%) 48 (35.8%) 55 (41.7%) + cl C.1 43 (32.1%) 46 (34.3%) 43 (32.6%) diff --git a/tests/testthat/test-fda-table_35.R b/tests/testthat/test-fda-table_35.R index 5001130a..ed53b0d9 100644 --- a/tests/testthat/test-fda-table_35.R +++ b/tests/testthat/test-fda-table_35.R @@ -2,14 +2,47 @@ adsl <- adsl_raw adae <- adae_raw test_that("Table 35 generation works with default values", { - result <- make_table_35(adae, adsl) + withr::local_options(list(width = 150)) + result <- make_table_35(adae) res <- expect_silent(result) - expect_snapshot(res) + expect_snapshot(res$table |> as.data.frame()) + expect_snapshot(res$ard) + + # no ARD + result2 <- make_table_35(adae, adsl, return_ard = FALSE) + res2 <- expect_silent(result2) + + # tables the same + expect_error(expect_identical(res$table, res2)) +}) + +# gtsummary ---- + +test_that("Table 35 generation works with gtsummary with custom values", { + withr::local_options(list(width = 150)) + + result <- make_table_35_gtsummary(df = adae, denominator = adsl) + + res <- expect_silent(result) + expect_snapshot(res |> as.data.frame()) }) +test_that("Table 35 generation works with gtsummary missing values", { + withr::local_options(list(width = 150)) + + set.seed(5) + adae[sample(seq_len(nrow(adae)), 10), "AEBODSYS"] <- NA + + result <- make_table_35_gtsummary(df = adae, denominator = adsl) + res <- expect_silent(result) + expect_snapshot(res |> as.data.frame()) +}) + +# rtables ----- + test_that("Table 35 generation works with custom values", { - result <- make_table_35( + result <- make_table_35_rtables( adae, adsl, lbl_overall = "Total\nPopulation", @@ -41,7 +74,7 @@ test_that("Table 35 generation works with custom values", { test_that("Table 35 generation works with risk difference column", { risk_diff <- list(arm_x = "B: Placebo", arm_y = "A: Drug X") - result <- make_table_35(adae, adsl, risk_diff = risk_diff) + result <- make_table_35_rtables(adae, adsl, risk_diff = risk_diff) res <- expect_silent(result) expect_snapshot(res) @@ -52,14 +85,14 @@ test_that("Table 35 generation works with some NA values", { adae[sample(seq_len(nrow(adae)), 10), "AEBODSYS"] <- NA adae <- adae %>% df_explicit_na() - result <- make_table_35(adae, adsl) + result <- make_table_35_rtables(adae, adsl) res <- expect_silent(result) expect_snapshot(res) }) test_that("Table 35 generation works with custom values (SOC variable and label)", { - result <- make_table_35( + result <- make_table_35_rtables( adae, adsl, lbl_overall = "Total\nPopulation", @@ -73,7 +106,7 @@ test_that("Table 35 generation works with custom values (SOC variable and label) test_that("Table 35 generation works with pruning", { adae$AESOC[adae$AESOC == "cl C"] <- "cl A" - result <- make_table_35(adae, adsl, prune_0 = TRUE) + result <- make_table_35_rtables(adae, adsl, prune_0 = TRUE) res <- expect_silent(result) expect_snapshot(res)