From d1609a6c263282b3040e160a1e8bb48808cac3f8 Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:19:02 -0400 Subject: [PATCH] Adds `denom` argument to count `s_*` functions (#1326) # Pull Request Fixes #1325 Adds `denom` to `s_count_occurrences_by_grade()` as well as `s_count_cumulative()` and `s_count_missed_doses()`. Downstream changes: - [ ] https://github.com/insightsengineering/scda.test/pull/155 - [ ] https://github.com/insightsengineering/tlg-catalog/pull/276 --------- Co-authored-by: shajoezhu Co-authored-by: 27856297+dependabot-preview[bot]@users.noreply.github.com <27856297+dependabot-preview[bot]@users.noreply.github.com> --- NEWS.md | 9 +- R/analyze_variables.R | 48 +++--- R/argument_convention.R | 4 + R/count_cumulative.R | 16 +- R/count_missed_doses.R | 8 +- R/count_occurrences.R | 31 ++-- R/count_occurrences_by_grade.R | 150 +++++++++++++----- R/count_patients_with_event.R | 2 +- R/count_patients_with_flags.R | 2 +- R/count_values.R | 2 +- man/analyze_variables.Rd | 6 +- man/argument_convention.Rd | 7 + man/count_cumulative.Rd | 14 ++ man/count_missed_doses.Rd | 26 ++- man/count_occurrences.Rd | 17 +- man/count_occurrences_by_grade.Rd | 43 +++-- man/count_patients_with_event.Rd | 4 +- man/count_patients_with_flags.Rd | 4 +- man/count_values.Rd | 4 +- tests/testthat/_snaps/count_cumulative.md | 16 ++ tests/testthat/_snaps/count_missed_doses.md | 18 +++ .../_snaps/count_occurrences_by_grade.md | 113 +++++++++++-- .../_snaps/estimate_incidence_rate.md | 134 ---------------- tests/testthat/test-analyze_variables.R | 2 +- tests/testthat/test-count_cumulative.R | 26 +++ tests/testthat/test-count_missed_doses.R | 26 ++- tests/testthat/test-count_occurrences.R | 15 +- .../test-count_occurrences_by_grade.R | 78 +++++++++ tests/testthat/test-g_km.R | 2 + 29 files changed, 555 insertions(+), 272 deletions(-) delete mode 100644 tests/testthat/_snaps/estimate_incidence_rate.md diff --git a/NEWS.md b/NEWS.md index e8b56db2bc..d2f9a43fe1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ # tern 0.9.6.9007 +### Enhancements +* Added the `denom` parameter to `s_count_cumulative()`, `s_count_missed_doses()`, and `s_count_occurrences_by_grade()`. +* Added `"N_row"` as an optional input to `denom` in `s_count_occurrences()`. +* Refactored `a_count_occurrences_by_grade()` to no longer use `make_afun()`. + ### Bug Fixes * Fixed bug in `a_summary()` causing non-unique `row_name` values to occur when multiple statistics are selected for count variables. @@ -15,7 +20,7 @@ * Refactored `estimate_incidence_rate` to work as both an analyze function and a summarize function, controlled by the added `summarize` parameter. When `summarize = TRUE`, labels can be fine-tuned via the new `label_fmt` argument to the same function. * Added `fraction` statistic to the `analyze_var_count` method group. * Improved `summarize_glm_count()` documentation and all its associated functions to better describe the results and the functions' purpose. -* Added `method` argument to `s_odds_ratio()` and `estimate_odds_ratio()` to control whether exact or approximate conditional likelihood calculations are used. +* Added `method` argument to `s_odds_ratio()` and `estimate_odds_ratio()` to control whether exact or approximate conditional likelihood calculations are used. ### Bug Fixes * Added defaults for `d_count_cumulative` parameters as described in the documentation. @@ -72,7 +77,7 @@ ### Miscellaneous * Added function `expect_snapshot_ggplot` to test setup file to process plot snapshot tests and allow plot dimensions to be set. * Adapted to argument renames introduced in `ggplot2` 3.5.0. -* Renamed `individual_patient_plot.R` to `g_ipp.R`. +* Renamed `individual_patient_plot.R` to `g_ipp.R`. * Removed all instances of deprecated parameters `time_unit_input`, `time_unit_output`, `na_level` and `indent_mod`. * Removed deprecated functions `summarize_vars`, `control_summarize_vars`, `a_compare`, `create_afun_summary`, `create_afun_compare`, and `summary_custom`. * Removed `vdiffr` package from Suggests in DESCRIPTION file. diff --git a/R/analyze_variables.R b/R/analyze_variables.R index 5a8ae1c31f..5af2961f10 100644 --- a/R/analyze_variables.R +++ b/R/analyze_variables.R @@ -238,11 +238,6 @@ s_summary.numeric <- function(x, #' @describeIn analyze_variables Method for `factor` class. #' -#' @param denom (`string`)\cr choice of denominator for factor proportions. Options are: -#' * `n`: number of values in this row and column intersection. -#' * `N_row`: total number of values in this row across columns. -#' * `N_col`: total number of values in this column across rows. -#' #' @return #' * If `x` is of class `factor` or converted from `character`, returns a `list` with named `numeric` items: #' * `n`: The [length()] of `x`. @@ -283,12 +278,11 @@ s_summary.numeric <- function(x, #' @export s_summary.factor <- function(x, na.rm = TRUE, # nolint - denom = c("n", "N_row", "N_col"), + denom = c("n", "N_col", "N_row"), .N_row, # nolint .N_col, # nolint ...) { assert_valid_factor(x) - denom <- match.arg(denom) if (na.rm) { x <- x[!is.na(x)] %>% fct_discard("") @@ -301,20 +295,23 @@ s_summary.factor <- function(x, y$n <- length(x) y$count <- as.list(table(x, useNA = "ifany")) - dn <- switch(denom, - n = length(x), - N_row = .N_row, - N_col = .N_col - ) + + denom <- match.arg(denom) %>% + switch( + n = length(x), + N_row = .N_row, + N_col = .N_col + ) + y$count_fraction <- lapply( y$count, function(x) { - c(x, ifelse(dn > 0, x / dn, 0)) + c(x, ifelse(denom > 0, x / denom, 0)) } ) y$fraction <- lapply( y$count, - function(count) c("num" = count, "denom" = dn) + function(count) c("num" = count, "denom" = denom) ) y$n_blq <- sum(grepl("BLQ|LTR|<[1-9]|% + switch( + n = length(x), + N_row = .N_row, + N_col = .N_col + ) y$count <- count - y$count_fraction <- c(count, ifelse(dn > 0, count / dn, 0)) + y$count_fraction <- c(count, ifelse(denom > 0, count / denom, 0)) y$n_blq <- 0L y } diff --git a/R/argument_convention.R b/R/argument_convention.R index e2b64b31c1..9e48b22308 100644 --- a/R/argument_convention.R +++ b/R/argument_convention.R @@ -30,6 +30,10 @@ #' @param col_by (`factor`)\cr defining column groups. #' @param conf_level (`proportion`)\cr confidence level of the interval. #' @param data (`data.frame`)\cr the dataset containing the variables to summarize. +#' @param denom (`string`)\cr choice of denominator for proportion. Options are: +#' * `n`: number of values in this row and column intersection. +#' * `N_row`: total number of values in this row across columns. +#' * `N_col`: total number of values in this column across rows. #' @param df (`data.frame`)\cr data set containing all analysis variables. #' @param groups_lists (named `list` of `list`)\cr optionally contains for each `subgroups` variable a #' list, which specifies the new group levels via the names and the diff --git a/R/count_cumulative.R b/R/count_cumulative.R index 90ee067f8b..0022c5c98e 100644 --- a/R/count_cumulative.R +++ b/R/count_cumulative.R @@ -78,7 +78,10 @@ h_count_cumulative <- function(x, length(x[is_keep & x > threshold]) } - result <- c(count = count, fraction = count / .N_col) + result <- c( + count = count, + fraction = if (count == 0 && .N_col == 0) 0 else count / .N_col + ) result } @@ -112,11 +115,20 @@ s_count_cumulative <- function(x, lower_tail = TRUE, include_eq = TRUE, .N_col, # nolint + .N_row, # nolint + denom = c("N_col", "n", "N_row"), ...) { checkmate::assert_numeric(thresholds, min.len = 1, any.missing = FALSE) + denom <- match.arg(denom) %>% + switch( + n = length(x), + N_row = .N_row, + N_col = .N_col + ) + count_fraction_list <- Map(function(thres) { - result <- h_count_cumulative(x, thres, lower_tail, include_eq, .N_col = .N_col, ...) + result <- h_count_cumulative(x, thres, lower_tail, include_eq, .N_col = denom, ...) label <- d_count_cumulative(thres, lower_tail, include_eq) formatters::with_label(result, label) }, thresholds) diff --git a/R/count_missed_doses.R b/R/count_missed_doses.R index 239b40ad01..8b3153cf47 100644 --- a/R/count_missed_doses.R +++ b/R/count_missed_doses.R @@ -58,13 +58,17 @@ d_count_missed_doses <- function(thresholds) { #' @keywords internal s_count_missed_doses <- function(x, thresholds, - .N_col) { # nolint + .N_col, # nolint + .N_row, # nolint + denom = c("N_col", "n", "N_row")) { stat <- s_count_cumulative( x = x, thresholds = thresholds, lower_tail = FALSE, include_eq = TRUE, - .N_col = .N_col + .N_col = .N_col, + .N_row = .N_row, + denom = denom ) labels <- d_count_missed_doses(thresholds) for (i in seq_along(stat$count_fraction)) { diff --git a/R/count_occurrences.R b/R/count_occurrences.R index 73052eb08c..1802eb80a1 100644 --- a/R/count_occurrences.R +++ b/R/count_occurrences.R @@ -51,9 +51,10 @@ NULL #' @describeIn count_occurrences Statistics function which counts number of patients that report an #' occurrence. #' -#' @param denom (`string`)\cr choice of denominator for patient proportions. Can be: -#' - `N_col`: total number of patients in this column across rows -#' - `n`: number of patients with any occurrences +#' @param denom (`string`)\cr choice of denominator for proportion. Options are: +#' * `N_col`: total number of patients in this column across rows. +#' * `n`: number of patients with any occurrences. +#' * `N_row`: total number of patients in this row across columns. #' #' @return #' * `s_count_occurrences()` returns a list with: @@ -66,6 +67,7 @@ NULL #' s_count_occurrences( #' df, #' .N_col = 4L, +#' .N_row = 4L, #' .df_row = df, #' .var = "MHDECOD", #' id = "USUBJID" @@ -73,8 +75,9 @@ NULL #' #' @export s_count_occurrences <- function(df, - denom = c("N_col", "n"), + denom = c("N_col", "n", "N_row"), .N_col, # nolint + .N_row, # nolint .df_row, drop = TRUE, .var = "MHDECOD", @@ -84,7 +87,6 @@ s_count_occurrences <- function(df, checkmate::assert_count(.N_col) checkmate::assert_multi_class(df[[.var]], classes = c("factor", "character")) checkmate::assert_multi_class(df[[id]], classes = c("factor", "character")) - denom <- match.arg(denom) occurrences <- if (drop) { # Note that we don't try to preserve original level order here since a) that would required @@ -101,10 +103,12 @@ s_count_occurrences <- function(df, df[[.var]] } ids <- factor(df[[id]]) - dn <- switch(denom, - n = nlevels(ids), - N_col = .N_col - ) + denom <- match.arg(denom) %>% + switch( + n = nlevels(ids), + N_row = .N_row, + N_col = .N_col + ) has_occurrence_per_id <- table(occurrences, ids) > 0 n_ids_per_occurrence <- as.list(rowSums(has_occurrence_per_id)) list( @@ -118,12 +122,12 @@ s_count_occurrences <- function(df, c(i, i / denom) } }, - denom = dn + denom = denom ), fraction = lapply( n_ids_per_occurrence, function(i, denom) c("num" = i, "denom" = denom), - denom = dn + denom = denom ) ) } @@ -147,9 +151,10 @@ s_count_occurrences <- function(df, a_count_occurrences <- function(df, labelstr = "", id = "USUBJID", - denom = c("N_col", "n"), + denom = c("N_col", "n", "N_row"), drop = TRUE, .N_col, # nolint + .N_row, # nolint .var = NULL, .df_row = NULL, .stats = NULL, @@ -159,7 +164,7 @@ a_count_occurrences <- function(df, na_str = default_na_str()) { denom <- match.arg(denom) x_stats <- s_count_occurrences( - df = df, denom = denom, .N_col = .N_col, .df_row = .df_row, drop = drop, .var = .var, id = id + df = df, denom = denom, .N_col = .N_col, .N_row = .N_row, .df_row = .df_row, drop = drop, .var = .var, id = id ) if (is.null(unlist(x_stats))) { return(NULL) diff --git a/R/count_occurrences_by_grade.R b/R/count_occurrences_by_grade.R index d77a049a7a..d23001605d 100644 --- a/R/count_occurrences_by_grade.R +++ b/R/count_occurrences_by_grade.R @@ -16,6 +16,7 @@ #' row/column context and operates on the level of the latest row split or the root of the table if no row splits have #' occurred. #' +#' @inheritParams count_occurrences #' @inheritParams argument_convention #' @param grade_groups (named `list` of `character`)\cr list containing groupings of grades. #' @param remove_single (`flag`)\cr `TRUE` to not include the elements of one-element grade groups @@ -149,15 +150,24 @@ h_append_grade_groups <- function(grade_groups, refs, remove_single = TRUE, only #' @export s_count_occurrences_by_grade <- function(df, .var, + .N_row, # nolint .N_col, # nolint id = "USUBJID", grade_groups = list(), remove_single = TRUE, only_grade_groups = FALSE, + denom = c("N_col", "n", "N_row"), labelstr = "") { assert_valid_factor(df[[.var]]) assert_df_with_variables(df, list(grade = .var, id = id)) + denom <- match.arg(denom) %>% + switch( + n = nlevels(factor(df[[id]])), + N_row = .N_row, + N_col = .N_col + ) + if (nrow(df) < 1) { grade_levels <- levels(df[[.var]]) l_count <- as.list(rep(0, length(grade_levels))) @@ -201,7 +211,17 @@ s_count_occurrences_by_grade <- function(df, l_count <- h_append_grade_groups(grade_groups, l_count, remove_single, only_grade_groups) } - l_count_fraction <- lapply(l_count, function(i, denom) c(i, i / denom), denom = .N_col) + l_count_fraction <- lapply( + l_count, + function(i, denom) { + if (i == 0 && denom == 0) { + c(0, 0) + } else { + c(i, i / denom) + } + }, + denom = denom + ) list( count_fraction = l_count_fraction @@ -215,22 +235,72 @@ s_count_occurrences_by_grade <- function(df, #' * `a_count_occurrences_by_grade()` returns the corresponding list with formatted [rtables::CellValue()]. #' #' @examples -#' # We need to ungroup `count_fraction` first so that the `rtables` formatting -#' # function `format_count_fraction()` can be applied correctly. -#' afun <- make_afun(a_count_occurrences_by_grade, .ungroup_stats = "count_fraction") -#' afun( +#' a_count_occurrences_by_grade( #' df, #' .N_col = 10L, +#' .N_row = 10L, #' .var = "AETOXGR", #' id = "USUBJID", #' grade_groups = list("ANY" = levels(df$AETOXGR)) #' ) #' #' @export -a_count_occurrences_by_grade <- make_afun( - s_count_occurrences_by_grade, - .formats = c("count_fraction" = format_count_fraction_fixed_dp) -) +a_count_occurrences_by_grade <- function(df, + labelstr = "", + id = "USUBJID", + grade_groups = list(), + remove_single = TRUE, + only_grade_groups = FALSE, + denom = c("N_col", "n", "N_row"), + .N_col, # nolint + .N_row, # nolint + .df_row, + .var = NULL, + .stats = NULL, + .formats = NULL, + .labels = NULL, + .indent_mods = NULL, + na_str = default_na_str()) { + x_stats <- s_count_occurrences_by_grade( + df = df, .var = .var, .N_row = .N_row, .N_col = .N_col, id = id, + grade_groups = grade_groups, remove_single = remove_single, only_grade_groups = only_grade_groups, + denom = denom, labelstr = labelstr + ) + + if (is.null(unlist(x_stats))) { + return(NULL) + } + x_lvls <- names(x_stats[[1]]) + + # Fill in with formatting defaults if needed + .stats <- get_stats("count_occurrences_by_grade", stats_in = .stats) + if (length(.formats) == 1 && is.null(names(.formats))) { + .formats <- rep(.formats, length(.stats)) %>% setNames(.stats) + } + .formats <- get_formats_from_stats(.stats, .formats) + .labels <- get_labels_from_stats(.stats, .labels, row_nms = x_lvls) + .indent_mods <- get_indents_from_stats(.stats, .indent_mods, row_nms = x_lvls) + + if ("count_fraction_fixed_dp" %in% .stats) x_stats[["count_fraction_fixed_dp"]] <- x_stats[["count_fraction"]] + x_stats <- x_stats[.stats] + + # Ungroup statistics with values for each level of x + x_ungrp <- ungroup_stats(x_stats, .formats, .labels, list()) + x_stats <- x_ungrp[["x"]] + .formats <- x_ungrp[[".formats"]] + + # Auto format handling + .formats <- apply_auto_formatting(.formats, x_stats, .df_row, .var) + + in_rows( + .list = x_stats, + .formats = .formats, + .names = unlist(.labels), + .labels = unlist(.labels), + .indent_mods = .indent_mods, + .format_na_strs = na_str + ) +} #' @describeIn count_occurrences_by_grade Layout-creating function which can take statistics function #' arguments and additional format arguments. This function is a wrapper for [rtables::analyze()]. @@ -300,40 +370,36 @@ count_occurrences_by_grade <- function(lyt, nested = TRUE, ..., table_names = var, - .stats = NULL, - .formats = NULL, + .stats = "count_fraction", + .formats = list(count_fraction = format_count_fraction_fixed_dp), .indent_mods = NULL, .labels = NULL) { checkmate::assert_flag(riskdiff) + extra_args <- list( + .stats = .stats, .formats = .formats, .labels = .labels, .indent_mods = .indent_mods, na_str = na_str + ) s_args <- list( id = id, grade_groups = grade_groups, remove_single = remove_single, only_grade_groups = only_grade_groups, ... ) - afun <- make_afun( - a_count_occurrences_by_grade, - .stats = .stats, - .formats = .formats, - .indent_mods = .indent_mods, - .ungroup_stats = "count_fraction" - ) - - extra_args <- if (isFALSE(riskdiff)) { - s_args + if (isFALSE(riskdiff)) { + extra_args <- c(extra_args, s_args) } else { - list( - afun = list("s_count_occurrences_by_grade" = afun), - .stats = .stats, - .indent_mods = .indent_mods, - s_args = s_args + extra_args <- c( + extra_args, + list( + afun = list("s_count_occurrences_by_grade" = a_count_occurrences_by_grade), + s_args = s_args + ) ) } analyze( lyt = lyt, vars = var, + afun = ifelse(isFALSE(riskdiff), a_count_occurrences_by_grade, afun_riskdiff), var_labels = var_labels, show_labels = show_labels, - afun = ifelse(isFALSE(riskdiff), afun, afun_riskdiff), table_names = table_names, na_str = na_str, nested = nested, @@ -377,29 +443,37 @@ summarize_occurrences_by_grade <- function(lyt, grade_groups = list(), remove_single = TRUE, only_grade_groups = FALSE, + riskdiff = FALSE, na_str = default_na_str(), ..., - .stats = NULL, - .formats = NULL, + .stats = "count_fraction", + .formats = list(count_fraction = format_count_fraction_fixed_dp), .indent_mods = NULL, .labels = NULL) { + checkmate::assert_flag(riskdiff) extra_args <- list( + .stats = .stats, .formats = .formats, .labels = .labels, .indent_mods = .indent_mods, na_str = na_str + ) + s_args <- list( id = id, grade_groups = grade_groups, remove_single = remove_single, only_grade_groups = only_grade_groups, ... ) - cfun <- make_afun( - a_count_occurrences_by_grade, - .stats = .stats, - .formats = .formats, - .labels = .labels, - .indent_mods = .indent_mods, - .ungroup_stats = "count_fraction" - ) + if (isFALSE(riskdiff)) { + extra_args <- c(extra_args, s_args) + } else { + extra_args <- c( + extra_args, + list( + afun = list("s_count_occurrences_by_grade" = a_count_occurrences_by_grade), + s_args = s_args + ) + ) + } summarize_row_groups( lyt = lyt, var = var, - cfun = cfun, + cfun = ifelse(isFALSE(riskdiff), a_count_occurrences_by_grade, afun_riskdiff), na_str = na_str, extra_args = extra_args ) diff --git a/R/count_patients_with_event.R b/R/count_patients_with_event.R index fb4275fcf6..995873bef9 100644 --- a/R/count_patients_with_event.R +++ b/R/count_patients_with_event.R @@ -65,7 +65,7 @@ s_count_patients_with_event <- function(df, filters, .N_col, # nolint .N_row, # nolint - denom = c("n", "N_row", "N_col")) { + denom = c("n", "N_col", "N_row")) { col_names <- names(filters) filter_values <- filters diff --git a/R/count_patients_with_flags.R b/R/count_patients_with_flags.R index e7fd062639..57272b36f0 100644 --- a/R/count_patients_with_flags.R +++ b/R/count_patients_with_flags.R @@ -58,7 +58,7 @@ s_count_patients_with_flags <- function(df, flag_labels = NULL, .N_col, # nolint .N_row, # nolint - denom = c("n", "N_row", "N_col")) { + denom = c("n", "N_col", "N_row")) { checkmate::assert_character(flag_variables) if (!is.null(flag_labels)) { checkmate::assert_character(flag_labels, len = length(flag_variables), any.missing = FALSE) diff --git a/R/count_values.R b/R/count_values.R index 4009e543a7..4f7b475eea 100644 --- a/R/count_values.R +++ b/R/count_values.R @@ -38,7 +38,7 @@ s_count_values <- function(x, na.rm = TRUE, # nolint .N_col, # nolint .N_row, # nolint - denom = c("n", "N_row", "N_col")) { + denom = c("n", "N_col", "N_row")) { UseMethod("s_count_values", x) } diff --git a/man/analyze_variables.Rd b/man/analyze_variables.Rd index ac83afe9f1..18a6b603dc 100644 --- a/man/analyze_variables.Rd +++ b/man/analyze_variables.Rd @@ -44,7 +44,7 @@ s_summary(x, na.rm = TRUE, denom, .N_row, .N_col, .var, ...) \method{s_summary}{factor}( x, na.rm = TRUE, - denom = c("n", "N_row", "N_col"), + denom = c("n", "N_col", "N_row"), .N_row, .N_col, ... @@ -53,7 +53,7 @@ s_summary(x, na.rm = TRUE, denom, .N_row, .N_col, .var, ...) \method{s_summary}{character}( x, na.rm = TRUE, - denom = c("n", "N_row", "N_col"), + denom = c("n", "N_col", "N_row"), .N_row, .N_col, .var, @@ -64,7 +64,7 @@ s_summary(x, na.rm = TRUE, denom, .N_row, .N_col, .var, ...) \method{s_summary}{logical}( x, na.rm = TRUE, - denom = c("n", "N_row", "N_col"), + denom = c("n", "N_col", "N_row"), .N_row, .N_col, ... diff --git a/man/argument_convention.Rd b/man/argument_convention.Rd index 775937acba..f973f4135c 100644 --- a/man/argument_convention.Rd +++ b/man/argument_convention.Rd @@ -49,6 +49,13 @@ that constitute the split. A custom label can be set for this level via the \cod \item{data}{(\code{data.frame})\cr the dataset containing the variables to summarize.} +\item{denom}{(\code{string})\cr choice of denominator for proportion. Options are: +\itemize{ +\item \code{n}: number of values in this row and column intersection. +\item \code{N_row}: total number of values in this row across columns. +\item \code{N_col}: total number of values in this column across rows. +}} + \item{df}{(\code{data.frame})\cr data set containing all analysis variables.} \item{groups_lists}{(named \code{list} of \code{list})\cr optionally contains for each \code{subgroups} variable a diff --git a/man/count_cumulative.Rd b/man/count_cumulative.Rd index b5cba3acd5..86d3cfb773 100644 --- a/man/count_cumulative.Rd +++ b/man/count_cumulative.Rd @@ -30,6 +30,8 @@ s_count_cumulative( lower_tail = TRUE, include_eq = TRUE, .N_col, + .N_row, + denom = c("N_col", "n", "N_row"), ... ) @@ -39,6 +41,8 @@ a_count_cumulative( lower_tail = TRUE, include_eq = TRUE, .N_col, + .N_row, + denom = c("N_col", "n", "N_row"), ... ) } @@ -85,6 +89,16 @@ unmodified default behavior. Can be negative.} \item{.N_col}{(\code{integer(1)})\cr column-wise N (column count) for the full column being analyzed that is typically passed by \code{rtables}.} + +\item{.N_row}{(\code{integer(1)})\cr row-wise N (row group count) for the group of observations being analyzed +(i.e. with no column-based subsetting) that is typically passed by \code{rtables}.} + +\item{denom}{(\code{string})\cr choice of denominator for proportion. Options are: +\itemize{ +\item \code{n}: number of values in this row and column intersection. +\item \code{N_row}: total number of values in this row across columns. +\item \code{N_col}: total number of values in this column across rows. +}} } \value{ \itemize{ diff --git a/man/count_missed_doses.Rd b/man/count_missed_doses.Rd index 3b2768f0e3..262c573a49 100644 --- a/man/count_missed_doses.Rd +++ b/man/count_missed_doses.Rd @@ -25,9 +25,21 @@ count_missed_doses( s_count_nonmissing(x) -s_count_missed_doses(x, thresholds, .N_col) +s_count_missed_doses( + x, + thresholds, + .N_col, + .N_row, + denom = c("N_col", "n", "N_row") +) -a_count_missed_doses(x, thresholds, .N_col) +a_count_missed_doses( + x, + thresholds, + .N_col, + .N_row, + denom = c("N_col", "n", "N_row") +) } \arguments{ \item{lyt}{(\code{PreDataTableLayouts})\cr layout that analyses will be added to.} @@ -67,6 +79,16 @@ unmodified default behavior. Can be negative.} \item{.N_col}{(\code{integer(1)})\cr column-wise N (column count) for the full column being analyzed that is typically passed by \code{rtables}.} + +\item{.N_row}{(\code{integer(1)})\cr row-wise N (row group count) for the group of observations being analyzed +(i.e. with no column-based subsetting) that is typically passed by \code{rtables}.} + +\item{denom}{(\code{string})\cr choice of denominator for proportion. Options are: +\itemize{ +\item \code{n}: number of values in this row and column intersection. +\item \code{N_row}: total number of values in this row across columns. +\item \code{N_col}: total number of values in this column across rows. +}} } \value{ \itemize{ diff --git a/man/count_occurrences.Rd b/man/count_occurrences.Rd index 856f6f29e1..b7b7320aed 100644 --- a/man/count_occurrences.Rd +++ b/man/count_occurrences.Rd @@ -41,8 +41,9 @@ summarize_occurrences( s_count_occurrences( df, - denom = c("N_col", "n"), + denom = c("N_col", "n", "N_row"), .N_col, + .N_row, .df_row, drop = TRUE, .var = "MHDECOD", @@ -53,9 +54,10 @@ a_count_occurrences( df, labelstr = "", id = "USUBJID", - denom = c("N_col", "n"), + denom = c("N_col", "n", "N_row"), drop = TRUE, .N_col, + .N_row, .var = NULL, .df_row = NULL, .stats = NULL, @@ -108,15 +110,19 @@ unmodified default behavior. Can be negative.} \item{df}{(\code{data.frame})\cr data set containing all analysis variables.} -\item{denom}{(\code{string})\cr choice of denominator for patient proportions. Can be: +\item{denom}{(\code{string})\cr choice of denominator for proportion. Options are: \itemize{ -\item \code{N_col}: total number of patients in this column across rows -\item \code{n}: number of patients with any occurrences +\item \code{N_col}: total number of patients in this column across rows. +\item \code{n}: number of patients with any occurrences. +\item \code{N_row}: total number of patients in this row across columns. }} \item{.N_col}{(\code{integer(1)})\cr column-wise N (column count) for the full column being analyzed that is typically passed by \code{rtables}.} +\item{.N_row}{(\code{integer(1)})\cr row-wise N (row group count) for the group of observations being analyzed +(i.e. with no column-based subsetting) that is typically passed by \code{rtables}.} + \item{.df_row}{(\code{data.frame})\cr data frame across all of the columns for the given row split.} \item{.var, var}{(\code{string})\cr single variable name that is passed by \code{rtables} when requested @@ -233,6 +239,7 @@ basic_table() \%>\% s_count_occurrences( df, .N_col = 4L, + .N_row = 4L, .df_row = df, .var = "MHDECOD", id = "USUBJID" diff --git a/man/count_occurrences_by_grade.Rd b/man/count_occurrences_by_grade.Rd index 547fa84d69..5c6df5f444 100644 --- a/man/count_occurrences_by_grade.Rd +++ b/man/count_occurrences_by_grade.Rd @@ -21,8 +21,8 @@ count_occurrences_by_grade( nested = TRUE, ..., table_names = var, - .stats = NULL, - .formats = NULL, + .stats = "count_fraction", + .formats = list(count_fraction = format_count_fraction_fixed_dp), .indent_mods = NULL, .labels = NULL ) @@ -34,10 +34,11 @@ summarize_occurrences_by_grade( grade_groups = list(), remove_single = TRUE, only_grade_groups = FALSE, + riskdiff = FALSE, na_str = default_na_str(), ..., - .stats = NULL, - .formats = NULL, + .stats = "count_fraction", + .formats = list(count_fraction = format_count_fraction_fixed_dp), .indent_mods = NULL, .labels = NULL ) @@ -45,23 +46,33 @@ summarize_occurrences_by_grade( s_count_occurrences_by_grade( df, .var, + .N_row, .N_col, id = "USUBJID", grade_groups = list(), remove_single = TRUE, only_grade_groups = FALSE, + denom = c("N_col", "n", "N_row"), labelstr = "" ) a_count_occurrences_by_grade( df, - .var, - .N_col, + labelstr = "", id = "USUBJID", grade_groups = list(), remove_single = TRUE, only_grade_groups = FALSE, - labelstr = "" + denom = c("N_col", "n", "N_row"), + .N_col, + .N_row, + .df_row, + .var = NULL, + .stats = NULL, + .formats = NULL, + .labels = NULL, + .indent_mods = NULL, + na_str = default_na_str() ) } \arguments{ @@ -115,12 +126,24 @@ unmodified default behavior. Can be negative.} \item{.var, var}{(\code{string})\cr single variable name that is passed by \code{rtables} when requested by a statistics function.} +\item{.N_row}{(\code{integer(1)})\cr row-wise N (row group count) for the group of observations being analyzed +(i.e. with no column-based subsetting) that is typically passed by \code{rtables}.} + \item{.N_col}{(\code{integer(1)})\cr column-wise N (column count) for the full column being analyzed that is typically passed by \code{rtables}.} +\item{denom}{(\code{string})\cr choice of denominator for proportion. Options are: +\itemize{ +\item \code{N_col}: total number of patients in this column across rows. +\item \code{n}: number of patients with any occurrences. +\item \code{N_row}: total number of patients in this row across columns. +}} + \item{labelstr}{(\code{string})\cr label of the level of the parent split currently being summarized (must be present as second argument in Content Row Functions). See \code{\link[rtables:summarize_row_groups]{rtables::summarize_row_groups()}} for more information.} + +\item{.df_row}{(\code{data.frame})\cr data frame across all of the columns for the given row split.} } \value{ \itemize{ @@ -248,12 +271,10 @@ s_count_occurrences_by_grade( grade_groups = list("ANY" = levels(df$AETOXGR)) ) -# We need to ungroup `count_fraction` first so that the `rtables` formatting -# function `format_count_fraction()` can be applied correctly. -afun <- make_afun(a_count_occurrences_by_grade, .ungroup_stats = "count_fraction") -afun( +a_count_occurrences_by_grade( df, .N_col = 10L, + .N_row = 10L, .var = "AETOXGR", id = "USUBJID", grade_groups = list("ANY" = levels(df$AETOXGR)) diff --git a/man/count_patients_with_event.Rd b/man/count_patients_with_event.Rd index 841bc102e5..a9aafc9a6d 100644 --- a/man/count_patients_with_event.Rd +++ b/man/count_patients_with_event.Rd @@ -27,7 +27,7 @@ s_count_patients_with_event( filters, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) a_count_patients_with_event( @@ -36,7 +36,7 @@ a_count_patients_with_event( filters, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) } \arguments{ diff --git a/man/count_patients_with_flags.Rd b/man/count_patients_with_flags.Rd index 1e9fe8a013..5cade48d70 100644 --- a/man/count_patients_with_flags.Rd +++ b/man/count_patients_with_flags.Rd @@ -30,7 +30,7 @@ s_count_patients_with_flags( flag_labels = NULL, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) a_count_patients_with_flags( @@ -40,7 +40,7 @@ a_count_patients_with_flags( flag_labels = NULL, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) } \arguments{ diff --git a/man/count_values.Rd b/man/count_values.Rd index 91c8660aa9..39b44918ba 100644 --- a/man/count_values.Rd +++ b/man/count_values.Rd @@ -29,7 +29,7 @@ s_count_values( na.rm = TRUE, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) \method{s_count_values}{character}(x, values = "Y", na.rm = TRUE, ...) @@ -44,7 +44,7 @@ a_count_values( na.rm = TRUE, .N_col, .N_row, - denom = c("n", "N_row", "N_col") + denom = c("n", "N_col", "N_row") ) } \arguments{ diff --git a/tests/testthat/_snaps/count_cumulative.md b/tests/testthat/_snaps/count_cumulative.md index a7f11e2bff..928125ec98 100644 --- a/tests/testthat/_snaps/count_cumulative.md +++ b/tests/testthat/_snaps/count_cumulative.md @@ -76,3 +76,19 @@ > 3 3 (60%) 4 (66.7%) > 7 1 (20%) 2 (33.3%) +# count_cumulative works with denom argument specified + + Code + res + Output + A B + ———————————————————————————— + x 5 2 + a + > 3 3 (60%) 1 (100%) + > 7 1 (20%) 1 (100%) + y 0 4 + a + > 3 0 3 (75%) + > 7 0 1 (25%) + diff --git a/tests/testthat/_snaps/count_missed_doses.md b/tests/testthat/_snaps/count_missed_doses.md index 5b9ab387bb..34981b9674 100644 --- a/tests/testthat/_snaps/count_missed_doses.md +++ b/tests/testthat/_snaps/count_missed_doses.md @@ -58,3 +58,21 @@ At least 3 missed doses 3 (60%) 5 (83.3%) At least 7 missed doses 2 (40%) 2 (33.3%) +# count_missed_doses works with denom argument specified + + Code + res + Output + A B + ———————————————————————————————————————————————— + x + Missed Doses + n 5 1 + At least 3 missed doses 3 (60%) 1 (100%) + At least 7 missed doses 2 (40%) 1 (100%) + y + Missed Doses + n 0 4 + At least 3 missed doses 0 4 (100%) + At least 7 missed doses 0 1 (25%) + diff --git a/tests/testthat/_snaps/count_occurrences_by_grade.md b/tests/testthat/_snaps/count_occurrences_by_grade.md index 50c754c762..94cf945880 100644 --- a/tests/testthat/_snaps/count_occurrences_by_grade.md +++ b/tests/testthat/_snaps/count_occurrences_by_grade.md @@ -228,6 +228,39 @@ +# a_count_occurrences_by_grade works with healthy input. + + Code + res + Output + RowsVerticalSection (in_rows) object print method: + ---------------------------- + row_name formatted_cell indent_mod row_label + 1 1 2 (20%) 0 1 + 2 2 2 (20%) 0 2 + 3 3 2 (20%) 0 3 + 4 4 0 0 4 + 5 5 0 0 5 + 6 1 2 (20.0%) 0 1 + 7 2 2 (20.0%) 0 2 + 8 3 2 (20.0%) 0 3 + 9 4 0 0 4 + 10 5 0 0 5 + +# a_count_occurrences_by_grade works with custom input. + + Code + res + Output + RowsVerticalSection (in_rows) object print method: + ---------------------------- + row_name formatted_cell indent_mod row_label + 1 Level: 1 2 (20%) 1 Level: 1 + 2 LVL 2 2 (20%) 2 LVL 2 + 3 Count of 3 2 (20%) 0 Count of 3 + 4 Missing 4 0 (0%) 3 Missing 4 + 5 5 0 (0%) 0 5 + # count_occurrences_by_grade works with default arguments for intensity Code @@ -248,9 +281,9 @@ A B D (N=3) (N=3) (N=0) ———————————————————————————————————————— - MILD 0 2 (66.7%) NA - MODERATE 1 (33.3%) 1 (33.3%) NA - SEVERE 2 (66.7%) 0 NA + MILD 0 2 (66.7%) 0 + MODERATE 1 (33.3%) 1 (33.3%) 0 + SEVERE 2 (66.7%) 0 0 # count_occurrences_by_grade label works when more than one variables are analyzed @@ -322,17 +355,17 @@ Code res Output - A B D - (N=10) (N=10) (N=0) - ——————————————————————————————————————————————————————— - LOW - MILD 0.00 (0.00%) 1.00 (10.00%) 0.00 (NA%) - MODERATE 0.00 (0.00%) 0.00 (0.00%) 0.00 (NA%) - SEVERE 2.00 (20.00%) 0.00 (0.00%) 0.00 (NA%) - HIGH - MILD 0.00 (0.00%) 1.00 (10.00%) 0.00 (NA%) - MODERATE 1.00 (10.00%) 1.00 (10.00%) 0.00 (NA%) - SEVERE 0.00 (0.00%) 0.00 (0.00%) 0.00 (NA%) + A B D + (N=10) (N=10) (N=0) + ————————————————————————————————————————————————————————— + LOW + MILD 0.00 (0.00%) 1.00 (10.00%) 0.00 (0.00%) + MODERATE 0.00 (0.00%) 0.00 (0.00%) 0.00 (0.00%) + SEVERE 2.00 (20.00%) 0.00 (0.00%) 0.00 (0.00%) + HIGH + MILD 0.00 (0.00%) 1.00 (10.00%) 0.00 (0.00%) + MODERATE 1.00 (10.00%) 1.00 (10.00%) 0.00 (0.00%) + SEVERE 0.00 (0.00%) 0.00 (0.00%) 0.00 (0.00%) # summarize_occurrences_by_grade works with custom arguments for grade @@ -400,3 +433,55 @@ MODERATE 7 (3.5%) 9 (5.1%) 6 (3.7%) -1.6 (-5.7 - 2.5) SEVERE 17 (8.4%) 23 (13.0%) 22 (13.6%) -4.6 (-10.8 - 1.7) +# count_occurrences_by_grade works with denom argument specified + + Code + res + Output + A B + ————————————————————————————————— + LOW 2 (100%) 1 (100%) + MILD 0 1 (100%) + MODERATE 0 0 + SEVERE 2 (100%) 0 + HIGH 1 (100%) 2 (100%) + MILD 0 1 (50.0%) + MODERATE 1 (100%) 1 (50.0%) + SEVERE 0 0 + +# summarize_occurrences works as expected with risk difference column + + Code + res + Output + A: Drug X B: Placebo C: Combination Risk Difference (%) (95% CI) + (N=202) (N=177) (N=162) (N=379) + ———————————————————————————————————————————————————————————————————————————————————— + F + MILD 3 (1.5%) 4 (2.3%) 1 (0.6%) -0.8 (-3.5 - 2.0) + MODERATE 8 (4.0%) 6 (3.4%) 6 (3.7%) 0.6 (-3.2 - 4.4) + SEVERE 22 (10.9%) 21 (11.9%) 20 (12.3%) -1.0 (-7.4 - 5.4) + M + MILD 3 (1.5%) 0 1 (0.6%) 1.5 (-0.2 - 3.2) + MODERATE 11 (5.4%) 9 (5.1%) 8 (4.9%) 0.4 (-4.1 - 4.9) + SEVERE 12 (5.9%) 17 (9.6%) 12 (7.4%) -3.7 (-9.1 - 1.8) + +--- + + Code + res + Output + A: Drug X B: Placebo C: Combination Risk Difference (%) (95% CI) + (N=202) (N=177) (N=162) (N=379) + ————————————————————————————————————————————————————————————————————————————————————— + F + -Any- 20 (9.9%) 19 (10.7%) 19 (11.7%) -0.8 (-7.0 - 5.3) + MILD 2 (1.0%) 2 (1.1%) 0 -0.1 (-2.2 - 1.9) + MODERATE 4 (2.0%) 3 (1.7%) 3 (1.9%) 0.3 (-2.4 - 3.0) + SEVERE 14 (6.9%) 14 (7.9%) 16 (9.9%) -1.0 (-6.3 - 4.3) + M + -Any- 14 (6.9%) 21 (11.9%) 17 (10.5%) -4.9 (-10.8 - 1.0) + MILD 1 (0.5%) 0 1 (0.6%) 0.5 (-0.5 - 1.5) + MODERATE 4 (2.0%) 7 (4.0%) 5 (3.1%) -2.0 (-5.4 - 1.5) + SEVERE 9 (4.5%) 14 (7.9%) 11 (6.8%) -3.5 (-8.3 - 1.4) + diff --git a/tests/testthat/_snaps/estimate_incidence_rate.md b/tests/testthat/_snaps/estimate_incidence_rate.md deleted file mode 100644 index 7133820dea..0000000000 --- a/tests/testthat/_snaps/estimate_incidence_rate.md +++ /dev/null @@ -1,134 +0,0 @@ -# control_incidence_rate works with customized parameters - - Code - res - Output - $conf_level - [1] 0.9 - - $conf_type - [1] "exact" - - $input_time_unit - [1] "month" - - $num_pt_year - [1] 100 - - -# h_incidence_rate_normal works as expected with healthy input - - Code - res - Output - $rate - [1] 0.01 - - $rate_ci - [1] -0.001630872 0.021630872 - - -# h_incidence_rate_normal_log works as expected with healthy input - - Code - res - Output - $rate - [1] 0.01 - - $rate_ci - [1] 0.003125199 0.031997963 - - -# h_incidence_rate_exact works as expected with healthy input - - Code - res - Output - $rate - [1] 0.01 - - $rate_ci - [1] 0.001776808 0.031478968 - - -# h_incidence_rate_byar works as expected with healthy input - - Code - res - Output - $rate - [1] 0.01 - - $rate_ci - [1] 0.002820411 0.027609866 - - -# h_incidence_rate works as expected with healthy input - - Code - res - Output - $rate - [1] 1 - - $rate_ci - [1] 0.3125199 3.1997963 - - -# s_incidence_rate works as expected with healthy input - - Code - res - Output - $person_years - [1] 9.058333 - attr(,"label") - [1] "Total patient-years at risk" - - $n_events - [1] 4 - attr(,"label") - [1] "Number of adverse events observed" - - $rate - [1] 44.15823 - attr(,"label") - [1] "AE rate per 100 patient-years" - - $rate_ci - [1] 19.40154 100.50487 - attr(,"label") - [1] "90% CI" - - $n_rate - [1] 4.00000 44.15823 - attr(,"label") - [1] "Number of adverse events observed (AE rate per 100 patient-years)" - - -# estimate_incidence_rate works as expected with healthy input - - Code - res - Output - A B - (N=3) (N=3) - ———————————————————————————————————————————————————————————————————— - Total patient-years at risk 3.8 5.2 - Number of adverse events observed 1 3 - AE rate per 100 patient-years 26.20 57.23 - 90% CI (5.06, 135.73) (22.14, 147.94) - -# estimate_incidence_rate `n_rate` statistic works as expected - - Code - res - Output - A B - (N=3) (N=3) - ————————————————————————————————————————————————————————————————————————————————————— - Number of adverse events observed 1 3 - AE rate per 100 patient-years 2.18 4.77 - Number of adverse events observed (AE rate per 100 patient-years) 1 (2.2) 3 (4.8) - diff --git a/tests/testthat/test-analyze_variables.R b/tests/testthat/test-analyze_variables.R index f64d01f045..9940e36c3e 100644 --- a/tests/testthat/test-analyze_variables.R +++ b/tests/testthat/test-analyze_variables.R @@ -165,7 +165,7 @@ testthat::test_that("a_summary work with healthy input.", { res_out <- testthat::expect_silent(result) # numeric input - a_summary - result <- a_summary(x = x, .N_col = 10, .N_row = 20, .var = "bla") + result <- a_summary(x = x, .N_col = 10, .N_row = 10, .var = "bla") res <- testthat::expect_silent(result) testthat::expect_identical(res_out, res) testthat::expect_snapshot(res) diff --git a/tests/testthat/test-count_cumulative.R b/tests/testthat/test-count_cumulative.R index b773669c69..49c20ebbdd 100644 --- a/tests/testthat/test-count_cumulative.R +++ b/tests/testthat/test-count_cumulative.R @@ -102,3 +102,29 @@ testthat::test_that("count_cumulative works with customized arguments", { res <- testthat::expect_silent(result) testthat::expect_snapshot(res) }) + +testthat::test_that("count_cumulative works with denom argument specified", { + set.seed(1, kind = "Mersenne-Twister") + df <- data.frame( + a = c(sample(1:10, 10), NA), + type = factor(sample(c("x", "y"), 11, replace = TRUE)), + grp = factor(c(rep("A", 5), rep("B", 6)), levels = c("A", "B")) + ) + + result <- basic_table() %>% + split_cols_by("grp") %>% + split_rows_by("type") %>% + summarize_row_groups(format = "xx.") %>% + count_cumulative( + vars = "a", + thresholds = c(3, 7), + lower_tail = FALSE, + include_eq = FALSE, + na.rm = FALSE, + denom = "n" + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-count_missed_doses.R b/tests/testthat/test-count_missed_doses.R index 3061125599..ea46ec1eb2 100644 --- a/tests/testthat/test-count_missed_doses.R +++ b/tests/testthat/test-count_missed_doses.R @@ -28,7 +28,8 @@ testthat::test_that("s_count_missed_doses works as expected", { result <- s_count_missed_doses( x = c(0, 1, 0, 2, 3, 4, 0, 2), thresholds = c(2, 5), - .N_col = 10 + .N_col = 10, + .N_row = 10 ) res <- testthat::expect_silent(result) @@ -54,3 +55,26 @@ testthat::test_that("count_missed_doses works as expected", { res <- testthat::expect_silent(result) testthat::expect_snapshot(res) }) + +testthat::test_that("count_missed_doses works with denom argument specified", { + set.seed(1) + df <- data.frame( + a = c(sample(1:10, 10), NA), + type = factor(sample(c("x", "y"), 11, replace = TRUE)), + grp = factor(c(rep("A", 5), rep("B", 6)), levels = c("A", "B")) + ) + + result <- basic_table() %>% + split_cols_by("grp") %>% + split_rows_by("type") %>% + count_missed_doses( + "a", + thresholds = c(3, 7), + var_labels = "Missed Doses", + denom = "n" + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-count_occurrences.R b/tests/testthat/test-count_occurrences.R index 35994b913e..dec4fa5cbb 100644 --- a/tests/testthat/test-count_occurrences.R +++ b/tests/testthat/test-count_occurrences.R @@ -4,7 +4,7 @@ testthat::test_that("s_count_occurrences functions as expected with valid input MHDECOD = c("MH1", "MH2", "MH1", "MH1", "MH1", "MH3") ) - result <- s_count_occurrences(df = df, .N_col = 4L, .df_row = df) + result <- s_count_occurrences(df = df, .N_col = 4L, .N_row = 4L, .df_row = df) res <- testthat::expect_silent(result) testthat::expect_snapshot(res) @@ -18,7 +18,7 @@ testthat::test_that("s_count_occurrences drops non appearing levels by default", levels = c("MH1", "MH2", "MH3", "MHX") ) ) - result <- s_count_occurrences(df = df, .N_col = 4L, .df_row = df) + result <- s_count_occurrences(df = df, .N_col = 4L, .N_row = 4L, .df_row = df) testthat::expect_false("MHX" %in% c(names(result$count), names(result$count_fraction), names(result$fraction))) }) @@ -30,7 +30,7 @@ testthat::test_that("s_count_occurrences keeps non appearing levels if requested levels = c("MH1", "MH2", "MH3", "MHX") ) ) - result <- s_count_occurrences(df = df, .N_col = 4L, .df_row = df, drop = FALSE) + result <- s_count_occurrences(df = df, .N_col = 4L, .N_row = 4L, .df_row = df, drop = FALSE) testthat::expect_true("MHX" %in% names(result$count)) testthat::expect_true("MHX" %in% names(result$count_fraction)) testthat::expect_true("MHX" %in% names(result$fraction)) @@ -48,6 +48,7 @@ testthat::test_that("s_count_occurrences fails when it receives empty .df_row an testthat::expect_error(s_count_occurrences( df = df_sub, .N_col = 4L, + .N_row = 4L, .df_row = df_sub, drop = TRUE )) @@ -59,7 +60,7 @@ testthat::test_that("s_count_occurrences functions as expected when requesting d MHDECOD = c("MH1", "MH2", "MH1", "MH1", "MH1", "MH3") ) - result <- s_count_occurrences(df = df, denom = "n", .N_col = 4L, .df_row = df) + result <- s_count_occurrences(df = df, denom = "n", .N_col = 4L, .N_row = 4L, .df_row = df) res <- testthat::expect_silent(result) testthat::expect_snapshot(res) @@ -74,7 +75,7 @@ testthat::test_that("a_count_occurrences works with healthy input.", { x = factor(c("a", "a", "b", "c", "a")) ) result <- a_count_occurrences( - df = df, .N_col = 10, .stats = get_stats("count_occurrences"), .var = "x", id = "id", .df_row = df + df = df, .N_col = 10, .N_row = 10, .stats = get_stats("count_occurrences"), .var = "x", id = "id", .df_row = df ) res_out <- testthat::expect_silent(result) @@ -84,7 +85,7 @@ testthat::test_that("a_count_occurrences works with healthy input.", { x = c("a", "a", "b", "c", "a") ) result <- a_count_occurrences( - df = df, .N_col = 10, .stats = get_stats("count_occurrences"), .var = "x", id = "id", .df_row = df + df = df, .N_col = 10, .N_row = 10, .stats = get_stats("count_occurrences"), .var = "x", id = "id", .df_row = df ) res_out <- testthat::expect_silent(result) }) @@ -98,7 +99,7 @@ testthat::test_that("a_count_occurrences works with custom input.", { ) result <- a_count_occurrences( - df = df, .df_row = df, .var = "x", id = "id", .N_col = 5, + df = df, .df_row = df, .var = "x", id = "id", .N_col = 5, .N_row = 5, .stats = c("count", "count_fraction"), drop = FALSE, .formats = c(count_fraction = "xx (xx%)"), .labels = c(a = "Level: a", b = "LVL B", count.c = "Count of c", d = "Missing D"), diff --git a/tests/testthat/test-count_occurrences_by_grade.R b/tests/testthat/test-count_occurrences_by_grade.R index 105c906585..7f5b09bfdc 100644 --- a/tests/testthat/test-count_occurrences_by_grade.R +++ b/tests/testthat/test-count_occurrences_by_grade.R @@ -135,6 +135,34 @@ testthat::test_that("s_count_occurrences_by_grade works with valid input for int testthat::expect_snapshot(res) }) +testthat::test_that("a_count_occurrences_by_grade works with healthy input.", { + options("width" = 100) + + result <- a_count_occurrences_by_grade( + df = raw_data, .N_col = 10, .N_row = 10, .df_row = raw_data, + .stats = get_stats("count_occurrences_by_grade"), + .var = "AETOXGR", id = "USUBJID" + ) + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("a_count_occurrences_by_grade works with custom input.", { + options("width" = 100) + + result <- a_count_occurrences_by_grade( + df = raw_data, .N_col = 10, .N_row = 10, .df_row = raw_data, + .stats = "count_fraction", + .formats = c(count_fraction = "xx (xx%)"), + .labels = list("1" = "Level: 1", "2" = "LVL 2", "count_fraction.3" = "Count of 3", "4" = "Missing 4"), + .indent_mods = list("1" = 1L, "2" = 2L, "count_fraction.4" = 3L), + .var = "AETOXGR", id = "USUBJID" + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + testthat::test_that("count_occurrences_by_grade works with default arguments for intensity", { df <- raw_data df_adsl <- unique(df[c("ARM", "ARM_EMPTY", "USUBJID")]) @@ -414,3 +442,53 @@ testthat::test_that("count_occurrences_by_grade works as expected with risk diff res <- testthat::expect_silent(result) testthat::expect_snapshot(res) }) + +testthat::test_that("count_occurrences_by_grade works with denom argument specified", { + df <- raw_data + df_adsl <- unique(df[c("ARM", "ARM_EMPTY", "USUBJID")]) + + result <- basic_table() %>% + split_cols_by("ARM") %>% + split_rows_by("BMRKR") %>% + summarize_occurrences_by_grade(var = "BMRKR", denom = "n") %>% + count_occurrences_by_grade(var = "AESEV", denom = "n") %>% + build_table(df, alt_counts_df = df_adsl) + + res <- testthat::expect_silent(result[-c(2, 6)]) + testthat::expect_snapshot(res) +}) + +testthat::test_that("summarize_occurrences works as expected with risk difference column", { + tern_ex_adae$AESEV <- factor(tern_ex_adae$AESEV) + + # Default parameters + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM", split_fun = add_riskdiff("A: Drug X", "B: Placebo")) %>% + split_rows_by("SEX", child_labels = "visible") %>% + summarize_occurrences_by_grade( + var = "AESEV", + riskdiff = TRUE + ) %>% + build_table(tern_ex_adae) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) + + # Grade groups, custom id var + grade_groups <- list("-Any-" = levels(tern_ex_adae$AESEV)) + + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM", split_fun = add_riskdiff("A: Drug X", "B: Placebo")) %>% + split_rows_by("SEX", child_labels = "visible") %>% + summarize_occurrences_by_grade( + var = "AESEV", + riskdiff = TRUE, + .indent_mods = 1L, + grade_groups = grade_groups, + id = "SITEID" + ) %>% + build_table(tern_ex_adae) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-g_km.R b/tests/testthat/test-g_km.R index ebce5ad5d0..e137387214 100644 --- a/tests/testthat/test-g_km.R +++ b/tests/testthat/test-g_km.R @@ -1,3 +1,5 @@ +library(nestcolor) + df <- tern_ex_adtte %>% filter(PARAMCD == "OS") %>% mutate(is_event = CNSR == 0)