diff --git a/DESCRIPTION b/DESCRIPTION index d411bc2b..f6f70177 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: pmtables Type: Package Title: Tables for Pharmacometrics -Version: 0.5.0 +Version: 0.5.1 Authors@R: c( person(given = "Kyle", @@ -36,7 +36,7 @@ Suggests: testthat, yaml, fs, texPreview, magick, pdftools Encoding: UTF-8 Language: en-US LazyData: true -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.0 Roxygen: list(markdown = TRUE) VignetteBuilder: knitr Collate: diff --git a/NAMESPACE b/NAMESPACE index 60405145..1ccda511 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -102,6 +102,7 @@ export(st_data) export(st_drop) export(st_edit) export(st_files) +export(st_filter) export(st_hline) export(st_image_show) export(st_it) diff --git a/NEWS.md b/NEWS.md index 0eb752c9..a1be2c87 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,19 @@ +# pmtables 0.5.1 + +- New function `st_filter()` to filter data item in a pipeline (#298). + +- Add `summarize_all` and `all_name_stacked` arguments to + `pt_data_inventory()` (#297). + +## Bugs Fixed + +- Fixed bug where `all_name` was not getting used in `pt_data_inventory()` + (#297). + +- Fixed bug where detached table notes were getting rendered too close to the + main table when building standalone pdf under certain TeX distributions + (#286). + # pmtables 0.5.0 - New functions `st_as_image()`, `st2pdf()`, and `st2png()` to render tables with diff --git a/R/data_inventory_table.R b/R/data_inventory_table.R index 305124fb..71c58486 100644 --- a/R/data_inventory_table.R +++ b/R/data_inventory_table.R @@ -26,7 +26,7 @@ data_inventory_chunk <- function(data, by, panel = by, stacked = FALSE, ...) { if(by==".total" | panel == ".total") { - data <- data_total_col(data,all_name = all_name) + data <- data_total_col(data, all_name = all_name) } miss <- FALSE @@ -134,7 +134,7 @@ data_inventory_data_split <- function(data,by,panel=by,stacked=FALSE,...) { #' #' @export data_inventory_data <- function(data, by, panel = by, all_name = "all", - stacked = FALSE, ...) { + summarize_all = TRUE, stacked = FALSE, ...) { by <- unname(by) panel <- unname(panel) @@ -156,7 +156,7 @@ data_inventory_data <- function(data, by, panel = by, all_name = "all", ... ) - if(by != ".total") { + if(by != ".total" && isTRUE(summarize_all)) { tot <- data_inventory_chunk( data, by = ".total", @@ -229,6 +229,10 @@ pt_data_study <- function(data, study_col = "STUDY", panel = study_col, ...) { #' `MISS` values are equal to zero. #' @param stacked If `TRUE`, then independent summaries are created by `outer` #' and included in a single table (see examples). +#' @param summarize_all if `TRUE` then a complete data summary will be +#' appended to the bottom of the table when `stacked` is `FALSE`. +#' @param all_name_stacked a name to use for the complete data summary when +#' `stacked` is `TRUE`. #' @param dv_col Character name of `DV` column. #' @param bq_col Character name of `BQL` column; see [find_bq_col()]. #' @param id_col Character name of `ID` column. @@ -300,12 +304,21 @@ pt_data_study <- function(data, study_col = "STUDY", panel = study_col, ...) { pt_data_inventory <- function(data, by = ".total", panel = by, inner_summary = TRUE, drop_miss = FALSE, stacked = FALSE, table = NULL, - all_name = "all", + summarize_all = TRUE, + all_name = "All data", + all_name_stacked = "Group Total", dv_col = "DV", bq_col = find_bq_col(data), id_col = "ID", ...) { + stacked <- isTRUE(stacked) + if(stacked) all_name <- all_name_stacked + summarize_all <- isTRUE(summarize_all) + + assert_that(is.data.frame(data)) + data <- as.data.frame(data) + has_panel <- !missing(panel) panel_data <- as.panel(panel) panel <- panel_data$col @@ -327,10 +340,11 @@ pt_data_inventory <- function(data, by = ".total", panel = by, inner_summary <- FALSE } - total_name <- case_when( - isTRUE(stacked) ~ "\\hline {\\it Group Total}", - TRUE ~ "\\hline \\hline {\\bf All data}" - ) + if(stacked) { + total_name <- paste0("\\hline {\\it ", all_name, "}") + } else { + total_name <- paste0("\\hline \\hline {\\bf ", all_name, "}") + } ans <- data_inventory_data( data, @@ -338,13 +352,14 @@ pt_data_inventory <- function(data, by = ".total", panel = by, panel = panel, stacked = stacked, all_name = all_name, + summarize_all = summarize_all, dv_col = dv_col, bq_col = bq_col, id_col = id_col, ... ) - if(exists(by,ans)) { + if(exists(by, ans)) { ans <- mutate( ans, !!sym(by) := ifelse(!!sym(by)==".total", total_name, !!sym(by)) @@ -365,7 +380,7 @@ pt_data_inventory <- function(data, by = ".total", panel = by, `Percent.OBS` = .data[["OOBS"]], `Percent.BQL` = .data[["OBQL"]] ) - ans <- mutate(ans,POBS=NULL,PBQL=NULL) + ans <- mutate(ans, POBS = NULL, PBQL = NULL) } ans <- rename( @@ -384,7 +399,7 @@ pt_data_inventory <- function(data, by = ".total", panel = by, ans <- mutate(ans, Number.MISS = NULL) } - ans <- mutate(ans,.total = NULL) + ans <- mutate(ans, .total = NULL) out <- ans notes <- pt_data_inventory_notes(bq = bq_col, drop_bql = drop_bql) @@ -396,8 +411,6 @@ pt_data_inventory <- function(data, by = ".total", panel = by, out <- select(out, !contains("BQL")) } - .sumrows <- NULL - .panel <- rowpanel(NULL) if(has_panel) { .panel <- panel_data @@ -406,10 +419,6 @@ pt_data_inventory <- function(data, by = ".total", panel = by, if(panel==by) panel <- NULL - if(!stacked & isTRUE(has_by)) { - .sumrows <- sumrow(out[,1]==total_name, bold = TRUE) - } - out <- list( data = out, panel = .panel, diff --git a/R/table-notes.R b/R/table-notes.R index ca3934d8..8e30a021 100644 --- a/R/table-notes.R +++ b/R/table-notes.R @@ -108,9 +108,11 @@ noteconf <- function(width = 0.8, is.noteconfig <- function(x) inherits(x, "noteconfig") mini_notes <- function(notes, x) { - hline1 <- hline2 <- paste0("\\rule{", 1, "\\linewidth}{",x$hline_pt, "pt}") - tskip <- paste0("\\vskip ", x$table_skip, "cm") - nskip <- paste0("\\vskip ", x$note_skip, "cm") + hline1 <- hline2 <- c( + paste0("\\rule{", 1, "\\linewidth}{",x$hline_pt, "pt}") + ) + tskip <- paste0("\\vspace{", x$table_skip, "cm}") + nskip <- paste0("\\vspace{", x$note_skip, "cm}") if(!x$hline1) { hline1 <- NULL } @@ -119,7 +121,9 @@ mini_notes <- function(notes, x) { } notes <- paste(notes, "\\newline") out <- c( + " ", tskip, + " ", paste0("\\begin{minipage}{",x$width,"\\linewidth}"), paste0("\\linespread{1.1}\\selectfont"), hline1, diff --git a/R/table-object.R b/R/table-object.R index 932a8673..842c25dd 100644 --- a/R/table-object.R +++ b/R/table-object.R @@ -867,8 +867,21 @@ st_drop <- function(x, ...) { #' These functions modify the input data frame prior to passing it to #' [stable()] or [stable_long()]. #' -#' @param x an stobject -#' @param ... passed to [dplyr::select()], or [dplyr::mutate()] +#' @param x an stobject. +#' @param ... passed to [dplyr::select()], [dplyr::mutate()], or +#' [dplyr::filter()]. +#' +#' @details +#' - `st_select` calls `dplyr::select` on the data +#' - `st_mutate` calls `dplyr::mutate` on the data +#' - `st_filter` calls `dplyr::filter` on the data +#' +#' @examples +#' tab <- pt_data_inventory(pmt_obs, by = "FORM") +#' obj <- st_new(tab) +#' st_filter(obj, FORM != "troche") +#' st_select(obj, -contains("BQL")) +#' st_mutate(obj, FORM = ifelse(FORM=="tablet", "ODT", FORM)) #' #' @export st_select <- function(x, ...) { @@ -885,6 +898,14 @@ st_mutate <- function(x, ...) { x } +#' @rdname st_select +#' @export +st_filter <- function(x, ...) { + check_st(x) + x$data <- filter(x$data, ...) + x +} + #' Edit table contents #' #' These functions modify the input data frame prior to passing to [stable()] diff --git a/inst/validation/build-validation-docs.R b/inst/validation/build-validation-docs.R index 36be5204..a77991bf 100644 --- a/inst/validation/build-validation-docs.R +++ b/inst/validation/build-validation-docs.R @@ -12,11 +12,11 @@ ####################################################### PKGNAME <- "pmtables" -PKGVERSION <- "0.4.1.9003" +PKGVERSION <- "0.5.0" STYLE_REF_DIR <- "docx-ref-header-image" # set to NULL if not using style ref # set up directories and clear existing output dirs, if they exist -val_dir <- system.file("validation", package = PKGNAME) +val_dir <- getwd() # system.file("validation", package = PKGNAME) print(val_dir) style_ref_path <- NULL diff --git a/inst/validation/pmtables-stories.yaml b/inst/validation/pmtables-stories.yaml index 8caa1933..d920ff83 100644 --- a/inst/validation/pmtables-stories.yaml +++ b/inst/validation/pmtables-stories.yaml @@ -1,4 +1,28 @@ # Please add user stories at the top of this file +PMT-S082: + name: Filter data from an st object + description: > + As a user, I want to filter data in an st object. + ProductRisk: low-risk + tests: + - PMT-STFUN-0001 +PMT-S081: + name: Customize All Data summary in pt inventory table + description: > + As a user, I want to customize the label on All Data summary in + pt_data_inventory. + ProductRisk: low-risk + tests: + - PMT-INVEN-0002 + - PMT-INVEN-0003 +PMT-S080: + name: Optional All Data summary on pt inventory table + description: > + As a user, I want to opt in to the All Data summary + in pt_data_inventory. + ProductRisk: medium-risk + tests: + - PMT-INVEN-0001 PMT-S079: name: sig returns character when passed integer description: > diff --git a/man/data_inventory_data.Rd b/man/data_inventory_data.Rd index 569653ee..e406309f 100644 --- a/man/data_inventory_data.Rd +++ b/man/data_inventory_data.Rd @@ -9,6 +9,7 @@ data_inventory_data( by, panel = by, all_name = "all", + summarize_all = TRUE, stacked = FALSE, ... ) @@ -24,6 +25,9 @@ not add or remove rows prior to summarizing \code{data}} \item{all_name}{a name to use for the complete data summary} +\item{summarize_all}{if \code{TRUE} then a complete data summary will be +appended to the bottom of the table when \code{stacked} is \code{FALSE}.} + \item{stacked}{If \code{TRUE}, then independent summaries are created by \code{outer} and included in a single table (see examples).} diff --git a/man/pt_data_inventory.Rd b/man/pt_data_inventory.Rd index 20e51269..2c974691 100644 --- a/man/pt_data_inventory.Rd +++ b/man/pt_data_inventory.Rd @@ -12,7 +12,9 @@ pt_data_inventory( drop_miss = FALSE, stacked = FALSE, table = NULL, - all_name = "all", + summarize_all = TRUE, + all_name = "All data", + all_name_stacked = "Group Total", dv_col = "DV", bq_col = find_bq_col(data), id_col = "ID", @@ -40,8 +42,14 @@ and included in a single table (see examples).} \item{table}{a named list to use for renaming columns (see details and examples)} +\item{summarize_all}{if \code{TRUE} then a complete data summary will be +appended to the bottom of the table when \code{stacked} is \code{FALSE}.} + \item{all_name}{a name to use for the complete data summary} +\item{all_name_stacked}{a name to use for the complete data summary when +\code{stacked} is \code{TRUE}.} + \item{dv_col}{Character name of \code{DV} column.} \item{bq_col}{Character name of \code{BQL} column; see \code{\link[=find_bq_col]{find_bq_col()}}.} diff --git a/man/st_select.Rd b/man/st_select.Rd index b6d1ce3b..7074f6d2 100644 --- a/man/st_select.Rd +++ b/man/st_select.Rd @@ -3,18 +3,37 @@ \name{st_select} \alias{st_select} \alias{st_mutate} +\alias{st_filter} \title{Filter, select, or mutate data} \usage{ st_select(x, ...) st_mutate(x, ...) + +st_filter(x, ...) } \arguments{ -\item{x}{an stobject} +\item{x}{an stobject.} -\item{...}{passed to \code{\link[dplyr:select]{dplyr::select()}}, or \code{\link[dplyr:mutate]{dplyr::mutate()}}} +\item{...}{passed to \code{\link[dplyr:select]{dplyr::select()}}, \code{\link[dplyr:mutate]{dplyr::mutate()}}, or +\code{\link[dplyr:filter]{dplyr::filter()}}.} } \description{ These functions modify the input data frame prior to passing it to \code{\link[=stable]{stable()}} or \code{\link[=stable_long]{stable_long()}}. } +\details{ +\itemize{ +\item \code{st_select} calls \code{dplyr::select} on the data +\item \code{st_mutate} calls \code{dplyr::mutate} on the data +\item \code{st_filter} calls \code{dplyr::filter} on the data +} +} +\examples{ +tab <- pt_data_inventory(pmt_obs, by = "FORM") +obj <- st_new(tab) +st_filter(obj, FORM != "troche") +st_select(obj, -contains("BQL")) +st_mutate(obj, FORM = ifelse(FORM=="tablet", "ODT", FORM)) + +} diff --git a/tests/testthat/test-inventory-table.R b/tests/testthat/test-inventory-table.R index 54797281..45147a3f 100644 --- a/tests/testthat/test-inventory-table.R +++ b/tests/testthat/test-inventory-table.R @@ -1,4 +1,5 @@ library(testthat) +library(pmtables) context("test-inventory-table") @@ -150,3 +151,43 @@ test_that("handle BQL and BLQ inventory table [PMT-TEST-0133]", { expect_equal(tab2$notes[3], "MISS: missing observations (non-BLQ)") expect_equal(tab3$notes[2], "MISS: missing observations") }) + +test_that("Optional All Data summary - inventory table [PMT-INVEN-0001]", { + a <- pt_data_inventory( + pmt_obs, + by = "STUDYf" + )$data + b <- pt_data_inventory( + pmt_obs, + by = "STUDYf", + summarize_all = FALSE + )$data + expect_identical(a[1:4,], b[1:4,]) + expect_true(nrow(a) == 1 + nrow(b)) +}) + +test_that("Change All Data name - inventory table [PMT-INVEN-0002]", { + a <- pt_data_inventory( + pmt_obs, + by = "STUDYf" + )$data + b <- pt_data_inventory( + pmt_obs, + by = "STUDYf", + all_name="ALL" + )$data + expect_match(a$STUDYf[5], "All") + expect_match(b$STUDYf[5], "ALL") +}) + +test_that("Change stacked group name - inventory table [PMT-INVEN-0003]", { + a <- pt_data_inventory( + pmt_obs, + by = "STUDYf", + stacked = TRUE, + panel = "SEQf", + all_name_stacked = "STACKED" + )$data + expect_match(a$STUDYf[5], "STACKED") + expect_match(a$STUDYf[11], "STACKED") +}) diff --git a/tests/testthat/test-notes.R b/tests/testthat/test-notes.R index 3b8f3a2f..b7eeea28 100644 --- a/tests/testthat/test-notes.R +++ b/tests/testthat/test-notes.R @@ -34,8 +34,8 @@ test_that("mini notes [PMT-TEST-0154]", { expect_match(x$mini_notes,"abcd \\newline", fixed = TRUE, all = FALSE) expect_match(x$mini_notes,"xyz \\newline", fixed = TRUE, all = FALSE) expect_match(x$mini_notes,"end{minipage}", fixed = TRUE, all = FALSE) - expect_equal(x$mini_notes[1], "\\vskip 0.6cm", fixed = TRUE, all = FALSE) - expect_match(x$mini_notes, "\\vskip 0.1cm", fixed = TRUE, all = FALSE) + expect_equal(x$mini_notes[2], "\\vspace{0.6cm}", fixed = TRUE, all = FALSE) + expect_match(x$mini_notes, "\\vspace{0.1cm}", fixed = TRUE, all = FALSE) }) test_that("notes escape [PMT-TEST-0155]", { diff --git a/tests/testthat/test-table-object.R b/tests/testthat/test-table-object.R index f6468437..7a987da3 100644 --- a/tests/testthat/test-table-object.R +++ b/tests/testthat/test-table-object.R @@ -412,3 +412,11 @@ test_that("substitute lines in table notes", { regexp = "did not find any notes" ) }) + +test_that("st_filter filters data in pmtable object [PMT-STFUN-0001]", { + data <- stdata() + x <- st_new(data) + y <- st_filter(x, FORM != "troche") + expect_true(all(y$data$FORM %in% c("tablet", "capsule"))) + expect_true("troche" %in% data$FORM) +}) diff --git a/tests/testthat/validate/notes-mini.tex b/tests/testthat/validate/notes-mini.tex index f23c4c44..f7986be5 100644 --- a/tests/testthat/validate/notes-mini.tex +++ b/tests/testthat/validate/notes-mini.tex @@ -21,11 +21,13 @@ \hline \end{tabular} \end{threeparttable} -\vskip 0.67cm + +\vspace{0.67cm} + \begin{minipage}{0.8\linewidth} \linespread{1.1}\selectfont \rule{1\linewidth}{0.4pt} -\vskip 0.02cm +\vspace{0.02cm} WT: weight \newline ALB: albumin \newline \end{minipage} diff --git a/tests/testthat/validate/validate.pdf b/tests/testthat/validate/validate.pdf index d1ecc045..a364254a 100644 Binary files a/tests/testthat/validate/validate.pdf and b/tests/testthat/validate/validate.pdf differ