diff --git a/DESCRIPTION b/DESCRIPTION index 72dd769379..674e79f8f0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gtsummary Title: Presentation-Ready Data Summary and Analytic Result Tables -Version: 2.0.4.9016 +Version: 2.0.4.9017 Authors@R: c( person("Daniel D.", "Sjoberg", , "danield.sjoberg@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-0862-2018")), @@ -45,7 +45,7 @@ BugReports: https://github.com/ddsjoberg/gtsummary/issues Depends: R (>= 4.2) Imports: - cards (>= 0.4.0), + cards (>= 0.5.0), cli (>= 3.6.3), dplyr (>= 1.1.3), glue (>= 1.8.0), @@ -60,7 +60,7 @@ Suggests: broom.helpers (>= 1.17.0), broom.mixed (>= 0.2.9), car (>= 3.0-11), - cardx (>= 0.2.2.9021), + cardx (>= 0.2.3), cmprsk, effectsize (>= 0.6.0), emmeans (>= 1.7.3), @@ -86,7 +86,6 @@ Suggests: testthat (>= 3.2.0), withr (>= 2.5.0), workflows (>= 0.2.4) -Remotes: insightsengineering/cardx VignetteBuilder: knitr Config/Needs/check: hms diff --git a/NAMESPACE b/NAMESPACE index 333eaeca83..33a2d81c2c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -104,6 +104,7 @@ export(add_q) export(add_significance_stars) export(add_stat) export(add_stat_label) +export(add_variable_group_header) export(add_vif) export(all_categorical) export(all_continuous) diff --git a/NEWS.md b/NEWS.md index 177a7b789f..0753fc8af2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,20 @@ ### New Features and Functions +* Added function `tbl_strata_nested_stack()`. The function is similar to `tbl_strata()`, but this function nests the resulting tables, indented under each of the strata headers. (#2006) + +* The `add_ci.tbl_summary()` function now works with categorical variables that were summarized using `tbl_summary(percent = c('row', 'cell'))`. (#1929) + +* Adding the `tbl_merge(merge_vars)` argument. This argument allows users to specify any merging columns providing much more flexibility when merging unlike tables. Additionally, columns selected by `cards::all_ard_groups()` have been added to the default merging columns, which provides the functionality for merging the results from `tbl_hierarchical()` and `tbl_hierarchical_count()`. (#1861) + + This does, however, introduce one change in behavior from the previous version of `tbl_merge()`. Previously, merging on a table with the same variable, but with a different label would be reconciled silently in the background and the first label would be used in the final table. While this may have been useful in a few edge cases, it largely was an unintuitive result. This update performs more straightforward merging and the results are more aligned with users' expectations. + +* Added `add_variable_group_header()` function that adds a header row above a group of variables. (#2150) + +### Updates to `modify_*()` Functions + +There were a number of updates to the family of `modify_*()` functions: functions that modify table attributes like headers, footnotes, bold, titles, etc. + * Adding functions `modify_bold()`, `modify_italic()`, `remove_bold()`, and `remove_italic()` for adding or remove bold or italic styling to cells in the table. (#2125) * Updates to the handling of footnotes. Previously, header footnotes were handled with `modify_footnote()` and `modify_table_styling(footnote)`. It was possible to also include footnotes in the table body with `modify_table_styling(footnote)`, but this was largely a hidden feature. Also confusingly, a special abbreviation footnote was handled with `modify_footnote(abbreviation=TRUE)`. @@ -20,16 +34,10 @@ * Added new function `remove_column_merge()` to undo a column merge. (#2130) -* Added function `tbl_strata_nested_stack()`. The function is similar to `tbl_strata()`, but this function nests the resulting tables, indented under each of the strata headers. (#2006) - -* The `add_ci.tbl_summary()` function now works with categorical variables that were summarized using `tbl_summary(percent = c('row', 'cell'))`. (#1929) - -* Adding the `tbl_merge(merge_vars)` argument. This argument allows users to specify any merging columns providing much more flexibility when merging unlike tables. Additionally, columns selected by `cards::all_ard_groups()` have been added to the default merging columns, which provides the functionality for merging the results from `tbl_hierarchical()` and `tbl_hierarchical_count()`. (#1861) - - This does, however, introduce one change in behavior from the previous version of `tbl_merge()`. Previously, merging on a table with the same variable, but with a different label would be reconciled silently in the background and the first label would be used in the final table. While this may have been useful in a few edge cases, it largely was an unintuitive result. This update performs more straightforward merging and the results are more aligned with users' expectations. - * The `modify_caption(caption)` argument now accepts a vector of captions, instead of just a string. Note, however, that not all print engines support a vector of captions. (#2107) +* Added `show_header_names(show_hidden)` argument, which will print both hidden and printed column information. (#2147) + ### Other Updates * Language translations have been updated with a handful of missing translations. (#2100) diff --git a/R/add_variable_group_header.R b/R/add_variable_group_header.R new file mode 100644 index 0000000000..103f6a9e00 --- /dev/null +++ b/R/add_variable_group_header.R @@ -0,0 +1,70 @@ +#' Variable Group Header +#' +#' @description +#' Some data are inherently grouped, and should be reported together. +#' Grouped variables are all indented together. +#' This function indents the variables that should be reported together while +#' adding a header above the group. +#' +#' @inheritParams modify_column_indent +#' @param x (`tbl_summary`)\cr +#' gtsummary object of class `'tbl_summary'` +#' @param header (`string`)\cr +#' string of the header to place above the variable group +#' @param variables ([`tidy-select`][dplyr::dplyr_tidy_select])\cr +#' Variables to group that appear in `x$table_body`. +#' Selected variables should be appear consecutively in table. +#' +#' @returns a gtsummary table +#' @export +#' +#' @details +#' This function works by inserting a row into the `x$table_body` and +#' indenting the group of selected variables. +#' This function cannot be used in conjunction with all functions in gtsummary; +#' for example, `bold_labels()` will bold the incorrect rows after running +#' this function. +#' +#' @examples +#' # Example 1 ---------------------------------- +#' set.seed(11234) +#' data.frame( +#' exclusion_age = sample(c(TRUE, FALSE), 20, replace = TRUE), +#' exclusion_mets = sample(c(TRUE, FALSE), 20, replace = TRUE), +#' exclusion_physician = sample(c(TRUE, FALSE), 20, replace = TRUE) +#' ) |> +#' tbl_summary( +#' label = list(exclusion_age = "Age", +#' exclusion_mets = "Metastatic Disease", +#' exclusion_physician = "Physician") +#' ) |> +#' add_variable_group_header( +#' header = "Exclusion Reason", +#' variables = starts_with("exclusion_") +#' ) |> +#' modify_caption("**Study Exclusion Criteria**") +add_variable_group_header <- function(x, header, variables, indent = 4L) { + # check inputs --------------------------------------------------------------- + set_cli_abort_call() + check_class(x, "tbl_summary") + check_string(header) + check_scalar_integerish(indent) + + # process variables ---------------------------------------------------------- + cards::process_selectors(scope_table_body(x$table_body), variables = {{ variables }}) + # return unaltered table if no variables selected + if (is_empty(variables)) return(x) # styler: off + + # add header above the first variable selected ------------------------------- + df_insert <- dplyr::tibble(row_type = "variable_group", label = header) + # identify row where to insert header + idx <- which(x$table_body$variable %in% variables)[1] + + x |> + modify_table_body(~ dplyr::add_row(.x, df_insert, .before = idx)) |> + .modify_column_indent( + columns = "label", + rows = .data$variable %in% .env$variables, + indent = indent + ) +} diff --git a/R/modify.R b/R/modify.R index 0dc3cb9ece..f6ff5976d4 100644 --- a/R/modify.R +++ b/R/modify.R @@ -28,6 +28,9 @@ #' An integer specifying which level to place the spanning header. #' @param columns ([`tidy-select`][dplyr::dplyr_tidy_select])\cr #' Columns from which to remove spanning headers. +#' @param show_hidden (scalar `logical`)\cr +#' Logical indicating whether to print hidden columns as well as printed columns. +#' Default is `FALSE`. #' @param update,quiet `r lifecycle::badge("deprecated")` #' @param include_example `r lifecycle::badge("deprecated")` #' @@ -225,9 +228,10 @@ remove_spanning_header <- function(x, columns, level = 1L) { #' @name modify #' @export -show_header_names <- function(x, include_example, quiet) { +show_header_names <- function(x, show_hidden = FALSE, include_example, quiet) { # checking input ------------------------------------------------------------- check_class(x, "gtsummary") + check_scalar_logical(show_hidden) # deprecated arguments ------------------------------------------------------- if (!missing(include_example)) { @@ -247,8 +251,11 @@ show_header_names <- function(x, include_example, quiet) { # printing info -------------------------------------------------------------- df_print <- - x$table_styling$header |> - dplyr::filter(!.data$hide) |> + x$table_styling$header %>% + {case_switch( + !show_hidden ~ dplyr::filter(., !.data$hide), + .default = dplyr::mutate(., column = ifelse(.data$hide, paste0(.data$column, "\U2020"), .data$column)) + )} |> dplyr::select("column", "label", starts_with("modify_stat_")) # save column class abbreviations @@ -298,10 +305,16 @@ show_header_names <- function(x, include_example, quiet) { tibble_as_cli(df_print, label = list(column = "Column Name", label = "Header")) + msg <- + c("* These values may be dynamically placed into headers (and other locations).", + "i" = "Review the {.help [{.fun modify_header}](gtsummary::modify_header)} help for examples." + ) + if (show_hidden) { + msg <- append(msg, values = "\U2020 Hidden columns", after = 2) + } + cat("\n") - cli::cli_inform(c("* These values may be dynamically placed into headers (and other locations).", - "i" = "Review the {.help [{.fun modify_header}](gtsummary::modify_header)} help for examples." - )) + cli::cli_inform(message = msg) } .get_class_abbreviation <- function(x) { diff --git a/cran-comments.md b/cran-comments.md index 4262433fd5..8ce9dae56f 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -4,18 +4,16 @@ ## revdepcheck results -We checked 22 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. +We checked 23 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. * We saw 0 new problems * We failed to check 2 packages -Issues with CRAN packages are summarised below. - ### Failed to check -* brms.mmrm (NA) -* equatiomatic (NA) +* brms.mmrm +* equatiomatic ## Additional Comments -Thank you for your time. +Thank you for your time! diff --git a/man/add_variable_group_header.Rd b/man/add_variable_group_header.Rd new file mode 100644 index 0000000000..442a148a11 --- /dev/null +++ b/man/add_variable_group_header.Rd @@ -0,0 +1,57 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_variable_group_header.R +\name{add_variable_group_header} +\alias{add_variable_group_header} +\title{Variable Group Header} +\usage{ +add_variable_group_header(x, header, variables, indent = 4L) +} +\arguments{ +\item{x}{(\code{tbl_summary})\cr +gtsummary object of class \code{'tbl_summary'}} + +\item{header}{(\code{string})\cr +string of the header to place above the variable group} + +\item{variables}{(\code{\link[dplyr:dplyr_tidy_select]{tidy-select}})\cr +Variables to group that appear in \code{x$table_body}. +Selected variables should be appear consecutively in table.} + +\item{indent}{(\code{integer})\cr +An integer indicating how many space to indent text} +} +\value{ +a gtsummary table +} +\description{ +Some data are inherently grouped, and should be reported together. +Grouped variables are all indented together. +This function indents the variables that should be reported together while +adding a header above the group. +} +\details{ +This function works by inserting a row into the \code{x$table_body} and +indenting the group of selected variables. +This function cannot be used in conjunction with all functions in gtsummary; +for example, \code{bold_labels()} will bold the incorrect rows after running +this function. +} +\examples{ +# Example 1 ---------------------------------- +set.seed(11234) +data.frame( + exclusion_age = sample(c(TRUE, FALSE), 20, replace = TRUE), + exclusion_mets = sample(c(TRUE, FALSE), 20, replace = TRUE), + exclusion_physician = sample(c(TRUE, FALSE), 20, replace = TRUE) +) |> + tbl_summary( + label = list(exclusion_age = "Age", + exclusion_mets = "Metastatic Disease", + exclusion_physician = "Physician") + ) |> + add_variable_group_header( + header = "Exclusion Reason", + variables = starts_with("exclusion_") + ) |> + modify_caption("**Study Exclusion Criteria**") +} diff --git a/man/figures/README-tbl_merge_ex1-1.png b/man/figures/README-tbl_merge_ex1-1.png index 0541d7851c..0aef81e146 100644 Binary files a/man/figures/README-tbl_merge_ex1-1.png and b/man/figures/README-tbl_merge_ex1-1.png differ diff --git a/man/figures/README-tbl_regression_printa-1.png b/man/figures/README-tbl_regression_printa-1.png index 65ded49c33..61d2d59419 100644 Binary files a/man/figures/README-tbl_regression_printa-1.png and b/man/figures/README-tbl_regression_printa-1.png differ diff --git a/man/figures/README-tbl_summary_print_extra-1.png b/man/figures/README-tbl_summary_print_extra-1.png index ca40f6dc27..5a601eb0ea 100644 Binary files a/man/figures/README-tbl_summary_print_extra-1.png and b/man/figures/README-tbl_summary_print_extra-1.png differ diff --git a/man/figures/README-tbl_summary_print_simple-1.png b/man/figures/README-tbl_summary_print_simple-1.png index c75a2aa303..78ca34baa5 100644 Binary files a/man/figures/README-tbl_summary_print_simple-1.png and b/man/figures/README-tbl_summary_print_simple-1.png differ diff --git a/man/modify.Rd b/man/modify.Rd index dcbf9d5977..62cb80b4ce 100644 --- a/man/modify.Rd +++ b/man/modify.Rd @@ -21,7 +21,7 @@ modify_spanning_header( remove_spanning_header(x, columns, level = 1L) -show_header_names(x, include_example, quiet) +show_header_names(x, show_hidden = FALSE, include_example, quiet) } \arguments{ \item{x}{(\code{gtsummary})\cr @@ -50,6 +50,10 @@ An integer specifying which level to place the spanning header.} \item{columns}{(\code{\link[dplyr:dplyr_tidy_select]{tidy-select}})\cr Columns from which to remove spanning headers.} +\item{show_hidden}{(scalar \code{logical})\cr +Logical indicating whether to print hidden columns as well as printed columns. +Default is \code{FALSE}.} + \item{include_example}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} } \value{ diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 53bc6ff6f7..bfa5b797f3 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -65,6 +65,7 @@ reference: - add_ci - add_stat_label - add_stat + - add_variable_group_header - separate_p_footnotes - subtitle: Cross Tables - contents: diff --git a/tests/testthat/_snaps/show_header_names.md b/tests/testthat/_snaps/show_header_names.md index 4a9f6bc98c..37b829ec21 100644 --- a/tests/testthat/_snaps/show_header_names.md +++ b/tests/testthat/_snaps/show_header_names.md @@ -12,6 +12,26 @@ * These values may be dynamically placed into headers (and other locations). i Review the `modify_header()` (`?gtsummary::modify_header()`) help for examples. +# show_header_names(show_hidden) + + Code + show_header_names(tbl_summary(trial, include = age, by = trt, missing = "no"), + show_hidden = TRUE) + Output + Column Name Header level* N* n* p* + variable† "variable" 200 + var_type† "var_type" 200 + row_type† "row_type" 200 + var_label† "var_label" 200 + label "**Characteristic**" 200 + stat_1 "**Drug A** \nN = 98" Drug A 200 98 0.490 + stat_2 "**Drug B** \nN = 102" Drug B 200 102 0.510 + + Message + * These values may be dynamically placed into headers (and other locations). + i Review the `modify_header()` (`?gtsummary::modify_header()`) help for examples. + † Hidden columns + # show_header_names() works with tbl_regression Code diff --git a/tests/testthat/test-add_variable_group_header.R b/tests/testthat/test-add_variable_group_header.R new file mode 100644 index 0000000000..ac95354465 --- /dev/null +++ b/tests/testthat/test-add_variable_group_header.R @@ -0,0 +1,19 @@ +test_that("add_variable_group_header() works", { + expect_silent( + tbl <- trial |> + tbl_summary(include = c(age, response, death, marker), missing = "no") |> + add_variable_group_header(header = "the header row", variables = c(response, death)) + ) + + # header row is in correct location + expect_equal( + as_tibble(tbl, col_labels = FALSE)$label[2], + "the header row" + ) + + # grouped variables are indented. + expect_equal( + .table_styling_expr_to_row_number(tbl)$table_styling$indent$row_numbers[[1]], + c(3L, 4L) + ) +}) diff --git a/tests/testthat/test-show_header_names.R b/tests/testthat/test-show_header_names.R index a91c0e5a08..e7d827d5dc 100644 --- a/tests/testthat/test-show_header_names.R +++ b/tests/testthat/test-show_header_names.R @@ -8,6 +8,13 @@ test_that("show_header_names() works with tbl_summary()", { ) }) +test_that("show_header_names(show_hidden)", { + expect_snapshot( + tbl_summary(trial, include = age, by = trt, missing = "no") |> + show_header_names(show_hidden = TRUE) + ) +}) + test_that("show_header_names() works with tbl_regression", { mod_logistic <- glm(response ~ age + stage, trial, family = binomial) expect_snapshot(