From d2cdf0ab5a85ff7470cd79cf6fb7e3234a842507 Mon Sep 17 00:00:00 2001 From: Kyle Baron Date: Thu, 17 Mar 2022 23:41:33 -0500 Subject: [PATCH 1/5] initial commit for col note --- DESCRIPTION | 1 + NAMESPACE | 1 + R/col-note.R | 60 ++++++++++++++++++++++++++++++++++++++++++++++ man/ys_col_note.Rd | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 R/col-note.R create mode 100644 man/ys_col_note.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 78b3cc1..18b61d5 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -61,6 +61,7 @@ Collate: 'class-ycol.R' 'class-yproject.R' 'class-yspec.R' + 'col-note.R' 'col_factor.R' 'define.R' 'definetemplate.R' diff --git a/NAMESPACE b/NAMESPACE index 139c82e..99028b2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -84,6 +84,7 @@ export(ys_add_factors) export(ys_add_labels) export(ys_check) export(ys_check_file) +export(ys_col_note) export(ys_document) export(ys_dont_sanitize) export(ys_extend) diff --git a/R/col-note.R b/R/col-note.R new file mode 100644 index 0000000..7639b8d --- /dev/null +++ b/R/col-note.R @@ -0,0 +1,60 @@ +#' Create table note text for abbreviated columns +#' +#' Produces a string with `: ` format which can be +#' included in a table note providing a more informative definition with the +#' column name. The column definition can be generated from the `short` name +#' or the `label` (but note that these could frequently be the same). +#' +#' @param .spec a yspec object. +#' @param ... passed to [ys_select()]. +#' @param .unit logical; if `TRUE`, then append the unit surrounded by parens +#' to the column definition. +#' @param .title_case logical; if `TRUE` then the column definition text is +#' passed through [tools::toTitleCaase()]. +#' @param .sep a separator character for columns. +#' @param .to_string logical; if `TRUE`, then a single string is returned. +#' @param .width if `numeric` and `.to_string` is `TRUE`, then the result is +#' passed through [base::strwrap()]. +#' @param .type selects if the column definition is generated from calling +#' [ys_get_short()] or [ys_get_label()] +#' +#' @return +#' A string of length one when `.to_string` is `TRUE` or a character vector +#' if `.to_string` is `FALSE`. +#' +#' @examples +#' +#' spec <- ys_help$spec() +#' +#' ys_col_note(spec, AST, ALT, SCR, .unit = TRUE) +#' +#' @md +#' @export +ys_col_note <- function(.spec, ..., .unit = FALSE, .title_case = FALSE, + .sep = "; ", .to_string = TRUE, .width = NULL, + .type = c("short", "label")) { + .spec <- ys_select(.spec, ...) + if(length(.spec)==0) return(NULL) + .type <- match.arg(.type) + if(.type=="short") { + sh <- unlist(ys_get_short(.spec)) + } + if(.type=="label") { + sh <- unlist(ys_get_label(.spec)) + } + if(isTRUE(.title_case)) { + sh <- toTitleCase(sh) + } + if(isTRUE(.unit)) { + u <- ys_get_unit(.spec, parens = TRUE) + sh <- paste0(sh, " ", u) + } + ans <- paste0(names(.spec), ": ", unname(sh)) + if(isTRUE(.to_string)) { + ans <- paste0(ans, collapse = .sep) + if(is.numeric(.width)) { + ans <- strwrap(ans, width = .width) + } + } + ans +} diff --git a/man/ys_col_note.Rd b/man/ys_col_note.Rd new file mode 100644 index 0000000..cef17a9 --- /dev/null +++ b/man/ys_col_note.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/col-note.R +\name{ys_col_note} +\alias{ys_col_note} +\title{Create table note text for abbreviated columns} +\usage{ +ys_col_note( + .spec, + ..., + .unit = FALSE, + .sep = "; ", + .to_string = TRUE, + .width = NULL +) +} +\arguments{ +\item{.spec}{a yspec object.} + +\item{...}{passed to \code{\link[=ys_select]{ys_select()}}.} + +\item{.unit}{passed to \code{\link[=ys_get_short]{ys_get_short()}} or \code{\link[=ys_get_short_unit]{ys_get_short_unit()}}.} + +\item{.sep}{a separator character for columns.} + +\item{.to_string}{logical; if \code{TRUE}, then a single string is returned.} + +\item{.width}{if \code{numeric} and \code{.to_string} is \code{TRUE}, then the result is +passed through \code{\link[base:strwrap]{base::strwrap()}}.} +} +\value{ +A string of length one when \code{.to_string} is \code{TRUE} or a character vector +if \code{.to_string} is \code{FALSE}. +} +\description{ +Create table note text for abbreviated columns +} +\examples{ + +spec <- ys_help$spec() + +ys_col_note(spec, AST, ALT, SCR, .unit = TRUE) + +} From 4a8b491d14b7e793af4b43e831dc649b67926923 Mon Sep 17 00:00:00 2001 From: Kyle Baron Date: Fri, 18 Mar 2022 15:51:23 -0500 Subject: [PATCH 2/5] Update R/col-note.R Co-authored-by: KatherineKayMRG <42553148+KatherineKayMRG@users.noreply.github.com> --- R/col-note.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/col-note.R b/R/col-note.R index 7639b8d..f603c8d 100644 --- a/R/col-note.R +++ b/R/col-note.R @@ -10,7 +10,7 @@ #' @param .unit logical; if `TRUE`, then append the unit surrounded by parens #' to the column definition. #' @param .title_case logical; if `TRUE` then the column definition text is -#' passed through [tools::toTitleCaase()]. +#' passed through [tools::toTitleCase()]. #' @param .sep a separator character for columns. #' @param .to_string logical; if `TRUE`, then a single string is returned. #' @param .width if `numeric` and `.to_string` is `TRUE`, then the result is From bb286002e0dbbd6e0a810bcebb0dd2380c72d7fa Mon Sep 17 00:00:00 2001 From: Kyle Baron Date: Fri, 18 Mar 2022 16:02:26 -0500 Subject: [PATCH 3/5] refactor based on reviewer comments --- R/col-note.R | 15 +++++++++------ man/ys_col_note.Rd | 29 +++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/R/col-note.R b/R/col-note.R index f603c8d..943da0b 100644 --- a/R/col-note.R +++ b/R/col-note.R @@ -11,8 +11,11 @@ #' to the column definition. #' @param .title_case logical; if `TRUE` then the column definition text is #' passed through [tools::toTitleCase()]. -#' @param .sep a separator character for columns. -#' @param .to_string logical; if `TRUE`, then a single string is returned. +#' @param .sep a string to separate column name and column definition; +#' usually a space should be included as the terminal character (see default) +#' @param .to_string logical; if `TRUE`, then a single string is returned +#' @param .collapse a string to separate items when `.to_string` is `TRUE`; +#' usually a space should be included as the terminal character (see default) #' @param .width if `numeric` and `.to_string` is `TRUE`, then the result is #' passed through [base::strwrap()]. #' @param .type selects if the column definition is generated from calling @@ -31,8 +34,8 @@ #' @md #' @export ys_col_note <- function(.spec, ..., .unit = FALSE, .title_case = FALSE, - .sep = "; ", .to_string = TRUE, .width = NULL, - .type = c("short", "label")) { + .sep = ": ", .to_string = TRUE, .collapse = "; ", + .width = NULL, .type = c("short", "label")) { .spec <- ys_select(.spec, ...) if(length(.spec)==0) return(NULL) .type <- match.arg(.type) @@ -49,9 +52,9 @@ ys_col_note <- function(.spec, ..., .unit = FALSE, .title_case = FALSE, u <- ys_get_unit(.spec, parens = TRUE) sh <- paste0(sh, " ", u) } - ans <- paste0(names(.spec), ": ", unname(sh)) + ans <- paste0(names(.spec), .sep, unname(sh)) if(isTRUE(.to_string)) { - ans <- paste0(ans, collapse = .sep) + ans <- paste0(ans, collapse = .collapse) if(is.numeric(.width)) { ans <- strwrap(ans, width = .width) } diff --git a/man/ys_col_note.Rd b/man/ys_col_note.Rd index cef17a9..d53dcc1 100644 --- a/man/ys_col_note.Rd +++ b/man/ys_col_note.Rd @@ -8,9 +8,12 @@ ys_col_note( .spec, ..., .unit = FALSE, - .sep = "; ", + .title_case = FALSE, + .sep = ": ", .to_string = TRUE, - .width = NULL + .collapse = "; ", + .width = NULL, + .type = c("short", "label") ) } \arguments{ @@ -18,21 +21,35 @@ ys_col_note( \item{...}{passed to \code{\link[=ys_select]{ys_select()}}.} -\item{.unit}{passed to \code{\link[=ys_get_short]{ys_get_short()}} or \code{\link[=ys_get_short_unit]{ys_get_short_unit()}}.} +\item{.unit}{logical; if \code{TRUE}, then append the unit surrounded by parens +to the column definition.} -\item{.sep}{a separator character for columns.} +\item{.title_case}{logical; if \code{TRUE} then the column definition text is +passed through \code{\link[tools:toTitleCase]{tools::toTitleCase()}}.} -\item{.to_string}{logical; if \code{TRUE}, then a single string is returned.} +\item{.sep}{a string to separate column name and column definition; +usually a space should be included as the terminal character (see default)} + +\item{.to_string}{logical; if \code{TRUE}, then a single string is returned} + +\item{.collapse}{a string to separate items when \code{.to_string} is \code{TRUE}; +usually a space should be included as the terminal character (see default)} \item{.width}{if \code{numeric} and \code{.to_string} is \code{TRUE}, then the result is passed through \code{\link[base:strwrap]{base::strwrap()}}.} + +\item{.type}{selects if the column definition is generated from calling +\code{\link[=ys_get_short]{ys_get_short()}} or \code{\link[=ys_get_label]{ys_get_label()}}} } \value{ A string of length one when \code{.to_string} is \code{TRUE} or a character vector if \code{.to_string} is \code{FALSE}. } \description{ -Create table note text for abbreviated columns +Produces a string with \verb{: } format which can be +included in a table note providing a more informative definition with the +column name. The column definition can be generated from the \code{short} name +or the \code{label} (but note that these could frequently be the same). } \examples{ From e10027a8865077262c511a8f07956e7aa4ccb48b Mon Sep 17 00:00:00 2001 From: Kyle Baron Date: Fri, 18 Mar 2022 16:20:05 -0500 Subject: [PATCH 4/5] tests for col-note --- R/col-note.R | 1 - man/ys_col_note.Rd | 1 - tests/testthat/test-ys_col_note.R | 35 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-ys_col_note.R diff --git a/R/col-note.R b/R/col-note.R index 943da0b..accf4ea 100644 --- a/R/col-note.R +++ b/R/col-note.R @@ -26,7 +26,6 @@ #' if `.to_string` is `FALSE`. #' #' @examples -#' #' spec <- ys_help$spec() #' #' ys_col_note(spec, AST, ALT, SCR, .unit = TRUE) diff --git a/man/ys_col_note.Rd b/man/ys_col_note.Rd index d53dcc1..67ce608 100644 --- a/man/ys_col_note.Rd +++ b/man/ys_col_note.Rd @@ -52,7 +52,6 @@ column name. The column definition can be generated from the \code{short} name or the \code{label} (but note that these could frequently be the same). } \examples{ - spec <- ys_help$spec() ys_col_note(spec, AST, ALT, SCR, .unit = TRUE) diff --git a/tests/testthat/test-ys_col_note.R b/tests/testthat/test-ys_col_note.R new file mode 100644 index 0000000..642f9e6 --- /dev/null +++ b/tests/testthat/test-ys_col_note.R @@ -0,0 +1,35 @@ +library(yspec) +library(testthat) + +context("test-col-note") + +spec <- ys_help$spec() + +test_that("ys_col_note generates column definitions", { + ans <- ys_col_note(spec, WT, AAG, STUDY, .to_string = FALSE) + expect_equal(length(ans), 3) + ans <- ys_col_note(spec, CRCL, AAG, STUDY) + expect_equal(length(ans), 1) + expect_is(ans, "character") + expect_match(ans, "CRCL: CRCL", fixed = TRUE) + expect_match(ans, "AAG: alpha-1-acid glycoprotein", fixed = TRUE) + expect_match(ans, "STUDY: study number", fixed = TRUE) +}) + +test_that("ys_col_note can pull label", { + ans <- ys_col_note(spec, CRCL, AAG, .type = "label") + expect_match(ans, "CRCL: creatinine clearance", fixed = TRUE) + expect_match(ans, "AAG: alpha-1-acid glycoprotein", fixed = TRUE) +}) + +test_that("ys_col_note can customize separators", { + ans <- ys_col_note(spec, CRCL, .sep = "-") + expect_equal(ans, "CRCL-CRCL") + ans <- ys_col_note(spec, CRCL, MDV, .collapse = "+", .sep = "-") + expect_equal(ans, "CRCL-CRCL+MDV-MDV") +}) + +test_that("ys_col_note can render title case", { + ans <- ys_col_note(spec, STUDY, .title_case = TRUE) + expect_equal(ans, "STUDY: Study Number") +}) From 4386db69d1f3642195f74b8d604b3b9ab479fa0f Mon Sep 17 00:00:00 2001 From: Kyle Baron Date: Fri, 18 Mar 2022 23:26:46 -0500 Subject: [PATCH 5/5] clean up code and documentation; add NEWS entry --- NEWS.md | 4 ++++ R/col-note.R | 14 +++++++------- man/ys_col_note.Rd | 8 ++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/NEWS.md b/NEWS.md index ac3148f..0f70864 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # yspec (development version) +- Add `ys_col_note()` to generate a string of column names and definitions + based on `short` or `label`; use this when column names appear in a table + and require explanation in a a footnote #119, #120. + # yspec 0.5.2 - Adds `ys_extend()` to extend a spec object with additional diff --git a/R/col-note.R b/R/col-note.R index accf4ea..bc50ebb 100644 --- a/R/col-note.R +++ b/R/col-note.R @@ -12,14 +12,14 @@ #' @param .title_case logical; if `TRUE` then the column definition text is #' passed through [tools::toTitleCase()]. #' @param .sep a string to separate column name and column definition; -#' usually a space should be included as the terminal character (see default) -#' @param .to_string logical; if `TRUE`, then a single string is returned +#' usually a space should be included as the terminal character (see default). +#' @param .to_string logical; if `TRUE`, then a single string is returned. #' @param .collapse a string to separate items when `.to_string` is `TRUE`; -#' usually a space should be included as the terminal character (see default) +#' usually a space should be included as the terminal character (see default). #' @param .width if `numeric` and `.to_string` is `TRUE`, then the result is #' passed through [base::strwrap()]. #' @param .type selects if the column definition is generated from calling -#' [ys_get_short()] or [ys_get_label()] +#' [ys_get_short()] or [ys_get_label()]. #' #' @return #' A string of length one when `.to_string` is `TRUE` or a character vector @@ -39,10 +39,10 @@ ys_col_note <- function(.spec, ..., .unit = FALSE, .title_case = FALSE, if(length(.spec)==0) return(NULL) .type <- match.arg(.type) if(.type=="short") { - sh <- unlist(ys_get_short(.spec)) + sh <- unlist(ys_get_short(.spec), use.names=FALSE) } if(.type=="label") { - sh <- unlist(ys_get_label(.spec)) + sh <- unlist(ys_get_label(.spec), use.names=FALSE) } if(isTRUE(.title_case)) { sh <- toTitleCase(sh) @@ -51,7 +51,7 @@ ys_col_note <- function(.spec, ..., .unit = FALSE, .title_case = FALSE, u <- ys_get_unit(.spec, parens = TRUE) sh <- paste0(sh, " ", u) } - ans <- paste0(names(.spec), .sep, unname(sh)) + ans <- paste0(names(.spec), .sep, sh) if(isTRUE(.to_string)) { ans <- paste0(ans, collapse = .collapse) if(is.numeric(.width)) { diff --git a/man/ys_col_note.Rd b/man/ys_col_note.Rd index 67ce608..2ff936a 100644 --- a/man/ys_col_note.Rd +++ b/man/ys_col_note.Rd @@ -28,18 +28,18 @@ to the column definition.} passed through \code{\link[tools:toTitleCase]{tools::toTitleCase()}}.} \item{.sep}{a string to separate column name and column definition; -usually a space should be included as the terminal character (see default)} +usually a space should be included as the terminal character (see default).} -\item{.to_string}{logical; if \code{TRUE}, then a single string is returned} +\item{.to_string}{logical; if \code{TRUE}, then a single string is returned.} \item{.collapse}{a string to separate items when \code{.to_string} is \code{TRUE}; -usually a space should be included as the terminal character (see default)} +usually a space should be included as the terminal character (see default).} \item{.width}{if \code{numeric} and \code{.to_string} is \code{TRUE}, then the result is passed through \code{\link[base:strwrap]{base::strwrap()}}.} \item{.type}{selects if the column definition is generated from calling -\code{\link[=ys_get_short]{ys_get_short()}} or \code{\link[=ys_get_label]{ys_get_label()}}} +\code{\link[=ys_get_short]{ys_get_short()}} or \code{\link[=ys_get_label]{ys_get_label()}}.} } \value{ A string of length one when \code{.to_string} is \code{TRUE} or a character vector