From 1ff35a963e09a50a0fce535230b14a8c1629ba99 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:02:08 +0000 Subject: [PATCH 01/12] #2571 transform_scale: implement transform_scale() --- NAMESPACE | 2 + R/admiral-package.R | 10 +-- R/transform_scale.R | 109 +++++++++++++++++++++++++++++ man/compute_bmi.Rd | 3 +- man/compute_bsa.Rd | 3 +- man/compute_egfr.Rd | 3 +- man/compute_framingham.Rd | 3 +- man/compute_map.Rd | 3 +- man/compute_qtc.Rd | 3 +- man/compute_qual_imputation.Rd | 3 +- man/compute_qual_imputation_dec.Rd | 3 +- man/compute_rr.Rd | 3 +- man/compute_scale.Rd | 3 +- man/transform_scale.Rd | 81 +++++++++++++++++++++ 14 files changed, 217 insertions(+), 15 deletions(-) create mode 100644 R/transform_scale.R create mode 100644 man/transform_scale.Rd diff --git a/NAMESPACE b/NAMESPACE index 3548055f55..6b4f66d415 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -155,6 +155,7 @@ export(restrict_derivation) export(set_admiral_options) export(signal_duplicate_records) export(slice_derivation) +export(transform_scale) export(use_ad_template) export(yn_to_numeric) import(admiraldev) @@ -166,6 +167,7 @@ importFrom(cli,cli_text) importFrom(cli,cli_warn) importFrom(dplyr,across) importFrom(dplyr,arrange) +importFrom(dplyr,between) importFrom(dplyr,bind_cols) importFrom(dplyr,bind_rows) importFrom(dplyr,case_when) diff --git a/R/admiral-package.R b/R/admiral-package.R index 321fd90fee..f6866180ff 100644 --- a/R/admiral-package.R +++ b/R/admiral-package.R @@ -2,11 +2,11 @@ #' @family internal #' @import admiraldev #' @importFrom cli cli_abort ansi_collapse cli_div cli_inform cli_text cli_warn -#' @importFrom dplyr across arrange bind_cols bind_rows case_when coalesce -#' desc distinct ends_with everything filter first full_join -#' group_by group_by_at if_else mutate n n_distinct na_if pull -#' rename rename_with row_number select semi_join slice starts_with -#' summarise summarise_all tibble tribble ungroup union lag +#' @importFrom dplyr across arrange between bind_cols bind_rows case_when +#' coalesce desc distinct ends_with everything filter first full_join group_by +#' group_by_at if_else mutate n n_distinct na_if pull rename rename_with +#' row_number select semi_join slice starts_with summarise summarise_all +#' tibble tribble ungroup union lag #' @importFrom hms as_hms #' @importFrom lifecycle deprecate_warn deprecate_stop deprecated #' @importFrom lubridate %--% as_datetime ceiling_date date days duration diff --git a/R/transform_scale.R b/R/transform_scale.R new file mode 100644 index 0000000000..b3bd7a3e1a --- /dev/null +++ b/R/transform_scale.R @@ -0,0 +1,109 @@ +#' Tranform Scale Parameters +#' +#' Transforms results from the source range to the target range. For example, +#' for transforming source values 1, 2, 3, 4, 5 to 0, 25, 50, 75, 100. +#' +#' @param source A vector of values to be scaled +#' +#' A numeric vector is expected. +#' +#' @param source_range The permitted source range +#' +#' A numeric vector containing two elements is expected, representing the +#' lower and upper bounds of the permitted source range. +#' +#' @param target_range The target range +#' +#' A numeric vector containing two elements is expected, representing the +#' lower and upper bounds of the target range. +#' +#' @param flip_direction Flip direction of the scale? +#' +#' The transformed values will be reversed within the target range, e.g. +#' within the range 0 to 100, 25 would be reversed to 75. +#' +#' *Permitted Values*: `TRUE`, `FALSE` +#' +#' @param outside_range Handling of values outside the source range +#' +#' Values outside the source range (`source_range`) are transformed to `NA`. +#' +#' If `"warning"` or `"error"` is specified, a warning or error is issued if +#' `source` includes any values outside the source range. +#' +#' *Permitted Values*: `"NA"`, `"warning"`, `"error"` +#' +#' @details Returns the values of `source` linearly transformed from the source +#' range (`source_range`) to the target range (`target_range`). Values outside +#' the source range are set to `NA`. +#' +#' @return The source linearly transformed to the target range +#' +#' @keywords com_bds_findings +#' +#' @family com_bds_findings +#' +#' @export +#' +#' @examples +#' transform_scale( +#' source = c(1, 4, 3, 6, 5), +#' source_range = c(1, 5), +#' target_range = c(0, 100), +#' flip_direction = TRUE +#' ) +#' +transform_scale <- function(source, + source_range, + target_range, + flip_direction = FALSE, + outside_range = "NA") { + # Function argument checks + assert_numeric_vector(source) # nolint: undesirable_function_linter + assert_numeric_vector(source_range) + assert_numeric_vector(target_range) + assert_logical_scalar(flip_direction) + assert_character_scalar(outside_range, values = c("NA", "error", "warning")) + + outsider <- !between(source, source_range[[1]], source_range[[2]]) + if (any(outsider)) { + outside_index <- which(outsider) + outside_value <- source[outsider] + source <- if_else(outsider, NA, source) + msg <- c( + paste( + "{.arg source} contains values outside the range of {.val {source_range[[1]]}}", + "to {.val {source_range[[2]]}}:" + ), + paste0("source[[", outside_index, "]] = {.val {", outside_value, "}}") + ) + if (outside_range == "warning") { + cli_warn( + msg, + class = c("outside_source_range", "assert-admiral"), + outside_index = outside_index, + outside_value = outside_value + ) + } else if (outside_range == "error") { + cli_abort( + msg, + class = c("outside_source_range", "assert-admiral"), + outside_index = outside_index, + outside_value = outside_value + ) + } + } + + # Computation + scale_constant <- min(target_range) - min(source_range) + scale_coefficient <- (max(target_range) - min(target_range)) / + (max(source_range) - min(source_range)) + + target <- (source + scale_constant) * scale_coefficient + + if (flip_direction == TRUE) { + target <- max(target_range) - target + } + + target +} diff --git a/man/compute_bmi.Rd b/man/compute_bmi.Rd index bbe36fdcff..0c1d564b44 100644 --- a/man/compute_bmi.Rd +++ b/man/compute_bmi.Rd @@ -43,7 +43,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_bsa.Rd b/man/compute_bsa.Rd index 0a31db8d28..63d6b68e00 100644 --- a/man/compute_bsa.Rd +++ b/man/compute_bsa.Rd @@ -73,7 +73,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_egfr.Rd b/man/compute_egfr.Rd index 7495f0b5c1..a7a6dcc263 100644 --- a/man/compute_egfr.Rd +++ b/man/compute_egfr.Rd @@ -148,7 +148,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_framingham.Rd b/man/compute_framingham.Rd index 017188a58f..22057d7faa 100644 --- a/man/compute_framingham.Rd +++ b/man/compute_framingham.Rd @@ -124,7 +124,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_map.Rd b/man/compute_map.Rd index 4c28ac2a56..64d38a1ced 100644 --- a/man/compute_map.Rd +++ b/man/compute_map.Rd @@ -54,7 +54,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qtc.Rd b/man/compute_qtc.Rd index 766147d7da..d03ce33b63 100644 --- a/man/compute_qtc.Rd +++ b/man/compute_qtc.Rd @@ -58,7 +58,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qual_imputation.Rd b/man/compute_qual_imputation.Rd index 2c8ce68a8c..aad673868f 100644 --- a/man/compute_qual_imputation.Rd +++ b/man/compute_qual_imputation.Rd @@ -39,7 +39,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qtc}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qual_imputation_dec.Rd b/man/compute_qual_imputation_dec.Rd index 7aff94c8dc..000be650c8 100644 --- a/man/compute_qual_imputation_dec.Rd +++ b/man/compute_qual_imputation_dec.Rd @@ -36,7 +36,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qtc}()}, \code{\link{compute_qual_imputation}()}, \code{\link{compute_rr}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_rr.Rd b/man/compute_rr.Rd index 064f1fdc6d..a45cca9972 100644 --- a/man/compute_rr.Rd +++ b/man/compute_rr.Rd @@ -37,7 +37,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qtc}()}, \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, -\code{\link{compute_scale}()} +\code{\link{compute_scale}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_scale.Rd b/man/compute_scale.Rd index 742fb01e36..8a9d81a0aa 100644 --- a/man/compute_scale.Rd +++ b/man/compute_scale.Rd @@ -89,7 +89,8 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qtc}()}, \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, -\code{\link{compute_rr}()} +\code{\link{compute_rr}()}, +\code{\link{transform_scale}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/transform_scale.Rd b/man/transform_scale.Rd new file mode 100644 index 0000000000..816a175981 --- /dev/null +++ b/man/transform_scale.Rd @@ -0,0 +1,81 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/transform_scale.R +\name{transform_scale} +\alias{transform_scale} +\title{Tranform Scale Parameters} +\usage{ +transform_scale( + source, + source_range, + target_range, + flip_direction = FALSE, + outside_range = "NA" +) +} +\arguments{ +\item{source}{A vector of values to be scaled + +A numeric vector is expected.} + +\item{source_range}{The permitted source range + +A numeric vector containing two elements is expected, representing the +lower and upper bounds of the permitted source range.} + +\item{target_range}{The target range + +A numeric vector containing two elements is expected, representing the +lower and upper bounds of the target range.} + +\item{flip_direction}{Flip direction of the scale? + +The transformed values will be reversed within the target range, e.g. +within the range 0 to 100, 25 would be reversed to 75. + +\emph{Permitted Values}: \code{TRUE}, \code{FALSE}} + +\item{outside_range}{Handling of values outside the source range + +Values outside the source range (\code{source_range}) are transformed to \code{NA}. + +If \code{"warning"} or \code{"error"} is specified, a warning or error is issued if +\code{source} includes any values outside the source range. + +\emph{Permitted Values}: \code{"NA"}, \code{"warning"}, \code{"error"}} +} +\value{ +The source linearly transformed to the target range +} +\description{ +Transforms results from the source range to the target range. For example, +for transforming source values 1, 2, 3, 4, 5 to 0, 25, 50, 75, 100. +} +\details{ +Returns the values of \code{source} linearly transformed from the source +range (\code{source_range}) to the target range (\code{target_range}). Values outside +the source range are set to \code{NA}. +} +\examples{ +transform_scale( + source = c(1, 4, 3, 6, 5), + source_range = c(1, 5), + target_range = c(0, 100), + flip_direction = TRUE +) + +} +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_egfr}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_rr}()}, +\code{\link{compute_scale}()} +} +\concept{com_bds_findings} +\keyword{com_bds_findings} From ef742689f4538cf035347ab731ca36a8220f080a Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:09:14 +0000 Subject: [PATCH 02/12] #2571 transform_scale: update NEWS --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 5bd3cd8f67..cb0f41dd7e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,8 @@ `AVALCATx` & `AVALCAxN`. (#2480) - New function `derive_vars_crit_flag()` for deriving criterion flag variables (`CRITy`, `CRITyFL`, `CRITyFLN`). (#2468) +- New function `transform_scale()` to transform values from a source range to a +target range. (#2571) - Replace use of `data("sdtm")` with `sdtm <- pharmaverse::sdtm` in templates and vignettes. (#2498) - Remove `dthcaus_source()` calls in `ADSL` template because they are deprecated. (#2517) From 4d001df6e76c4a1d33d008422ab373d9182f1242 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:39:57 +0000 Subject: [PATCH 03/12] #2571 transform_scale: add tests --- R/transform_scale.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/transform_scale.R b/R/transform_scale.R index b3bd7a3e1a..babb02cbb0 100644 --- a/R/transform_scale.R +++ b/R/transform_scale.R @@ -65,7 +65,7 @@ transform_scale <- function(source, assert_logical_scalar(flip_direction) assert_character_scalar(outside_range, values = c("NA", "error", "warning")) - outsider <- !between(source, source_range[[1]], source_range[[2]]) + outsider <- !(between(source, source_range[[1]], source_range[[2]]) | is.na(source)) if (any(outsider)) { outside_index <- which(outsider) outside_value <- source[outsider] From 3df0083e59898d101eeb46d1e930b22bca173e83 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:44:15 +0000 Subject: [PATCH 04/12] ~#2571 transform_scale: update compute_scale() --- R/compute_scale.R | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/R/compute_scale.R b/R/compute_scale.R index 5aff81e62b..e5d2a20489 100644 --- a/R/compute_scale.R +++ b/R/compute_scale.R @@ -101,15 +101,12 @@ compute_scale <- function(source, target <- mean(source, na.rm = TRUE) # nolint: undesirable_function_linter if (!is.null(source_range) && !is.null(target_range)) { - scale_constant <- min(target_range) - min(source_range) - scale_coefficient <- (max(target_range) - min(target_range)) / - (max(source_range) - min(source_range)) - - target <- (target + scale_constant) * scale_coefficient - - if (flip_direction == TRUE) { - target <- max(target_range) - target - } + target <- transform_scale( + target, + source_range = source_range, + target_range = target_range, + flip_direction = flip_direction + ) } } else { target <- NA From e8835f775ff9b6262c100846c162428705dd7b3f Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:55:49 +0000 Subject: [PATCH 05/12] ~#2571 transform_scale: rename to transform_range() --- NAMESPACE | 2 +- R/compute_scale.R | 2 +- R/{transform_scale.R => transform_range.R} | 16 +++--- man/compute_bmi.Rd | 2 +- man/compute_bsa.Rd | 2 +- man/compute_egfr.Rd | 2 +- man/compute_framingham.Rd | 2 +- man/compute_map.Rd | 2 +- man/compute_qtc.Rd | 2 +- man/compute_qual_imputation.Rd | 2 +- man/compute_qual_imputation_dec.Rd | 2 +- man/compute_rr.Rd | 2 +- man/compute_scale.Rd | 2 +- ...{transform_scale.Rd => transform_range.Rd} | 16 +++--- tests/testthat/_snaps/transform_range.md | 23 +++++++++ tests/testthat/_snaps/transform_scale.md | 23 +++++++++ tests/testthat/test-transform_range.R | 49 +++++++++++++++++++ 17 files changed, 123 insertions(+), 28 deletions(-) rename R/{transform_scale.R => transform_range.R} (89%) rename man/{transform_scale.Rd => transform_range.Rd} (88%) create mode 100644 tests/testthat/_snaps/transform_range.md create mode 100644 tests/testthat/_snaps/transform_scale.md create mode 100644 tests/testthat/test-transform_range.R diff --git a/NAMESPACE b/NAMESPACE index 6b4f66d415..58d568c0ab 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -155,7 +155,7 @@ export(restrict_derivation) export(set_admiral_options) export(signal_duplicate_records) export(slice_derivation) -export(transform_scale) +export(transform_range) export(use_ad_template) export(yn_to_numeric) import(admiraldev) diff --git a/R/compute_scale.R b/R/compute_scale.R index e5d2a20489..a551bdcecc 100644 --- a/R/compute_scale.R +++ b/R/compute_scale.R @@ -101,7 +101,7 @@ compute_scale <- function(source, target <- mean(source, na.rm = TRUE) # nolint: undesirable_function_linter if (!is.null(source_range) && !is.null(target_range)) { - target <- transform_scale( + target <- transform_range( target, source_range = source_range, target_range = target_range, diff --git a/R/transform_scale.R b/R/transform_range.R similarity index 89% rename from R/transform_scale.R rename to R/transform_range.R index babb02cbb0..26a60fde3d 100644 --- a/R/transform_scale.R +++ b/R/transform_range.R @@ -1,9 +1,9 @@ -#' Tranform Scale Parameters +#' Tranform Range #' #' Transforms results from the source range to the target range. For example, #' for transforming source values 1, 2, 3, 4, 5 to 0, 25, 50, 75, 100. #' -#' @param source A vector of values to be scaled +#' @param source A vector of values to be transformed #' #' A numeric vector is expected. #' @@ -17,7 +17,7 @@ #' A numeric vector containing two elements is expected, representing the #' lower and upper bounds of the target range. #' -#' @param flip_direction Flip direction of the scale? +#' @param flip_direction Flip direction of the range? #' #' The transformed values will be reversed within the target range, e.g. #' within the range 0 to 100, 25 would be reversed to 75. @@ -46,14 +46,14 @@ #' @export #' #' @examples -#' transform_scale( +#' transform_range( #' source = c(1, 4, 3, 6, 5), #' source_range = c(1, 5), #' target_range = c(0, 100), #' flip_direction = TRUE #' ) #' -transform_scale <- function(source, +transform_range <- function(source, source_range, target_range, flip_direction = FALSE, @@ -95,11 +95,11 @@ transform_scale <- function(source, } # Computation - scale_constant <- min(target_range) - min(source_range) - scale_coefficient <- (max(target_range) - min(target_range)) / + range_constant <- min(target_range) - min(source_range) + range_coefficient <- (max(target_range) - min(target_range)) / (max(source_range) - min(source_range)) - target <- (source + scale_constant) * scale_coefficient + target <- (source + range_constant) * range_coefficient if (flip_direction == TRUE) { target <- max(target_range) - target diff --git a/man/compute_bmi.Rd b/man/compute_bmi.Rd index 0c1d564b44..68a12c0248 100644 --- a/man/compute_bmi.Rd +++ b/man/compute_bmi.Rd @@ -44,7 +44,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_bsa.Rd b/man/compute_bsa.Rd index 63d6b68e00..f16172a3ef 100644 --- a/man/compute_bsa.Rd +++ b/man/compute_bsa.Rd @@ -74,7 +74,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_egfr.Rd b/man/compute_egfr.Rd index a7a6dcc263..715f956b06 100644 --- a/man/compute_egfr.Rd +++ b/man/compute_egfr.Rd @@ -149,7 +149,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_framingham.Rd b/man/compute_framingham.Rd index 22057d7faa..fb1894041d 100644 --- a/man/compute_framingham.Rd +++ b/man/compute_framingham.Rd @@ -125,7 +125,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_map.Rd b/man/compute_map.Rd index 64d38a1ced..67980bf21c 100644 --- a/man/compute_map.Rd +++ b/man/compute_map.Rd @@ -55,7 +55,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qtc.Rd b/man/compute_qtc.Rd index d03ce33b63..32c3dd0dc6 100644 --- a/man/compute_qtc.Rd +++ b/man/compute_qtc.Rd @@ -59,7 +59,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qual_imputation.Rd b/man/compute_qual_imputation.Rd index aad673868f..e85e199c54 100644 --- a/man/compute_qual_imputation.Rd +++ b/man/compute_qual_imputation.Rd @@ -40,7 +40,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_qual_imputation_dec.Rd b/man/compute_qual_imputation_dec.Rd index 000be650c8..d8e3965cad 100644 --- a/man/compute_qual_imputation_dec.Rd +++ b/man/compute_qual_imputation_dec.Rd @@ -37,7 +37,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_rr}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_rr.Rd b/man/compute_rr.Rd index a45cca9972..8215947825 100644 --- a/man/compute_rr.Rd +++ b/man/compute_rr.Rd @@ -38,7 +38,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_scale}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/compute_scale.Rd b/man/compute_scale.Rd index 8a9d81a0aa..c81eeb59d5 100644 --- a/man/compute_scale.Rd +++ b/man/compute_scale.Rd @@ -90,7 +90,7 @@ BDS-Findings Functions that returns a vector: \code{\link{compute_qual_imputation}()}, \code{\link{compute_qual_imputation_dec}()}, \code{\link{compute_rr}()}, -\code{\link{transform_scale}()} +\code{\link{transform_range}()} } \concept{com_bds_findings} \keyword{com_bds_findings} diff --git a/man/transform_scale.Rd b/man/transform_range.Rd similarity index 88% rename from man/transform_scale.Rd rename to man/transform_range.Rd index 816a175981..6d574dfbaa 100644 --- a/man/transform_scale.Rd +++ b/man/transform_range.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/transform_scale.R -\name{transform_scale} -\alias{transform_scale} -\title{Tranform Scale Parameters} +% Please edit documentation in R/transform_range.R +\name{transform_range} +\alias{transform_range} +\title{Tranform Range} \usage{ -transform_scale( +transform_range( source, source_range, target_range, @@ -13,7 +13,7 @@ transform_scale( ) } \arguments{ -\item{source}{A vector of values to be scaled +\item{source}{A vector of values to be transformed A numeric vector is expected.} @@ -27,7 +27,7 @@ lower and upper bounds of the permitted source range.} A numeric vector containing two elements is expected, representing the lower and upper bounds of the target range.} -\item{flip_direction}{Flip direction of the scale? +\item{flip_direction}{Flip direction of the range? The transformed values will be reversed within the target range, e.g. within the range 0 to 100, 25 would be reversed to 75. @@ -56,7 +56,7 @@ range (\code{source_range}) to the target range (\code{target_range}). Values ou the source range are set to \code{NA}. } \examples{ -transform_scale( +transform_range( source = c(1, 4, 3, 6, 5), source_range = c(1, 5), target_range = c(0, 100), diff --git a/tests/testthat/_snaps/transform_range.md b/tests/testthat/_snaps/transform_range.md new file mode 100644 index 0000000000..372900873e --- /dev/null +++ b/tests/testthat/_snaps/transform_range.md @@ -0,0 +1,23 @@ +# transform_range Test 3: warning if outside range + + Code + transform_range(c(5, 1, 6, 2, NA), source_range = c(1, 5), target_range = c(0, + 100), outside_range = "warning") + Condition + Warning: + `source` contains values outside the range of 1 to 5: + source[[3]] = 6 + Output + [1] 100 0 NA 25 NA + +# transform_range Test 4: error if outside range + + Code + transform_range(c(5, 1, 6, 2, 7), source_range = c(1, 5), target_range = c(0, + 100), outside_range = "error") + Condition + Error in `transform_range()`: + ! `source` contains values outside the range of 1 to 5: + source[[3]] = 6 + source[[5]] = 7 + diff --git a/tests/testthat/_snaps/transform_scale.md b/tests/testthat/_snaps/transform_scale.md new file mode 100644 index 0000000000..84b06cb947 --- /dev/null +++ b/tests/testthat/_snaps/transform_scale.md @@ -0,0 +1,23 @@ +# transform_scale Test 3: warning if outside range + + Code + transform_scale(c(5, 1, 6, 2, NA), source_range = c(1, 5), target_range = c(0, + 100), outside_range = "warning") + Condition + Warning: + `source` contains values outside the range of 1 to 5: + source[[3]] = 6 + Output + [1] 100 0 NA 25 NA + +# transform_scale Test 4: error if outside range + + Code + transform_scale(c(5, 1, 6, 2, 7), source_range = c(1, 5), target_range = c(0, + 100), outside_range = "error") + Condition + Error in `transform_scale()`: + ! `source` contains values outside the range of 1 to 5: + source[[3]] = 6 + source[[5]] = 7 + diff --git a/tests/testthat/test-transform_range.R b/tests/testthat/test-transform_range.R new file mode 100644 index 0000000000..48105826c9 --- /dev/null +++ b/tests/testthat/test-transform_range.R @@ -0,0 +1,49 @@ +## Test 1: works as expected ---- +test_that("transform_range Test 1: works as expected", { + expect_equal( + transform_range( + c(5, 1, 6, 2, NA), + source_range = c(1, 5), + target_range = c(0, 100) + ), + c(100, 0, NA, 25, NA) + ) +}) + +## Test 2: range is flipped if flip_direction == TRUE ---- +test_that("transform_range Test 2: range is flipped if flip_direction == TRUE", { + expect_equal( + transform_range( + c(0, 4, 8, 11), + c(0, 10), + c(0, 100), + flip_direction = TRUE + ), + c(100, 60, 20, NA) + ) +}) + +## Test 3: warning if outside range ---- +test_that("transform_range Test 3: warning if outside range", { + expect_snapshot( + transform_range( + c(5, 1, 6, 2, NA), + source_range = c(1, 5), + target_range = c(0, 100), + outside_range = "warning" + ) + ) +}) + +## Test 4: error if outside range ---- +test_that("transform_range Test 4: error if outside range", { + expect_snapshot( + transform_range( + c(5, 1, 6, 2, 7), + source_range = c(1, 5), + target_range = c(0, 100), + outside_range = "error" + ), + error = TRUE + ) +}) From 2f8aee612f9b175d6a7b7f3c4caba7e7befaebf3 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 18:59:51 +0000 Subject: [PATCH 06/12] #2571 transform_range: style file --- R/transform_range.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/transform_range.R b/R/transform_range.R index 26a60fde3d..c952015df9 100644 --- a/R/transform_range.R +++ b/R/transform_range.R @@ -83,7 +83,7 @@ transform_range <- function(source, class = c("outside_source_range", "assert-admiral"), outside_index = outside_index, outside_value = outside_value - ) + ) } else if (outside_range == "error") { cli_abort( msg, @@ -97,13 +97,13 @@ transform_range <- function(source, # Computation range_constant <- min(target_range) - min(source_range) range_coefficient <- (max(target_range) - min(target_range)) / - (max(source_range) - min(source_range)) + (max(source_range) - min(source_range)) target <- (source + range_constant) * range_coefficient - if (flip_direction == TRUE) { - target <- max(target_range) - target - } + if (flip_direction == TRUE) { + target <- max(target_range) - target + } target } From 09d1280d35c03b356d3e4c20b0ac94684f93bf16 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 19:02:09 +0000 Subject: [PATCH 07/12] #2571 transform_range: fix spelling --- R/transform_range.R | 2 +- man/transform_range.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/transform_range.R b/R/transform_range.R index c952015df9..10097036d7 100644 --- a/R/transform_range.R +++ b/R/transform_range.R @@ -1,4 +1,4 @@ -#' Tranform Range +#' Transform Range #' #' Transforms results from the source range to the target range. For example, #' for transforming source values 1, 2, 3, 4, 5 to 0, 25, 50, 75, 100. diff --git a/man/transform_range.Rd b/man/transform_range.Rd index 6d574dfbaa..0f6a85bbe4 100644 --- a/man/transform_range.Rd +++ b/man/transform_range.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/transform_range.R \name{transform_range} \alias{transform_range} -\title{Tranform Range} +\title{Transform Range} \usage{ transform_range( source, From 065a33825c3ef0dc718de8838947f3c0f1e58e14 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Fri, 29 Nov 2024 19:06:22 +0000 Subject: [PATCH 08/12] #2571 transform_range: style file --- tests/testthat/test-transform_range.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-transform_range.R b/tests/testthat/test-transform_range.R index 48105826c9..720243783b 100644 --- a/tests/testthat/test-transform_range.R +++ b/tests/testthat/test-transform_range.R @@ -3,8 +3,8 @@ test_that("transform_range Test 1: works as expected", { expect_equal( transform_range( c(5, 1, 6, 2, NA), - source_range = c(1, 5), - target_range = c(0, 100) + source_range = c(1, 5), + target_range = c(0, 100) ), c(100, 0, NA, 25, NA) ) @@ -15,9 +15,9 @@ test_that("transform_range Test 2: range is flipped if flip_direction == TRUE", expect_equal( transform_range( c(0, 4, 8, 11), - c(0, 10), - c(0, 100), - flip_direction = TRUE + c(0, 10), + c(0, 100), + flip_direction = TRUE ), c(100, 60, 20, NA) ) From e038f381d72711e20aea568cd61d866c2410fc71 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Mon, 2 Dec 2024 12:55:28 +0000 Subject: [PATCH 09/12] #2571 transform_range: fix lintr --- .lintr | 2 +- R/compute_scale.R | 6 +++--- R/transform_range.R | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.lintr b/.lintr index 48c0ac8110..f51e013a38 100644 --- a/.lintr +++ b/.lintr @@ -3,7 +3,7 @@ linters: linters_with_defaults( object_usage_linter=NULL, cyclocomp_linter(complexity_limit = 22), indentation_linter=NULL, - undesirable_function_linter = undesirable_function_linter() + undesirable_function_linter = undesirable_function_linter(symbol_is_undesirable = FALSE) ) exclusions: list( "R/data.R" = Inf, diff --git a/R/compute_scale.R b/R/compute_scale.R index a551bdcecc..b7f6b85857 100644 --- a/R/compute_scale.R +++ b/R/compute_scale.R @@ -74,7 +74,7 @@ compute_scale <- function(source, flip_direction = FALSE, min_n = 1) { # Function argument checks - assert_numeric_vector(source) # nolint: undesirable_function_linter + assert_numeric_vector(source) assert_numeric_vector(source_range, optional = TRUE) if (!is.null(target_range) && is.null(source_range)) { cli_abort( @@ -97,8 +97,8 @@ compute_scale <- function(source, assert_integer_scalar(min_n, subset = "positive") # Computation - if (sum(!is.na(source)) >= min_n) { # nolint: undesirable_function_linter - target <- mean(source, na.rm = TRUE) # nolint: undesirable_function_linter + if (sum(!is.na(source)) >= min_n) { + target <- mean(source, na.rm = TRUE) if (!is.null(source_range) && !is.null(target_range)) { target <- transform_range( diff --git a/R/transform_range.R b/R/transform_range.R index 10097036d7..e6d60c9b59 100644 --- a/R/transform_range.R +++ b/R/transform_range.R @@ -59,7 +59,7 @@ transform_range <- function(source, flip_direction = FALSE, outside_range = "NA") { # Function argument checks - assert_numeric_vector(source) # nolint: undesirable_function_linter + assert_numeric_vector(source) assert_numeric_vector(source_range) assert_numeric_vector(target_range) assert_logical_scalar(flip_direction) From 8104ca547bfba71c58452676405446bc7f3520ac Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Tue, 3 Dec 2024 13:40:28 +0000 Subject: [PATCH 10/12] #2571 transform_range: update questionnaires vignette and check length of ranges --- R/compute_scale.R | 4 ++-- R/transform_range.R | 11 ++++++++--- man/transform_range.Rd | 7 ++++++- vignettes/questionnaires.Rmd | 3 ++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/R/compute_scale.R b/R/compute_scale.R index b7f6b85857..6f49014866 100644 --- a/R/compute_scale.R +++ b/R/compute_scale.R @@ -75,7 +75,7 @@ compute_scale <- function(source, min_n = 1) { # Function argument checks assert_numeric_vector(source) - assert_numeric_vector(source_range, optional = TRUE) + assert_numeric_vector(source_range, length = 2, optional = TRUE) if (!is.null(target_range) && is.null(source_range)) { cli_abort( c("Argument {.arg source_range} is missing with no default @@ -84,7 +84,7 @@ compute_scale <- function(source, ) ) } - assert_numeric_vector(target_range, optional = TRUE) + assert_numeric_vector(target_range, length = 2, optional = TRUE) if (!is.null(source_range) && is.null(target_range)) { cli_abort( c("Argument {.arg target_range} is missing with no default diff --git a/R/transform_range.R b/R/transform_range.R index e6d60c9b59..545226a37e 100644 --- a/R/transform_range.R +++ b/R/transform_range.R @@ -49,10 +49,15 @@ #' transform_range( #' source = c(1, 4, 3, 6, 5), #' source_range = c(1, 5), +#' target_range = c(0, 100) +#' ) +#' +#' transform_range( +#' source = c(1, 4, 3, 6, 5), +#' source_range = c(1, 5), #' target_range = c(0, 100), #' flip_direction = TRUE #' ) -#' transform_range <- function(source, source_range, target_range, @@ -60,8 +65,8 @@ transform_range <- function(source, outside_range = "NA") { # Function argument checks assert_numeric_vector(source) - assert_numeric_vector(source_range) - assert_numeric_vector(target_range) + assert_numeric_vector(source_range, length = 2) + assert_numeric_vector(target_range, length = 2) assert_logical_scalar(flip_direction) assert_character_scalar(outside_range, values = c("NA", "error", "warning")) diff --git a/man/transform_range.Rd b/man/transform_range.Rd index 0f6a85bbe4..af99bfa2f3 100644 --- a/man/transform_range.Rd +++ b/man/transform_range.Rd @@ -56,13 +56,18 @@ range (\code{source_range}) to the target range (\code{target_range}). Values ou the source range are set to \code{NA}. } \examples{ +transform_range( + source = c(1, 4, 3, 6, 5), + source_range = c(1, 5), + target_range = c(0, 100) +) + transform_range( source = c(1, 4, 3, 6, 5), source_range = c(1, 5), target_range = c(0, 100), flip_direction = TRUE ) - } \seealso{ BDS-Findings Functions that returns a vector: diff --git a/vignettes/questionnaires.Rmd b/vignettes/questionnaires.Rmd index 165318f0f0..7c44e01942 100644 --- a/vignettes/questionnaires.Rmd +++ b/vignettes/questionnaires.Rmd @@ -123,7 +123,8 @@ Depending on the question, `QSORRES == "YES"` is mapped to `QSSTRESN = 0` or `QSSTRESN = 1`. If the `QSSTRESN` values are not ready to be used for deriving scores and require transformation, it is recommended that `QSSTRESN` is kept in the ADaM dataset for traceability, and the transformed value is stored in -`AVAL`, since that's what will be used for the score calculation. +`AVAL`, since that's what will be used for the score calculation. The +computation function `transform_range()` can be used for the transformation. # Scales and Scores From f9890f836a13755e8eae032f827e49b5d629f48c Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Tue, 3 Dec 2024 16:04:31 +0000 Subject: [PATCH 11/12] #2571 transform_range: update questionnaires vignette --- vignettes/questionnaires.Rmd | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/vignettes/questionnaires.Rmd b/vignettes/questionnaires.Rmd index 7c44e01942..983211dbb1 100644 --- a/vignettes/questionnaires.Rmd +++ b/vignettes/questionnaires.Rmd @@ -117,14 +117,21 @@ We handle unscheduled visits as normal visits. For deriving visits based on time-windows, see [Visit and Period Variables](visits_periods.html#visits). And for flagging values to be used for analysis, see `derive_var_extreme_flag()`. +# Transformed Items + Please note that in the example data, the numeric values of the answers are mapped in SDTM (`QSSTRESN`) such that they can be used for deriving scores. Depending on the question, `QSORRES == "YES"` is mapped to `QSSTRESN = 0` or `QSSTRESN = 1`. If the `QSSTRESN` values are not ready to be used for deriving scores and require transformation, it is recommended that `QSSTRESN` is kept in the ADaM dataset for traceability, and the transformed value is stored in -`AVAL`, since that's what will be used for the score calculation. The -computation function `transform_range()` can be used for the transformation. +`AVAL`, since that's what will be used for the score calculation. + +It may also be necessary to transform the range of the numeric values of the +original items. For example if a scale should be derived as the average but the +range of the contributing items varies. In this case the values could be +linearly transformed to a unified range like `[0, 100]`. The computation +function `transform_range()` can be used for the transformation. # Scales and Scores From bce1cb17f54f4c18cb5f11cd49bf4f3809583f30 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Tue, 3 Dec 2024 16:11:39 +0000 Subject: [PATCH 12/12] #2571 transform_range: remove unnecessary snapshot --- tests/testthat/_snaps/transform_scale.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 tests/testthat/_snaps/transform_scale.md diff --git a/tests/testthat/_snaps/transform_scale.md b/tests/testthat/_snaps/transform_scale.md deleted file mode 100644 index 84b06cb947..0000000000 --- a/tests/testthat/_snaps/transform_scale.md +++ /dev/null @@ -1,23 +0,0 @@ -# transform_scale Test 3: warning if outside range - - Code - transform_scale(c(5, 1, 6, 2, NA), source_range = c(1, 5), target_range = c(0, - 100), outside_range = "warning") - Condition - Warning: - `source` contains values outside the range of 1 to 5: - source[[3]] = 6 - Output - [1] 100 0 NA 25 NA - -# transform_scale Test 4: error if outside range - - Code - transform_scale(c(5, 1, 6, 2, 7), source_range = c(1, 5), target_range = c(0, - 100), outside_range = "error") - Condition - Error in `transform_scale()`: - ! `source` contains values outside the range of 1 to 5: - source[[3]] = 6 - source[[5]] = 7 -