Skip to content

Commit

Permalink
Adding 'card' print method (#102)
Browse files Browse the repository at this point in the history
**What changes are proposed in this pull request?**
Adding `print.card()` method.

@bzkrouse what do you think of the print method? Once we finalize, I'll
finish the PR which will lead to some changes in snapshot testing I
think.

<img width="787" alt="image"
src="https://github.com/insightsengineering/cards/assets/26774684/a2b0f8d7-2142-4c81-9ff1-1a1a987cd104">

What the print method does:
- converts object to data frame so the list columns show the values
(unlike a tibble print)
- prints between 10 and 20 rows
- messaging for the number of rows/columns that are not shown
- Removes warning and error columns if nothing to report
- if there are more than 6 columns, statistic_fmt_fn/contex columns
removed
- A max of 9 characters are displayed for columns 'group##_level',
'variable_level', 'stat_label', and 'context'
- If a statistic is a double, then it is rounded to 3 decimal places
- If a proper function is found in the statistic_fmt_fn column, it's
printed as "\<fn\>" to save space

**Reference GitHub issue associated with pull request.** _e.g., 'closes
#1'_
closes #93


--------------------------------------------------------------------------------

Reviewer Checklist (if item does not apply, mark is as complete)

- [ ] Ensure all package dependencies are installed:
`devtools::install_dev_deps()`
- [ ] PR branch has pulled the most recent updates from master branch:
`usethis::pr_merge_main()`
- [ ] If a bug was fixed, a unit test was added.
- [ ] Run `pkgdown::build_site()`. Check the R console for errors, and
review the rendered website.
- [ ] Code coverage is suitable for any new functions/features:
`devtools::test_coverage()`
- [ ] `usethis::use_spell_check()` runs with no spelling errors in
documentation

When the branch is ready to be merged:
- [ ] Update `NEWS.md` with the changes from this pull request under the
heading "`# cards (development version)`". If there is an issue
associated with the pull request, reference it in parentheses at the end
update (see `NEWS.md` for examples).
- [ ] Increment the version number using `usethis::use_version(which =
"dev")`
- [ ] Run `usethis::use_spell_check()` again
- [ ] Approve Pull Request
- [ ] Merge the PR. Please use "Squash and merge".
  • Loading branch information
ddsjoberg authored Jan 5, 2024
1 parent 01c0df1 commit 0ebb96b
Show file tree
Hide file tree
Showing 37 changed files with 615 additions and 343 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand

S3method(print,card)
export("%>%")
export(all_ard_groups)
export(all_ard_variables)
Expand Down
3 changes: 1 addition & 2 deletions R/ard_categorical.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@
#' @export
#'
#' @examples
#' ard_categorical(ADSL, by = "ARM", variables = "AGEGR1") |>
#' flatten_ard()
#' ard_categorical(ADSL, by = "ARM", variables = "AGEGR1")
ard_categorical <- function(data, variables, by = NULL, strata = NULL,
statistics = everything() ~ categorical_variable_summary_fns(),
denominator = NULL,
Expand Down
16 changes: 6 additions & 10 deletions R/ard_comparison.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,17 @@
#' @examples
#' ADSL |>
#' dplyr::filter(ARM %in% c("Placebo", "Xanomeline High Dose")) |>
#' ard_ttest(by = "ARM", variable = "AGE") |>
#' flatten_ard()
#' ard_ttest(by = "ARM", variable = "AGE")
#'
#' ADSL |>
#' dplyr::filter(ARM %in% c("Placebo", "Xanomeline High Dose")) |>
#' ard_wilcoxtest(by = "ARM", variable = "AGE") |>
#' flatten_ard()
#' ard_wilcoxtest(by = "ARM", variable = "AGE")
#'
#' ADSL |>
#' ard_chisqtest(by = "ARM", variable = "AGEGR1") |>
#' flatten_ard()
#' ard_chisqtest(by = "ARM", variable = "AGEGR1")
#'
#' ADSL[1:30,] |>
#' ard_fishertest(by = "ARM", variable = "AGEGR1") |>
#' flatten_ard()
#' ard_fishertest(by = "ARM", variable = "AGEGR1")
NULL

#' @rdname ard_comparison
Expand Down Expand Up @@ -103,9 +99,9 @@ ard_ttest <- function(data, by, variable, ...) {
.data$stat_name %in% "parameter" ~ "Degrees of Freedom",
.data$stat_name %in% "conf.low" ~ "CI Lower Bound",
.data$stat_name %in% "conf.high" ~ "CI Upper Bound",
.data$stat_name %in% "mu" ~ "Null Hypothesis Mean",
.data$stat_name %in% "mu" ~ "H0 Mean",
.data$stat_name %in% "paired" ~ "Paired t-test",
.data$stat_name %in% "var.equal" ~ "Assumed Equal Variances",
.data$stat_name %in% "var.equal" ~ "Equal Variances",
.data$stat_name %in% "conf.level" ~ "CI Confidence Level",
TRUE ~ .data$stat_name
)
Expand Down
3 changes: 1 addition & 2 deletions R/ard_continuous.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
#' @export
#'
#' @examples
#' ard_continuous(ADSL, by = "ARM", variables = "AGE") |>
#' flatten_ard()
#' ard_continuous(ADSL, by = "ARM", variables = "AGE")
ard_continuous <- function(data,
variables,
by = NULL,
Expand Down
6 changes: 2 additions & 4 deletions R/ard_hierarchical.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@
#' variables = c(AESOC, AETERM),
#' by = c(TRTA, AESEV),
#' denominator = ADSL |> dplyr::rename(TRTA = ARM)
#' ) |>
#' flatten_ard()
#' )
#'
#' ard_hierarchical_count(
#' data = ADAE,
#' variables = c(AESOC, AETERM),
#' by = TRTA
#' ) |>
#' flatten_ard()
#' )
NULL

#' @rdname ard_hierarchical
Expand Down
3 changes: 1 addition & 2 deletions R/ard_missing.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
#' @export
#'
#' @examples
#' ard_missing(ADSL, by = "ARM", variables = "AGE") |>
#' flatten_ard()
#' ard_missing(ADSL, by = "ARM", variables = "AGE")
ard_missing <- function(data,
variables,
by = NULL,
Expand Down
30 changes: 27 additions & 3 deletions R/ard_regression.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
#'
#' @examples
#' lm(AGE ~ ARM, data = ADSL) |>
#' ard_regression(add_estimate_to_reference_rows = TRUE) |>
#' flatten_ard()
#' ard_regression(add_estimate_to_reference_rows = TRUE)
ard_regression <- function(model, tidy_fun = NULL, ...) {
# check installed packages ---------------------------------------------------
check_installed("broom.helpers")
Expand All @@ -37,6 +36,31 @@ ard_regression <- function(model, tidy_fun = NULL, ...) {
names_to = "stat_name",
values_to = "statistic"
) |>
dplyr::mutate(context = "regression") %>%
dplyr::mutate(
statistic_fmt_fn =
lapply(
.data$statistic,
function(x) switch(is.integer(x), 0L) %||% switch(is.numeric(x), 1L)),
context = "regression",
stat_label =
dplyr::case_when(
.data$stat_name %in% "var_label" ~ "Label",
.data$stat_name %in% "var_class" ~ "Class",
.data$stat_name %in% "var_type" ~ "Type",
.data$stat_name %in% "var_nlevels" ~ "N Levels",
.data$stat_name %in% "contrasts_type" ~ "Contrast Type",
.data$stat_name %in% "label" ~ "Level Label",
.data$stat_name %in% "n_obs" ~ "N Obs.",
.data$stat_name %in% "n_event" ~ "N Events",
.data$stat_name %in% "exposure" ~ "Exposure Time",
.data$stat_name %in% "estimate" ~ "Coefficient",
.data$stat_name %in% "std.error" ~ "Standard Error",
.data$stat_name %in% "p.value" ~ "p-value",
.data$stat_name %in% "conf.low" ~ "CI Lower Bound",
.data$stat_name %in% "conf.high" ~ "CI Upper Bound",
TRUE ~ .data$stat_name
)
) |>
tidy_ard_column_order() %>%
{structure(., class = c("card", class(.)))}
}
1 change: 0 additions & 1 deletion R/as_nested_list.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#' as_nested_list()
as_nested_list <- function(x) {
# check in inputs ------------------------------------------------------------
check_installed("jsonlite")
if (!inherits(x, "card")) {
cli::cli_abort("Argument {.code x} must be class {.cls card}.")
}
Expand Down
3 changes: 1 addition & 2 deletions R/flatten_ard.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
#' @export
#'
#' @examples
#' ard_categorical(mtcars, by = "cyl", variables = c("am", "gear")) |>
#' flatten_ard()
#' ard_categorical(mtcars, by = "cyl", variables = c("am", "gear"))
flatten_ard <- function(x) {
# check inputs ---------------------------------------------------------------
check_class(class = "card", x = x)
Expand Down
102 changes: 102 additions & 0 deletions R/print.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#' Print
#'
#' Print method for objects of class 'card'
#'
#' @param x object of class 'card'
#' @param n integer specifying the number of rows to print
#' @param columns string indicating whether to print a selected number of
#' columns or all.
#' @param n_col some columns are removed when there are more than a threshold
#' of columns present. This argument sets that threshold. Default is `6L`
#' @param ... not used
#'
#' @return data frame
#' @export
#'
#' @examples
#' ard_categorical(ADSL, variables = AGEGR1) |>
#' print()
print.card <- function(x, n = NULL, columns = c("auto", "all"), n_col = 6L, ...) {
# convert to a data frame so the list columns print the values in the list ---
x_print <- as.data.frame(x)

# number of rows to print (modeled after tibbles print) ----------------------
n <- n %||% ifelse(nrow(x_print) > 20L, 10L, nrow(x_print))
x_print <- utils::head(x_print, n = n)

# remove columns -------------------------------------------------------------
if (arg_match(columns) %in% "auto") {
# remove warning and error columns if nothing to report
if ("error" %in% names(x_print) && every(x_print[["error"]], is.null))
x_print[["error"]] <- NULL
if ("warning" %in% names(x_print) && every(x_print[["warning"]], is.null))
x_print[["warning"]] <- NULL
if (ncol(x_print) > n_col)
x_print[["statistic_fmt_fn"]] <- NULL # remove this col if there are many cols
if (ncol(x_print) > n_col)
x_print[["context"]] <- NULL # remove this col if there are many cols
}

# truncate the 'group##_level', 'variable_level', 'stat_label', and 'context' columns ------
x_print <-
tryCatch(
x_print |>
dplyr::mutate(
across(
c(all_ard_groups(FALSE, TRUE),
all_ard_variables(FALSE, TRUE),
any_of(c("context", "stat_label", "warning", "error"))),
function(x) {
lapply(
x,
function(e) {
e <- as.character(e) |> paste(collapse = ", ")
ifelse(nchar(e) > 9, paste0(substr(e, 1, 8), "\u2026"), e)
}
)
}
)
),
error = function(e) x_print
)

# for the statistics, round to 3 decimal places ------------------------------
if ("statistic" %in% names(x_print)) {
x_print$statistic <- lapply(
x_print$statistic,
function(x) {
if (isTRUE(is.double(x))) return(round5(x, digits = 3))
if (is_string(x) && nchar(x) > 9) return(paste0(substr(x, 1, 8), "\u2026"))
x
}
)
}

# for the formatting function column, abbreviate the print of proper functions
if ("statistic_fmt_fn" %in% names(x_print)) {
x_print$statistic_fmt_fn <- lapply(
x_print$statistic_fmt_fn,
function(x) {
if (isTRUE(is.function(x))) {
return("<fn>")
}
x
}
)
}

# final printing --------------------------------------------------------------
cli::cli_text(cli::col_grey("{{cards}} data frame: {nrow(x)} x {ncol(x)}"))
print(x_print)
if (nrow(x) > n) {
cli::cli_alert_info(cli::col_grey("{nrow(x) - n} more rows"))
cli::cli_alert_info(cli::col_grey("Use {.code print(n = ...)} to see more rows"))
}
if (ncol(x) > ncol(x_print)) {
missing_cols <- names(x) |> setdiff(names(x_print))
cli::cli_alert_info(cli::col_grey(
"{length(missing_cols)} more variable{?s}: {paste(missing_cols, collapse = ', ')}"
))
}
invisible(x)
}
9 changes: 3 additions & 6 deletions R/summary_functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,21 @@
#' ADSL,
#' variables = "AGE",
#' statistics = ~continuous_variable_summary_fns(c("N", "median"))
#' ) |>
#' flatten_ard()
#' )
#'
#' # categorical variable summaries
#' ard_categorical(
#' ADSL,
#' variables = "AGEGR1",
#' statistics = ~categorical_variable_summary_fns(c("n", "N"))
#' ) |>
#' flatten_ard()
#' )
#'
#' # summary for rates of missing data
#' ard_missing(
#' ADSL,
#' variables = c("AGE", "AGEGR1"),
#' statistics = ~missing_variable_summary_fns()
#' ) |>
#' flatten_ard()
#' )
NULL

#' @rdname summary_functions
Expand Down
1 change: 1 addition & 0 deletions R/tidy_as_ard.R
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ tidy_as_ard <- function(lst_tidy,
dplyr::tibble(
stat_name = names(lst_all_results),
statistic = lst_all_results,
statistic_fmt_fn = lapply(.data$statistic, function(x) switch(is.numeric(x), 1L)),
warning = lst_tidy["warning"],
error = lst_tidy["error"],
!!!lst_ard_columns,
Expand Down
20 changes: 4 additions & 16 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,15 @@ ARD Examples
```{r example}
library(cards)
ard_continuous(ADSL, by = "ARM", variables = c("AGE", "BMIBL")) |>
flatten_ard() |>
head(n = 10) |>
knitr::kable()
ard_continuous(ADSL, by = "ARM", variables = c("AGE", "BMIBL"))
ard_categorical(ADSL, by = "ARM", variables = c("AGEGR1", "SEX")) |>
flatten_ard() |>
head(n = 10) |>
knitr::kable()
ard_categorical(ADSL, by = "ARM", variables = c("AGEGR1", "SEX"))
ADSL |>
dplyr::filter(ARM %in% c("Placebo", "Xanomeline High Dose")) |> # only only two groups for a t-test
ard_ttest(by = "ARM", variable = "AGE") |>
dplyr::filter(stat_name %in% c("estimate", "conf.low", "conf.high", "p.value", "method")) |>
flatten_ard() |>
knitr::kable()
ard_ttest(by = "ARM", variable = "AGE")
survival::coxph(ggsurvfit::Surv_CNSR() ~ TRTP, data = ADTTE) |>
ard_regression(add_estimate_to_reference_rows = TRUE) |>
dplyr::filter(stat_name %in% c("estimate", "conf.low", "conf.high", "p.value")) |>
flatten_ard() |>
tidyr::drop_na() |>
knitr::kable()
dplyr::filter(stat_name %in% c("estimate", "conf.low", "conf.high", "p.value"))
```
Loading

0 comments on commit 0ebb96b

Please sign in to comment.