diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index ecf5ee1f22..c6c288fea2 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -68,6 +68,9 @@ jobs: uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} + with: + additional-env-vars: | + NOT_CRAN=true linter: if: github.event_name != 'push' name: SuperLinter πŸ¦Έβ€β™€οΈ diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d50b0e3295..27667cefef 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -52,6 +52,9 @@ jobs: uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} + with: + additional-env-vars: | + NOT_CRAN=true wasm: name: Build WASM packages πŸ§‘β€πŸ­ needs: release diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index 2fcaaccace..68181c320d 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -5,9 +5,26 @@ on: schedule: - cron: '45 3 * * 0' workflow_dispatch: + inputs: + chosen-workflow: + description: | + Select which workflow you'd like to run + required: true + type: choice + default: rhub + options: + - rhub + - dependency-test + - branch-cleanup + - revdepcheck jobs: dependency-test: + if: > + github.event_name == 'schedule' || ( + github.event_name == 'workflow_dispatch' && + inputs.chosen-workflow == 'dependency-test' + ) strategy: fail-fast: false matrix: @@ -24,6 +41,11 @@ jobs: extra-deps: | lme4 (>= 1.1-35) branch-cleanup: + if: > + github.event_name == 'schedule' || ( + github.event_name == 'workflow_dispatch' && + inputs.chosen-workflow == 'branch-cleanup' + ) name: Branch Cleanup 🧹 uses: insightsengineering/r.pkg.template/.github/workflows/branch-cleanup.yaml@main secrets: @@ -34,9 +56,19 @@ jobs: with: issue-assignees: "shajoezhu,Melkiades,edelarua,gmbecker,ayogasekaram" revdepcheck: + if: > + github.event_name == 'schedule' || ( + github.event_name == 'workflow_dispatch' && + inputs.chosen-workflow == 'revdepcheck' + ) name: revdepcheck ↩️ uses: insightsengineering/r.pkg.template/.github/workflows/revdepcheck.yaml@main rhub: + if: > + github.event_name == 'schedule' || ( + github.event_name == 'workflow_dispatch' && + inputs.chosen-workflow == 'rhub' + ) name: R-hub 🌐 uses: insightsengineering/r.pkg.template/.github/workflows/rhub.yaml@main with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9961e01a3..3e25af0ea2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.4.3 + rev: v0.4.3.9001 hooks: - id: style-files name: Style code with `styler` diff --git a/DESCRIPTION b/DESCRIPTION index 9396f1cba6..9cd6a36b1a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tern Title: Create Common TLGs Used in Clinical Trials -Version: 0.9.5.9022 -Date: 2024-09-09 +Version: 0.9.6 +Date: 2024-09-20 Authors@R: c( person("Joe", "Zhu", , "joe.zhu@roche.com", role = c("aut", "cre")), person("Daniel", "SabanΓ©s BovΓ©", , "daniel.sabanes_bove@roche.com", role = "aut"), @@ -63,7 +63,8 @@ Suggests: testthat (>= 3.1.9), withr (>= 2.0.0) VignetteBuilder: - knitr + knitr, + rmarkdown RdMacros: lifecycle, Rdpack @@ -130,6 +131,7 @@ Collate: 'h_adsl_adlb_merge_using_worst_flag.R' 'h_biomarkers_subgroups.R' 'h_cox_regression.R' + 'h_incidence_rate.R' 'h_km.R' 'h_logistic_regression.R' 'h_map_for_count_abnormal.R' diff --git a/NAMESPACE b/NAMESPACE index f65577812e..e5fbc85f5c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,6 +28,7 @@ export(a_count_patients_with_event) export(a_count_patients_with_flags) export(a_count_values) export(a_coxreg) +export(a_incidence_rate) export(a_length_proportion) export(a_odds_ratio) export(a_proportion) diff --git a/NEWS.md b/NEWS.md index 563bb9a09d..925fbb340e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,20 +1,24 @@ -# tern 0.9.5.9022 +# tern 0.9.6 + ### Enhancements * Added `median_long`, `quantiles_lower` and `quantiles_upper` to `s_surv_time` which includes estimate and confidence interval in one statistic. * Added `hr_long` to `s_coxph_pairwise` which includes estimate and confidence interval in one statistic. * Added `event_free_rate_long` to `s_surv_timepoint` which includes estimate and confidence interval in one statistic. * Added `rate_diff_long` to `s_surv_timepoint_diff` which includes estimate and confidence interval in one statistic. * Added `errorbar_width` and `linetype` parameters to `g_lineplot`. -* Reworking of `summarize_glm_count()` documentation and all its associated functions to better describe the results and the functions' purpose. * Added the `.formats` argument to `tabulate_rsp_subgroups` and `tabulate_survival_subgroups` to allow users to specify formats. * Added the `riskdiff` argument to `tabulate_rsp_subgroups` and `tabulate_survival_subgroups` to allow users to add a risk difference table column, and function `control_riskdiff` to specify settings for the risk difference column. * Added warning to `tabulate_rsp_subgroups` when `pval` statistic is selected but `df` has not been correctly generated to add p-values to the output table. * Added `n_rate` statistic as a non-default option to `estimate_incidence_rate` which returns both number of events observed and estimated incidence rate. +* Added `n_unique` statistic as a non-default option to `estimate_incidence_rate` which returns total number of patients with at least one event observed. +* 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. ### Bug Fixes -* Fixed a bug in `a_surv_time` that threw an error when split only has `"is_event"`. * Added defaults for `d_count_cumulative` parameters as described in the documentation. -* Empty levels on `g_lineplot` x-axis are not shown in either plots. +* Fixed a bug of empty levels on `g_lineplot` x-axis were not shown in either plots. +* Fixed a bug in `a_surv_time` that threw an error when split only has `"is_event"`. * Fixed disappearing line in `g_lineplot` when using only one group or strata level. * Fixed defaults for formats and labels in `get_formats_from_stats` and `get_labels_from_stats`. * Fixed bug for linear scaling factor (`scale` parameter) being applied to response but not to rate in `h_glm_count` while all distributions have logarithmic link function. @@ -26,6 +30,7 @@ * Began deprecation of the confusing functions `summary_formats` and `summary_labels`. * Enhanced general descriptions of analyze and summarize functions throughout package documentation. * Finalized deprecation of the `strata` and `cohort_id` arguments to `g_lineplot`. +* Moved incidence rate helper functions into a separate `h_incidence_rate.R` file. # tern 0.9.5 diff --git a/R/analyze_variables.R b/R/analyze_variables.R index 6f6a212131..69e0041607 100644 --- a/R/analyze_variables.R +++ b/R/analyze_variables.R @@ -310,6 +310,10 @@ s_summary.factor <- function(x, c(x, ifelse(dn > 0, x / dn, 0)) } ) + y$fraction <- lapply( + y$count, + function(count) c("num" = count, "denom" = dn) + ) y$n_blq <- sum(grepl("BLQ|LTR|<[1-9]|% eval() + .formats <- c(.formats, formats_def)[!duplicated(names(c(.formats, formats_def)))] + labels_def <- sapply(x_stats, \(x) attributes(x)$label) + .labels <- c(.labels, labels_def)[!duplicated(names(c(.labels, labels_def)))] + if (nzchar(labelstr) > 0) { + .labels <- sapply(.labels, \(x) gsub("%.labels", x, gsub("%s", labelstr, label_fmt))) + } + + # Fill in with formatting defaults if needed + .stats <- get_stats("estimate_incidence_rate", stats_in = .stats) + .formats <- get_formats_from_stats(.stats, .formats) + .labels <- get_labels_from_stats(.stats, .labels) + .indent_mods <- get_indents_from_stats(.stats, .indent_mods) + + x_stats <- x_stats[.stats] + + in_rows( + .list = x_stats, + .formats = .formats, + .labels = .labels, + .indent_mods = .indent_mods, + .format_na_strs = na_str + ) +} #' @describeIn incidence_rate Layout-creating function which can take statistics function arguments #' and additional format arguments. This function is a wrapper for [rtables::analyze()]. @@ -115,20 +186,8 @@ a_incidence_rate <- make_afun( #' the statistics from `s_incidence_rate()` to the table layout. #' #' @examples -#' library(dplyr) -#' -#' df <- data.frame( -#' USUBJID = as.character(seq(6)), -#' CNSR = c(0, 1, 1, 0, 0, 0), -#' AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), -#' ARM = factor(c("A", "A", "A", "B", "B", "B")) -#' ) %>% -#' mutate(is_event = CNSR == 0) %>% -#' mutate(n_events = as.integer(is_event)) -#' -#' basic_table() %>% +#' basic_table(show_colcounts = TRUE) %>% #' split_cols_by("ARM") %>% -#' add_colcounts() %>% #' estimate_incidence_rate( #' vars = "AVAL", #' n_events = "n_events", @@ -139,14 +198,30 @@ a_incidence_rate <- make_afun( #' ) %>% #' build_table(df) #' +#' # summarize = TRUE +#' basic_table(show_colcounts = TRUE) %>% +#' split_cols_by("ARM") %>% +#' split_rows_by("STRATA1", child_labels = "visible") %>% +#' estimate_incidence_rate( +#' vars = "AVAL", +#' n_events = "n_events", +#' .stats = c("n_unique", "n_rate"), +#' summarize = TRUE, +#' label_fmt = "%.labels" +#' ) %>% +#' build_table(df) +#' #' @export #' @order 2 estimate_incidence_rate <- function(lyt, vars, n_events, + id_var = "USUBJID", control = control_incidence_rate(), na_str = default_na_str(), nested = TRUE, + summarize = FALSE, + label_fmt = "%s - %.labels", ..., show_labels = "hidden", table_names = vars, @@ -154,152 +229,29 @@ estimate_incidence_rate <- function(lyt, .formats = NULL, .labels = NULL, .indent_mods = NULL) { - extra_args <- list(n_events = n_events, control = control, ...) - - afun <- make_afun( - a_incidence_rate, - .stats = .stats, - .formats = .formats, - .labels = .labels, - .indent_mods = .indent_mods - ) - - analyze( - lyt, - vars, - show_labels = show_labels, - table_names = table_names, - afun = afun, - na_str = na_str, - nested = nested, - extra_args = extra_args + extra_args <- c( + list(.stats = .stats, .formats = .formats, .labels = .labels, .indent_mods = .indent_mods, na_str = na_str), + list(n_events = n_events, id_var = id_var, control = control, label_fmt = label_fmt, ...) ) -} -#' Helper functions for incidence rate -#' -#' @description `r lifecycle::badge("stable")` -#' -#' @inheritParams incidence_rate -#' @param person_years (`numeric(1)`)\cr total person-years at risk. -#' @param alpha (`numeric(1)`)\cr two-sided alpha-level for confidence interval. -#' -#' @return Estimated incidence rate, `rate`, and associated confidence interval, `rate_ci`. -#' -#' @seealso [incidence_rate] -#' -#' @name h_incidence_rate -NULL - -#' @describeIn h_incidence_rate Helper function to estimate the incidence rate and -#' associated confidence interval based on the normal approximation for the -#' incidence rate. Unit is one person-year. -#' -#' @examples -#' h_incidence_rate_normal(200, 2) -#' -#' @export -h_incidence_rate_normal <- function(person_years, - n_events, - alpha = 0.05) { - checkmate::assert_number(person_years) - checkmate::assert_number(n_events) - assert_proportion_value(alpha) - - est <- n_events / person_years - se <- sqrt(est / person_years) - ci <- est + c(-1, 1) * stats::qnorm(1 - alpha / 2) * se - - list(rate = est, rate_ci = ci) -} - -#' @describeIn h_incidence_rate Helper function to estimate the incidence rate and -#' associated confidence interval based on the normal approximation for the -#' logarithm of the incidence rate. Unit is one person-year. -#' -#' @examples -#' h_incidence_rate_normal_log(200, 2) -#' -#' @export -h_incidence_rate_normal_log <- function(person_years, - n_events, - alpha = 0.05) { - checkmate::assert_number(person_years) - checkmate::assert_number(n_events) - assert_proportion_value(alpha) - - rate_est <- n_events / person_years - rate_se <- sqrt(rate_est / person_years) - lrate_est <- log(rate_est) - lrate_se <- rate_se / rate_est - ci <- exp(lrate_est + c(-1, 1) * stats::qnorm(1 - alpha / 2) * lrate_se) - - list(rate = rate_est, rate_ci = ci) -} - -#' @describeIn h_incidence_rate Helper function to estimate the incidence rate and -#' associated exact confidence interval. Unit is one person-year. -#' -#' @examples -#' h_incidence_rate_exact(200, 2) -#' -#' @export -h_incidence_rate_exact <- function(person_years, - n_events, - alpha = 0.05) { - checkmate::assert_number(person_years) - checkmate::assert_number(n_events) - assert_proportion_value(alpha) - - est <- n_events / person_years - lcl <- stats::qchisq(p = (alpha) / 2, df = 2 * n_events) / (2 * person_years) - ucl <- stats::qchisq(p = 1 - (alpha) / 2, df = 2 * n_events + 2) / (2 * person_years) - - list(rate = est, rate_ci = c(lcl, ucl)) -} - -#' @describeIn h_incidence_rate Helper function to estimate the incidence rate and -#' associated Byar's confidence interval. Unit is one person-year. -#' -#' @examples -#' h_incidence_rate_byar(200, 2) -#' -#' @export -h_incidence_rate_byar <- function(person_years, - n_events, - alpha = 0.05) { - checkmate::assert_number(person_years) - checkmate::assert_number(n_events) - assert_proportion_value(alpha) - - est <- n_events / person_years - seg_1 <- n_events + 0.5 - seg_2 <- 1 - 1 / (9 * (n_events + 0.5)) - seg_3 <- stats::qnorm(1 - alpha / 2) * sqrt(1 / (n_events + 0.5)) / 3 - lcl <- seg_1 * ((seg_2 - seg_3)^3) / person_years - ucl <- seg_1 * ((seg_2 + seg_3) ^ 3) / person_years # styler: off - - list(rate = est, rate_ci = c(lcl, ucl)) -} - -#' @describeIn h_incidence_rate Helper function to estimate the incidence rate and -#' associated confidence interval. -#' -#' @keywords internal -h_incidence_rate <- function(person_years, - n_events, - control = control_incidence_rate()) { - alpha <- 1 - control$conf_level - est <- switch(control$conf_type, - normal = h_incidence_rate_normal(person_years, n_events, alpha), - normal_log = h_incidence_rate_normal_log(person_years, n_events, alpha), - exact = h_incidence_rate_exact(person_years, n_events, alpha), - byar = h_incidence_rate_byar(person_years, n_events, alpha) - ) - - num_pt_year <- control$num_pt_year - list( - rate = est$rate * num_pt_year, - rate_ci = est$rate_ci * num_pt_year - ) + if (!summarize) { + analyze( + lyt, + vars, + show_labels = show_labels, + table_names = table_names, + afun = a_incidence_rate, + na_str = na_str, + nested = nested, + extra_args = extra_args + ) + } else { + summarize_row_groups( + lyt, + vars, + cfun = a_incidence_rate, + na_str = na_str, + extra_args = extra_args + ) + } } diff --git a/R/riskdiff.R b/R/riskdiff.R index 73e0856542..d52f6020a6 100644 --- a/R/riskdiff.R +++ b/R/riskdiff.R @@ -170,8 +170,8 @@ afun_riskdiff <- function(df, #' #' @inheritParams add_riskdiff #' @param format (`string` or `function`)\cr the format label (string) or formatting function to apply to the risk -#' difference statistic. See the `3d` string options in [list_valid_format_labels()] for possible format strings. -#' Defaults to `"xx.x (xx.x - xx.x)"`. +#' difference statistic. See the `3d` string options in [formatters::list_valid_format_labels()] for possible format +#' strings. Defaults to `"xx.x (xx.x - xx.x)"`. #' #' @return A `list` of items with names corresponding to the arguments. #' diff --git a/R/summarize_colvars.R b/R/summarize_colvars.R index 581539a8bc..3c4f182d38 100644 --- a/R/summarize_colvars.R +++ b/R/summarize_colvars.R @@ -4,7 +4,7 @@ #' #' The analyze function [summarize_colvars()] uses the statistics function [s_summary()] to analyze variables that are #' arranged in columns. The variables to analyze should be specified in the table layout via column splits (see -#' [split_cols_by()] and [split_cols_by_multivar()]) prior to using [summarize_colvars()]. +#' [rtables::split_cols_by()] and [rtables::split_cols_by_multivar()]) prior to using [summarize_colvars()]. #' #' The function is a minimal wrapper for [rtables::analyze_colvars()], a function typically used to apply different #' analysis methods in rows for each column variable. To use the analysis methods as column labels, please refer to diff --git a/R/utils_default_stats_formats_labels.R b/R/utils_default_stats_formats_labels.R index 5cec08d0b8..5524ec2445 100644 --- a/R/utils_default_stats_formats_labels.R +++ b/R/utils_default_stats_formats_labels.R @@ -382,7 +382,7 @@ tern_default_stats <- list( abnormal_by_worst_grade = c("count_fraction", "count_fraction_fixed_dp"), abnormal_by_worst_grade_worsen = c("fraction"), analyze_patients_exposure_in_cols = c("n_patients", "sum_exposure"), - analyze_vars_counts = c("n", "count", "count_fraction", "count_fraction_fixed_dp", "n_blq"), + analyze_vars_counts = c("n", "count", "count_fraction", "count_fraction_fixed_dp", "fraction", "n_blq"), analyze_vars_numeric = c( "n", "sum", "mean", "sd", "se", "mean_sd", "mean_se", "mean_ci", "mean_sei", "mean_sdi", "mean_pval", "median", "mad", "median_ci", "quantiles", "iqr", "range", "min", "max", "median_range", "cv", @@ -397,7 +397,7 @@ tern_default_stats <- list( count_patients_with_flags = c("n", "count", "count_fraction", "count_fraction_fixed_dp", "n_blq"), count_values = c("n", "count", "count_fraction", "count_fraction_fixed_dp", "n_blq"), coxph_pairwise = c("pvalue", "hr", "hr_ci", "n_tot", "n_tot_events"), - estimate_incidence_rate = c("person_years", "n_events", "rate", "rate_ci", "n_rate"), + estimate_incidence_rate = c("person_years", "n_events", "rate", "rate_ci", "n_unique", "n_rate"), estimate_multinomial_response = c("n_prop", "prop_ci"), estimate_odds_ratio = c("or_ci", "n_tot"), estimate_proportion = c("n_prop", "prop_ci"), diff --git a/man/control_riskdiff.Rd b/man/control_riskdiff.Rd index 4466050527..3d9f4cecf9 100644 --- a/man/control_riskdiff.Rd +++ b/man/control_riskdiff.Rd @@ -19,8 +19,8 @@ control_riskdiff( calculations. A new column will be added for each value of \code{arm_y}.} \item{format}{(\code{string} or \code{function})\cr the format label (string) or formatting function to apply to the risk -difference statistic. See the \verb{3d} string options in \code{\link[=list_valid_format_labels]{list_valid_format_labels()}} for possible format strings. -Defaults to \code{"xx.x (xx.x - xx.x)"}.} +difference statistic. See the \verb{3d} string options in \code{\link[formatters:list_formats]{formatters::list_valid_format_labels()}} for possible format +strings. Defaults to \code{"xx.x (xx.x - xx.x)"}.} \item{col_label}{(\code{character})\cr labels to use when rendering the risk difference column within the table. If more than one comparison arm is specified in \code{arm_y}, default labels will specify which two arms are diff --git a/man/h_incidence_rate.Rd b/man/h_incidence_rate.Rd index b1689f54c0..292c42dfdd 100644 --- a/man/h_incidence_rate.Rd +++ b/man/h_incidence_rate.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/incidence_rate.R +% Please edit documentation in R/h_incidence_rate.R \name{h_incidence_rate} \alias{h_incidence_rate} \alias{h_incidence_rate_normal} @@ -8,6 +8,8 @@ \alias{h_incidence_rate_byar} \title{Helper functions for incidence rate} \usage{ +h_incidence_rate(person_years, n_events, control = control_incidence_rate()) + h_incidence_rate_normal(person_years, n_events, alpha = 0.05) h_incidence_rate_normal_log(person_years, n_events, alpha = 0.05) @@ -15,26 +17,24 @@ h_incidence_rate_normal_log(person_years, n_events, alpha = 0.05) h_incidence_rate_exact(person_years, n_events, alpha = 0.05) h_incidence_rate_byar(person_years, n_events, alpha = 0.05) - -h_incidence_rate(person_years, n_events, control = control_incidence_rate()) } \arguments{ \item{person_years}{(\code{numeric(1)})\cr total person-years at risk.} \item{n_events}{(\code{integer(1)})\cr number of events observed.} -\item{alpha}{(\code{numeric(1)})\cr two-sided alpha-level for confidence interval.} - \item{control}{(\code{list})\cr parameters for estimation details, specified by using the helper function \code{\link[=control_incidence_rate]{control_incidence_rate()}}. Possible parameter options are: \itemize{ -\item \code{conf_level} (\code{proportion})\cr confidence level for the estimated incidence rate. -\item \code{conf_type} (\code{string})\cr \code{normal} (default), \code{normal_log}, \code{exact}, or \code{byar} +\item \code{conf_level}: (\code{proportion})\cr confidence level for the estimated incidence rate. +\item \code{conf_type}: (\code{string})\cr \code{normal} (default), \code{normal_log}, \code{exact}, or \code{byar} for confidence interval type. -\item \code{input_time_unit} (\code{string})\cr \code{day}, \code{week}, \code{month}, or \code{year} (default) +\item \code{input_time_unit}: (\code{string})\cr \code{day}, \code{week}, \code{month}, or \code{year} (default) indicating time unit for data input. -\item \code{num_pt_year} (\code{numeric})\cr time unit for desired output (in person-years). +\item \code{num_pt_year}: (\code{numeric})\cr time unit for desired output (in person-years). }} + +\item{alpha}{(\code{numeric(1)})\cr two-sided alpha-level for confidence interval.} } \value{ Estimated incidence rate, \code{rate}, and associated confidence interval, \code{rate_ci}. @@ -44,6 +44,9 @@ Estimated incidence rate, \code{rate}, and associated confidence interval, \code } \section{Functions}{ \itemize{ +\item \code{h_incidence_rate()}: Helper function to estimate the incidence rate and +associated confidence interval. + \item \code{h_incidence_rate_normal()}: Helper function to estimate the incidence rate and associated confidence interval based on the normal approximation for the incidence rate. Unit is one person-year. @@ -58,9 +61,6 @@ associated exact confidence interval. Unit is one person-year. \item \code{h_incidence_rate_byar()}: Helper function to estimate the incidence rate and associated Byar's confidence interval. Unit is one person-year. -\item \code{h_incidence_rate()}: Helper function to estimate the incidence rate and -associated confidence interval. - }} \examples{ h_incidence_rate_normal(200, 2) diff --git a/man/incidence_rate.Rd b/man/incidence_rate.Rd index 721023a5f8..0d35aa7a13 100644 --- a/man/incidence_rate.Rd +++ b/man/incidence_rate.Rd @@ -11,9 +11,12 @@ estimate_incidence_rate( lyt, vars, n_events, + id_var = "USUBJID", control = control_incidence_rate(), na_str = default_na_str(), nested = TRUE, + summarize = FALSE, + label_fmt = "\%s - \%.labels", ..., show_labels = "hidden", table_names = vars, @@ -27,16 +30,26 @@ s_incidence_rate( df, .var, n_events, - is_event, + is_event = lifecycle::deprecated(), + id_var = "USUBJID", control = control_incidence_rate() ) a_incidence_rate( df, + labelstr = "", .var, + .df_row, n_events, - is_event, - control = control_incidence_rate() + id_var = "USUBJID", + control = control_incidence_rate(), + .stats = NULL, + .formats = c(person_years = "xx.x", n_events = "xx", rate = "xx.xx", rate_ci = + "(xx.xx, xx.xx)", n_unique = "xx", n_rate = "xx (xx.x)"), + .labels = NULL, + .indent_mods = NULL, + na_str = default_na_str(), + label_fmt = "\%s - \%.labels" ) } \arguments{ @@ -44,7 +57,10 @@ a_incidence_rate( \item{vars}{(\code{character})\cr variable names for the primary analysis variable to be iterated over.} -\item{n_events}{(\code{integer(1)})\cr number of events observed.} +\item{n_events}{(\code{string})\cr name of integer variable indicating whether an event has been observed (1) or not (0).} + +\item{id_var}{(\code{string})\cr name of variable used as patient identifier if \code{"n_unique"} is included in \code{.stats}. +Defaults to \code{"USUBJID"}.} \item{control}{(\code{list})\cr parameters for estimation details, specified by using the helper function \code{\link[=control_incidence_rate]{control_incidence_rate()}}. Possible parameter options are: @@ -63,6 +79,13 @@ indicating time unit for data input. possible (\code{TRUE}, the default) or as a new top-level element (\code{FALSE}). Ignored if it would nest a split. underneath analyses, which is not allowed.} +\item{summarize}{(\code{flag})\cr whether the function should act as an analyze function (\code{summarize = FALSE}), or a +summarize function (\code{summarize = TRUE}). Defaults to \code{FALSE}.} + +\item{label_fmt}{(\code{string})\cr how labels should be formatted after a row split occurs if \code{summarize = TRUE}. The +string should use \code{"\%s"} to represent row split levels, and \code{"\%.labels"} to represent labels supplied to the +\code{.labels} argument. Defaults to \code{"\%s - \%.labels"}.} + \item{...}{additional arguments for the lower level functions.} \item{show_labels}{(\code{string})\cr label visibility: one of "default", "visible" and "hidden".} @@ -87,6 +110,12 @@ unmodified default behavior. Can be negative.} by a statistics function.} \item{is_event}{(\code{flag})\cr \code{TRUE} if event, \code{FALSE} if time to event is censored.} + +\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{ @@ -102,6 +131,7 @@ the statistics from \code{s_incidence_rate()} to the table layout. \item \code{n_events}: Total number of events observed. \item \code{rate}: Estimated incidence rate. \item \code{rate_ci}: Confidence interval for the incidence rate. +\item \code{n_unique}: Total number of patients with at least one event observed. \item \code{n_rate}: Total number of events observed & estimated incidence rate. } } @@ -126,25 +156,21 @@ and additional format arguments. This function is a wrapper for \code{\link[rtab \item \code{s_incidence_rate()}: Statistics function which estimates the incidence rate and the associated confidence interval. -\item \code{a_incidence_rate()}: Formatted analysis function which is used as \code{afun} -in \code{estimate_incidence_rate()}. +\item \code{a_incidence_rate()}: Formatted analysis function which is used as \code{afun} in \code{estimate_incidence_rate()}. }} \examples{ -library(dplyr) - df <- data.frame( USUBJID = as.character(seq(6)), CNSR = c(0, 1, 1, 0, 0, 0), AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), - ARM = factor(c("A", "A", "A", "B", "B", "B")) -) \%>\% - mutate(is_event = CNSR == 0) \%>\% - mutate(n_events = as.integer(is_event)) + ARM = factor(c("A", "A", "A", "B", "B", "B")), + STRATA1 = factor(c("X", "Y", "Y", "X", "X", "Y")) +) +df$n_events <- 1 - df$CNSR -basic_table() \%>\% +basic_table(show_colcounts = TRUE) \%>\% split_cols_by("ARM") \%>\% - add_colcounts() \%>\% estimate_incidence_rate( vars = "AVAL", n_events = "n_events", @@ -155,6 +181,26 @@ basic_table() \%>\% ) \%>\% build_table(df) +# summarize = TRUE +basic_table(show_colcounts = TRUE) \%>\% + split_cols_by("ARM") \%>\% + split_rows_by("STRATA1", child_labels = "visible") \%>\% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events", + .stats = c("n_unique", "n_rate"), + summarize = TRUE, + label_fmt = "\%.labels" + ) \%>\% + build_table(df) + +a_incidence_rate( + df, + .var = "AVAL", + .df_row = df, + n_events = "n_events" +) + } \seealso{ \code{\link[=control_incidence_rate]{control_incidence_rate()}} and helper functions \link{h_incidence_rate}. diff --git a/man/summarize_colvars.Rd b/man/summarize_colvars.Rd index 600c46583c..704cb0f52a 100644 --- a/man/summarize_colvars.Rd +++ b/man/summarize_colvars.Rd @@ -42,7 +42,7 @@ in columns, and add it to the table layout. The analyze function \code{\link[=summarize_colvars]{summarize_colvars()}} uses the statistics function \code{\link[=s_summary]{s_summary()}} to analyze variables that are arranged in columns. The variables to analyze should be specified in the table layout via column splits (see -\code{\link[=split_cols_by]{split_cols_by()}} and \code{\link[=split_cols_by_multivar]{split_cols_by_multivar()}}) prior to using \code{\link[=summarize_colvars]{summarize_colvars()}}. +\code{\link[rtables:split_cols_by]{rtables::split_cols_by()}} and \code{\link[rtables:split_cols_by_multivar]{rtables::split_cols_by_multivar()}}) prior to using \code{\link[=summarize_colvars]{summarize_colvars()}}. The function is a minimal wrapper for \code{\link[rtables:analyze_colvars]{rtables::analyze_colvars()}}, a function typically used to apply different analysis methods in rows for each column variable. To use the analysis methods as column labels, please refer to diff --git a/tests/testthat/_snaps/analyze_variables.md b/tests/testthat/_snaps/analyze_variables.md index b2a9dbb9a5..65f42acee6 100644 --- a/tests/testthat/_snaps/analyze_variables.md +++ b/tests/testthat/_snaps/analyze_variables.md @@ -628,6 +628,20 @@ [1] 4.0000000 0.4444444 + $fraction + $fraction$Female + num denom + 2 9 + + $fraction$Male + num denom + 3 9 + + $fraction$Unknown + num denom + 4 9 + + $n_blq [1] 0 @@ -668,6 +682,24 @@ [1] 1.0000000 0.1428571 + $fraction + $fraction$Female + num denom + 2 7 + + $fraction$Male + num denom + 2 7 + + $fraction$Unknown + num denom + 2 7 + + $fraction$`NA` + num denom + 1 7 + + $n_blq [1] 0 @@ -702,6 +734,20 @@ [1] 4.0000000 0.4444444 + $fraction + $fraction$Female + num denom + 2 9 + + $fraction$Male + num denom + 3 9 + + $fraction$Unknown + num denom + 4 9 + + $n_blq [1] 0 @@ -736,6 +782,20 @@ [1] 0 0 + $fraction + $fraction$a + num denom + 0 0 + + $fraction$b + num denom + 0 0 + + $fraction$c + num denom + 0 0 + + $n_blq [1] 0 @@ -770,6 +830,20 @@ [1] 4.0 0.2 + $fraction + $fraction$Female + num denom + 2 20 + + $fraction$Male + num denom + 3 20 + + $fraction$Unknown + num denom + 4 20 + + $n_blq [1] 0 @@ -804,6 +878,20 @@ [1] 4.0000000 0.1333333 + $fraction + $fraction$Female + num denom + 2 30 + + $fraction$Male + num denom + 3 30 + + $fraction$Unknown + num denom + 4 30 + + $n_blq [1] 0 @@ -844,6 +932,24 @@ [1] 1.0 0.1 + $fraction + $fraction$Female + num denom + 2 10 + + $fraction$Male + num denom + 3 10 + + $fraction$Unknown + num denom + 4 10 + + $fraction$`NA` + num denom + 1 10 + + $n_blq [1] 0 @@ -971,7 +1077,10 @@ 8 a 3 (60.0%) 0 a 9 b 1 (20.0%) 0 b 10 c 1 (20.0%) 0 c - 11 n_blq 0 0 n_blq + 11 a 3/5 (60.0%) 0 a + 12 b 1/5 (20.0%) 0 b + 13 c 1/5 (20.0%) 0 c + 14 n_blq 0 0 n_blq --- @@ -991,7 +1100,10 @@ 8 A 2 (50.0%) 0 A 9 B 1 (25.0%) 0 B 10 C 1 (25.0%) 0 C - 11 n_blq 0 0 n_blq + 11 A 2/4 (50.0%) 0 A + 12 B 1/4 (25.0%) 0 B + 13 C 1/4 (25.0%) 0 C + 14 n_blq 0 0 n_blq --- @@ -1005,7 +1117,8 @@ 2 count 3 0 count 3 count_fraction 3 (60%) 0 count_fraction 4 count_fraction 3 (60.0%) 0 count_fraction - 5 n_blq 0 0 n_blq + 5 fraction 0 fraction + 6 n_blq 0 0 n_blq # a_summary works with custom input. @@ -1039,7 +1152,11 @@ 11 b 1 (20.0%) 0 b 12 c 1 (20.0%) 0 c 13 NA 1 (20.0%) 0 NA - 14 n_blq 0 0 n_blq + 14 a 2/5 (40.0%) 0 a + 15 b 1/5 (20.0%) 0 b + 16 c 1/5 (20.0%) 0 c + 17 NA 1/5 (20.0%) 0 NA + 18 n_blq 0 0 n_blq # a_summary works with healthy input when compare = TRUE. @@ -1093,8 +1210,11 @@ 8 a 3 (60.0%) 0 a 9 b 1 (20.0%) 0 b 10 c 1 (20.0%) 0 c - 11 n_blq 0 0 n_blq - 12 p-value (chi-squared test) 0.9560 0 p-value (chi-squared test) + 11 a 3/5 (60.0%) 0 a + 12 b 1/5 (20.0%) 0 b + 13 c 1/5 (20.0%) 0 c + 14 n_blq 0 0 n_blq + 15 p-value (chi-squared test) 0.9560 0 p-value (chi-squared test) --- @@ -1114,8 +1234,11 @@ 8 A 2 (50.0%) 0 A 9 B 1 (25.0%) 0 B 10 C 1 (25.0%) 0 C - 11 n_blq 0 0 n_blq - 12 p-value (chi-squared test) 0.9074 0 p-value (chi-squared test) + 11 A 2/4 (50.0%) 0 A + 12 B 1/4 (25.0%) 0 B + 13 C 1/4 (25.0%) 0 C + 14 n_blq 0 0 n_blq + 15 p-value (chi-squared test) 0.9074 0 p-value (chi-squared test) --- @@ -1129,8 +1252,9 @@ 2 count 3 0 count 3 count_fraction 3 (60%) 0 count_fraction 4 count_fraction 3 (60.0%) 0 count_fraction - 5 n_blq 0 0 n_blq - 6 p-value (chi-squared test) 0.8091 0 p-value (chi-squared test) + 5 fraction 0 fraction + 6 n_blq 0 0 n_blq + 7 p-value (chi-squared test) 0.8091 0 p-value (chi-squared test) # a_summary works with custom input when compare = TRUE. @@ -1164,8 +1288,12 @@ 11 b 1 (20.0%) 0 b 12 c 1 (20.0%) 0 c 13 NA 1 (20.0%) 0 NA - 14 n_blq 0 0 n_blq - 15 p-value (chi-squared test) 0.8254 0 p-value (chi-squared test) + 14 a 2/5 (40.0%) 0 a + 15 b 1/5 (20.0%) 0 b + 16 c 1/5 (20.0%) 0 c + 17 NA 1/5 (20.0%) 0 NA + 18 n_blq 0 0 n_blq + 19 p-value (chi-squared test) 0.8254 0 p-value (chi-squared test) # `analyze_vars` works with healthy input, default `na.rm = TRUE`. diff --git a/tests/testthat/_snaps/compare_variables.md b/tests/testthat/_snaps/compare_variables.md index eaf443e564..493db5697c 100644 --- a/tests/testthat/_snaps/compare_variables.md +++ b/tests/testthat/_snaps/compare_variables.md @@ -21,8 +21,8 @@ Code res Output - [1] "n" "count" "count_fraction" "n_blq" - [5] "pval_counts" + [1] "n" "count" "count_fraction" "fraction" + [5] "n_blq" "pval_counts" --- @@ -75,6 +75,20 @@ [1] 1.0 0.2 + $fraction + $fraction$a + num denom + 3 5 + + $fraction$b + num denom + 1 5 + + $fraction$c + num denom + 1 5 + + $n_blq [1] 0 diff --git a/tests/testthat/_snaps/h_incidence_rate.md b/tests/testthat/_snaps/h_incidence_rate.md new file mode 100644 index 0000000000..4ab6826d47 --- /dev/null +++ b/tests/testthat/_snaps/h_incidence_rate.md @@ -0,0 +1,60 @@ +# 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 + + diff --git a/tests/testthat/_snaps/incidence_rate.md b/tests/testthat/_snaps/incidence_rate.md new file mode 100644 index 0000000000..44854523b7 --- /dev/null +++ b/tests/testthat/_snaps/incidence_rate.md @@ -0,0 +1,149 @@ +# 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 + + +# 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_unique + [1] 4 + attr(,"label") + [1] "Total number of patients with at least one adverse event" + + $n_rate + [1] 4.00000 44.15823 + attr(,"label") + [1] "Number of adverse events observed (AE rate per 100 patient-years)" + + +# a_incidence_rate works with default arguments + + Code + res + Output + RowsVerticalSection (in_rows) object print method: + ---------------------------- + row_name formatted_cell indent_mod + 1 person_years 108.7 0 + 2 n_events 4 0 + 3 rate 3.68 0 + 4 rate_ci (0.07, 7.29) 0 + 5 n_unique 4 0 + 6 n_rate 4 (3.7) 0 + row_label + 1 Total patient-years at risk + 2 Number of adverse events observed + 3 AE rate per 100 patient-years + 4 95% CI + 5 Total number of patients with at least one adverse event + 6 Number of adverse events observed (AE rate per 100 patient-years) + +# a_incidence_rate works with customized arguments + + Code + res + Output + RowsVerticalSection (in_rows) object print method: + ---------------------------- + row_name formatted_cell indent_mod + 1 n_rate 4.00 (44.16) 3 + 2 n_unique 4.00 0 + row_label + 1 Total number of applicable adverse events (rate) + 2 Total number of patients with at least one adverse event + +# estimate_incidence_rate works as expected with default input + + Code + res + Output + A B + (N=3) (N=3) + β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + Total patient-years at risk 45.8 62.9 + Number of adverse events observed 1 3 + AE rate per 100 patient-years 2.18 4.77 + 95% CI (-2.10, 6.46) (-0.63, 10.17) + +# estimate_incidence_rate works as expected with custom input + + Code + res + Output + A B + (N=3) (N=3) + β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + Total number of applicable adverse events (rate) 1.00 (26.20) 3.00 (57.23) + Total number of patients with at least one adverse event 1.00 3.00 + +# estimate_incidence_rate works with default arguments with summarize = TRUE + + Code + res + Output + A B + (N=3) (N=3) + β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + X - Total patient-years at risk 10.1 39.5 + X - Number of adverse events observed 1 2 + X - AE rate per 100 patient-years 9.90 5.06 + X - 95% CI (-9.50, 29.31) (-1.95, 12.08) + Y - Total patient-years at risk 35.7 23.4 + Y - Number of adverse events observed 0 1 + Y - AE rate per 100 patient-years 0.00 4.27 + Y - 95% CI (0.00, 0.00) (-4.10, 12.65) + +# estimate_incidence_rate works with custom arguments with summarize = TRUE + + Code + res + Output + A B + (N=3) (N=3) + β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + Total patient-years at risk 45.8 62.9 + Number of adverse events observed 1 3 + AE rate per 100 patient-years 2.18 4.77 + X + Total number of patients with at least one adverse event 1 2 + Number of adverse events observed (AE rate per 100 patient-years) 1 (9.9) 2 (5.1) + Y + Total number of patients with at least one adverse event 0 1 + Number of adverse events observed (AE rate per 100 patient-years) 0 (0.0) 1 (4.3) + diff --git a/tests/testthat/_snaps/utils_default_stats_formats_labels.md b/tests/testthat/_snaps/utils_default_stats_formats_labels.md index fb13d7d35c..774a14a31d 100644 --- a/tests/testthat/_snaps/utils_default_stats_formats_labels.md +++ b/tests/testthat/_snaps/utils_default_stats_formats_labels.md @@ -20,7 +20,7 @@ Output [1] "n" "count" [3] "count_fraction" "count_fraction_fixed_dp" - [5] "n_blq" + [5] "fraction" "n_blq" --- @@ -138,6 +138,8 @@ "n" "count" count_fraction count_fraction_fixed_dp "count_fraction" "count_fraction" - n_blq pval_counts - "n_blq" "p-value (chi-squared test)" + fraction n_blq + "fraction" "n_blq" + pval_counts + "p-value (chi-squared test)" diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 3a960b45ca..712205d9bb 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -1,6 +1,5 @@ # Extra libraries (suggested) for tests library(dplyr) -library(nestcolor) # skip_if_too_deep skip_if_too_deep <- function(depth) { diff --git a/tests/testthat/test-estimate_incidence_rate.R b/tests/testthat/test-estimate_incidence_rate.R deleted file mode 100644 index 2befdfa96f..0000000000 --- a/tests/testthat/test-estimate_incidence_rate.R +++ /dev/null @@ -1,135 +0,0 @@ -testthat::test_that("control_incidence_rate works with customized parameters", { - result <- control_incidence_rate( - conf_level = 0.9, - conf_type = "exact", - input_time_unit = "month", - num_pt_year = 100 - ) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("control_incidence_rate fails with wrong input", { - testthat::expect_error(control_incidence_rate(conf_level = 1.1)) - testthat::expect_error(control_incidence_rate(conf_type = "wald")) - testthat::expect_error(control_incidence_rate(input_time_unit = "decade")) - testthat::expect_error(control_incidence_rate(num_pt_year = "one")) -}) - -testthat::test_that("h_incidence_rate_normal works as expected with healthy input", { - result <- h_incidence_rate_normal(200, 2, 0.1) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("h_incidence_rate_normal_log works as expected with healthy input", { - result <- h_incidence_rate_normal_log(200, 2, 0.1) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("h_incidence_rate_exact works as expected with healthy input", { - result <- h_incidence_rate_exact(200, 2, 0.1) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("h_incidence_rate_byar works as expected with healthy input", { - result <- h_incidence_rate_byar(200, 2, 0.1) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("h_incidence_rate works as expected with healthy input", { - result <- h_incidence_rate( - 200, - 2, - control_incidence_rate(conf_level = 0.9, conf_type = "normal_log", num_pt_year = 100) - ) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("s_incidence_rate works as expected with healthy input", { - df <- data.frame( - USUBJID = as.character(seq(6)), - CNSR = c(0, 1, 1, 0, 0, 0), - AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), - ARM = factor(c("A", "A", "A", "B", "B", "B")) - ) %>% - dplyr::mutate(is_event = CNSR == 0) %>% - dplyr::mutate(n_events = as.integer(is_event)) - result <- s_incidence_rate( - df, - .var = "AVAL", - n_events = "n_events", - control = control_incidence_rate( - conf_level = 0.9, - conf_type = "normal_log", - input_time_unit = "month", - num_pt_year = 100 - ) - ) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("estimate_incidence_rate works as expected with healthy input", { - df <- data.frame( - USUBJID = as.character(seq(6)), - CNSR = c(0, 1, 1, 0, 0, 0), - AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), - ARM = factor(c("A", "A", "A", "B", "B", "B")) - ) %>% - dplyr::mutate(is_event = CNSR == 0) %>% - dplyr::mutate(n_events = as.integer(is_event)) - - result <- basic_table() %>% - split_cols_by("ARM") %>% - add_colcounts() %>% - estimate_incidence_rate( - vars = "AVAL", - n_events = "n_events", - control = control_incidence_rate( - conf_level = 0.9, - conf_type = "normal_log", - input_time_unit = "month", - num_pt_year = 100 - ) - ) %>% - build_table(df) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) - -testthat::test_that("estimate_incidence_rate `n_rate` statistic works as expected", { - df <- data.frame( - USUBJID = as.character(seq(6)), - CNSR = c(0, 1, 1, 0, 0, 0), - AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), - ARM = factor(c("A", "A", "A", "B", "B", "B")) - ) %>% - dplyr::mutate(is_event = CNSR == 0) %>% - dplyr::mutate(n_events = as.integer(is_event)) - - result <- basic_table() %>% - split_cols_by("ARM") %>% - add_colcounts() %>% - estimate_incidence_rate( - vars = "AVAL", - n_events = "n_events", - .stats = c("n_events", "rate", "n_rate") - ) %>% - build_table(df) - - res <- testthat::expect_silent(result) - testthat::expect_snapshot(res) -}) diff --git a/tests/testthat/test-h_incidence_rate.R b/tests/testthat/test-h_incidence_rate.R new file mode 100644 index 0000000000..6b4603572c --- /dev/null +++ b/tests/testthat/test-h_incidence_rate.R @@ -0,0 +1,38 @@ +testthat::test_that("h_incidence_rate_normal works as expected with healthy input", { + result <- h_incidence_rate_normal(200, 2, 0.1) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("h_incidence_rate_normal_log works as expected with healthy input", { + result <- h_incidence_rate_normal_log(200, 2, 0.1) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("h_incidence_rate_exact works as expected with healthy input", { + result <- h_incidence_rate_exact(200, 2, 0.1) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("h_incidence_rate_byar works as expected with healthy input", { + result <- h_incidence_rate_byar(200, 2, 0.1) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("h_incidence_rate works as expected with healthy input", { + result <- h_incidence_rate( + 200, + 2, + control_incidence_rate(conf_level = 0.9, conf_type = "normal_log", num_pt_year = 100) + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-incidence_rate.R b/tests/testthat/test-incidence_rate.R new file mode 100644 index 0000000000..045531971c --- /dev/null +++ b/tests/testthat/test-incidence_rate.R @@ -0,0 +1,151 @@ +df <- data.frame( + USUBJID = as.character(seq(6)), + CNSR = c(0, 1, 1, 0, 0, 0), + AVAL = c(10.1, 20.4, 15.3, 20.8, 18.7, 23.4), + ARM = factor(c("A", "A", "A", "B", "B", "B")), + STRATA1 = factor(c("X", "Y", "Y", "X", "X", "Y")) +) %>% + dplyr::mutate(n_events = 1 - CNSR) + +testthat::test_that("control_incidence_rate works with customized parameters", { + result <- control_incidence_rate( + conf_level = 0.9, + conf_type = "exact", + input_time_unit = "month", + num_pt_year = 100 + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("control_incidence_rate fails with wrong input", { + testthat::expect_error(control_incidence_rate(conf_level = 1.1)) + testthat::expect_error(control_incidence_rate(conf_type = "wald")) + testthat::expect_error(control_incidence_rate(input_time_unit = "decade")) + testthat::expect_error(control_incidence_rate(num_pt_year = "one")) +}) + +testthat::test_that("s_incidence_rate works as expected with healthy input", { + result <- s_incidence_rate( + df, + .var = "AVAL", + n_events = "n_events", + control = control_incidence_rate( + conf_level = 0.9, + conf_type = "normal_log", + input_time_unit = "month", + num_pt_year = 100 + ) + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("a_incidence_rate works with default arguments", { + result <- a_incidence_rate( + df, + .df_row = df, + .var = "AVAL", + n_events = "n_events" + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("a_incidence_rate works with customized arguments", { + result <- a_incidence_rate( + df, + .var = "AVAL", + n_events = "n_events", + control = control_incidence_rate( + conf_level = 0.9, + conf_type = "normal_log", + input_time_unit = "month", + num_pt_year = 100 + ), + .df_row = df, + .stats = c("n_rate", "n_unique"), + .formats = c(n_rate = "xx.xx (xx.xx)", n_unique = "xx.xx"), + .labels = c(n_rate = "Total number of applicable adverse events (rate)"), + .indent_mods = c(n_rate = 3L) + ) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("estimate_incidence_rate works as expected with default input", { + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM") %>% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events" + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("estimate_incidence_rate works as expected with custom input", { + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM") %>% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events", + control = control_incidence_rate( + conf_level = 0.9, + conf_type = "normal_log", + input_time_unit = "month", + num_pt_year = 100 + ), + .stats = c("n_rate", "n_unique"), + .formats = c(n_rate = "xx.xx (xx.xx)", n_unique = "xx.xx"), + .labels = c(n_rate = "Total number of applicable adverse events (rate)"), + .indent_mods = c(n_rate = 3L) + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("estimate_incidence_rate works with default arguments with summarize = TRUE", { + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM") %>% + split_rows_by("STRATA1") %>% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events", + summarize = TRUE + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + +testthat::test_that("estimate_incidence_rate works with custom arguments with summarize = TRUE", { + result <- basic_table(show_colcounts = TRUE) %>% + split_cols_by("ARM") %>% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events", + .stats = c("person_years", "n_events", "rate") + ) %>% + split_rows_by("STRATA1", child_labels = "visible") %>% + estimate_incidence_rate( + vars = "AVAL", + n_events = "n_events", + .stats = c("n_unique", "n_rate"), + summarize = TRUE, + label_fmt = "%.labels" + ) %>% + build_table(df) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-individual_patient_plot.R b/tests/testthat/test-individual_patient_plot.R index 0262fe2865..48fb863a6c 100644 --- a/tests/testthat/test-individual_patient_plot.R +++ b/tests/testthat/test-individual_patient_plot.R @@ -3,6 +3,9 @@ adlb <- tern_ex_adlb %>% slice(1:36) testthat::test_that("h_g_ipp works correctly", { + skip_if_not_installed("nestcolor") + require("nestcolor", quietly = TRUE) + testthat::expect_silent(h_g_ipp( df = adlb, xvar = "AVISIT", diff --git a/vignettes/tables.Rmd b/vignettes/tables.Rmd index 9feba45514..546aedf73d 100644 --- a/vignettes/tables.Rmd +++ b/vignettes/tables.Rmd @@ -27,7 +27,7 @@ knitr::opts_chunk$set( The `tern` R package provides functions to create common analyses from clinical trials in `R`. The core functionality for tabulation is built on the more general purpose `rtables` package. -New users should first begin by reading the ["Introduction to tern"](https://insightsengineering.github.io/tern/main/articles/tern.html) and ["Introduction to `rtables`"](https://insightsengineering.github.io/rtables/latest-tag/articles/introduction.html) vignettes. +New users should first begin by reading the ["Introduction to tern"](https://insightsengineering.github.io/tern/main/articles/tern.html) and ["Introduction to `rtables`"](https://insightsengineering.github.io/rtables/latest-tag/articles/rtables.html) vignettes. The packages used in this vignette are: @@ -54,7 +54,7 @@ The table layout is materialized with the `rtables::build_table` function and th The `tern` analyze functions are wrappers around `rtables::analyze` function, they offer various methods useful from the perspective of clinical trials and other statistical projects. Examples of the `tern` analyze functions are `count_occurrences`, `summarize_ancova` or `analyze_vars`. -As there is no one prefix to identify all `tern` analyze functions it is recommended to use the [the tern website functions reference](https://insightsengineering.github.io/tern/main/reference/index.html). +As there is no one prefix to identify all `tern` analyze functions it is recommended to use the [the tern website functions reference](https://insightsengineering.github.io/tern/latest-tag/reference/index.html). ### Internals of `tern` Analyze Functions diff --git a/vignettes/tern.Rmd b/vignettes/tern.Rmd index 0920bff77d..7cb8958e54 100644 --- a/vignettes/tern.Rmd +++ b/vignettes/tern.Rmd @@ -32,7 +32,7 @@ The `tern` R package contains analytical functions for creating tables and graph The main focus is on the clinical trial reporting tables but the graphs related to the clinical trials are also valuable. The core functionality for tabulation is built on top of the more general purpose `rtables` package. -[**It is strongly recommended that you start by reading the "Introduction to `rtables`" vignette to get familiar with the concept of `rtables`.**](https://insightsengineering.github.io/rtables/latest-tag/articles/introduction.html) +[**It is strongly recommended that you start by reading the "Introduction to `rtables`" vignette to get familiar with the concept of `rtables`.**](https://insightsengineering.github.io/rtables/latest-tag/articles/rtables.html) --------- @@ -69,7 +69,7 @@ data visualizations helper functions: - ... -The reference of `tern` functions is available on [the tern website functions reference](https://insightsengineering.github.io/tern/main/reference/index.html). +The reference of `tern` functions is available on [the tern website functions reference](https://insightsengineering.github.io/tern/latest-tag/reference/index.html). --------- @@ -82,7 +82,7 @@ The table layout is materialized with the `rtables::build_table` function and th The `tern` analytical functions are wrappers around the `rtables::analyze` function; they offer various methods useful from the perspective of clinical trials and other statistical projects. Examples of the `tern` analytical functions are `count_occurrences`, `summarize_ancova` and `analyze_vars`. -As there is no one prefix to identify all `tern` analytical functions it is recommended to use the reference subsection on [the tern website](https://insightsengineering.github.io/tern/main/reference/index.html). +As there is no one prefix to identify all `tern` analytical functions it is recommended to use the reference subsection on [the tern website](https://insightsengineering.github.io/tern/latest-tag/reference/index.html). In the `rtables` code below we first describe the two tables and assign the descriptions to the variables `lyt` and `lyt2`. We then built the tables using the actual data with `rtables::build_table`. The description of a table is called a table **layout**. The **analyze instruction** adds to the layout that the `ARM` variable should be analyzed with the `mean` analysis function and the result should be rounded to 1 decimal place. Hence, a **layout** is β€œpre-data”; that is, it’s a description of **how to build a table once we get data**.