diff --git a/DESCRIPTION b/DESCRIPTION index 0bca46308..6baa6e526 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -21,7 +21,6 @@ Depends: Imports: checkmate, digest, - formatters (>= 0.3.1), lifecycle, logger (>= 0.2.0), methods, diff --git a/NAMESPACE b/NAMESPACE index 6e6105229..9ac8192cd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -55,6 +55,7 @@ S3method(to_relational_data,TealDataset) S3method(to_relational_data,TealDatasetConnector) S3method(to_relational_data,data.frame) S3method(to_relational_data,list) +export("col_labels<-") export("data_label<-") export(as_cdisc) export(callable_code) @@ -68,6 +69,8 @@ export(cdisc_dataset_connector_file) export(cdisc_dataset_file) export(code_cdisc_dataset_connector) export(code_dataset_connector) +export(col_labels) +export(col_relabel) export(csv_cdisc_dataset_connector) export(csv_dataset_connector) export(data_connection) @@ -116,7 +119,6 @@ export(to_relational_data) export(validate_metadata) import(shiny) importFrom(digest,digest) -importFrom(formatters,var_labels) importFrom(logger,log_trace) importFrom(shinyjs,show) importFrom(stats,setNames) diff --git a/R/CodeClass.R b/R/CodeClass.R index 570b8086d..1db297191 100644 --- a/R/CodeClass.R +++ b/R/CodeClass.R @@ -86,7 +86,7 @@ CodeClass <- R6::R6Class( # nolint #' @description #' Set code in form of character #' @param code (`character`) vector of code text to be set - #' @param `dataname` optional, (`character`) vector of `datanames` to assign code to. If empty then the code + #' @param dataname optional, (`character`) vector of `datanames` to assign code to. If empty then the code #' is considered to be "global" #' @param deps optional, (`character`) vector of `datanames` that given code depends on #' @@ -106,7 +106,7 @@ CodeClass <- R6::R6Class( # nolint }, #' @description #' Get the code for a given data names - #' @param `dataname` optional, (`character`) vector of `datanames` for which the code is extracted. + #' @param dataname optional, (`character`) vector of `datanames` for which the code is extracted. #' If `NULL` then get the code for all data names #' @param deparse optional, (`logical`) whether to return the deparsed form of a call #' @return `character` or `list` of calls diff --git a/R/TealDataset.R b/R/TealDataset.R index 285fc5885..0d32b2ec0 100644 --- a/R/TealDataset.R +++ b/R/TealDataset.R @@ -184,7 +184,7 @@ TealDataset <- R6::R6Class( # nolint #' Derive the column labels #' @return `character` vector. get_column_labels = function() { - formatters::var_labels(private$.raw_data, fill = FALSE) + col_labels(private$.raw_data, fill = FALSE) }, #' @description #' Get the number of columns of the data diff --git a/R/formatters_var_labels.R b/R/formatters_var_labels.R new file mode 100644 index 000000000..c3075c107 --- /dev/null +++ b/R/formatters_var_labels.R @@ -0,0 +1,152 @@ +#' Get Label Attributes of Variables in a \code{data.frame} +#' +#' Variable labels can be stored as a \code{label} attribute for each variable. +#' This functions returns a named character vector with the variable labels +#' (empty sting if not specified) +#' +#' @param x a \code{data.frame} object +#' @param fill boolean in case the \code{label} attribute does not exist if +#' \code{TRUE} the variable names is returned, otherwise \code{NA} +#' +#' @source This function was taken 1-1 from +#' \href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +#' the dependency tree. +#' +#' @seealso [col_relabel()] [`col_labels<-`] +#' +#' @return a named character vector with the variable labels, the names +#' correspond to the variable names +#' +#' @export +#' +#' @examples +#' x <- iris +#' col_labels(x) +#' col_labels(x) <- paste("label for", names(iris)) +#' col_labels(x) +col_labels <- function(x, fill = FALSE) { + stopifnot(is.data.frame(x)) + if (NCOL(x) == 0) { + return(character()) + } + + y <- Map(function(col, colname) { + label <- attr(col, "label") + + if (is.null(label)) { + if (fill) { + colname + } else { + NA_character_ + } + } else { + if (!is.character(label) && !(length(label) == 1)) { + stop("label for variable ", colname, "is not a character string") + } + as.vector(label) + } + }, x, colnames(x)) + + labels <- unlist(y, recursive = FALSE, use.names = TRUE) + + if (!is.character(labels)) { + stop("label extraction failed") + } + + labels +} + +#' Set Label Attributes of All Variables in a \code{data.frame} +#' +#' Variable labels can be stored as a \code{label} attribute for each variable. +#' This functions sets all non-missing (non-NA) variable labels in a \code{data.frame} +#' +#' @inheritParams col_labels +#' @param value new variable labels, \code{NA} removes the variable label +#' +#' @source This function was taken 1-1 from +#' \href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +#' the dependency tree. +#' +#' @seealso [col_labels()] [col_relabel()] +#' +#' @return modifies the variable labels of \code{x} +#' +#' @export +#' +#' @examples +#' x <- iris +#' col_labels(x) +#' col_labels(x) <- paste("label for", names(iris)) +#' col_labels(x) +#' +#' if (interactive()) { +#' View(x) # in RStudio data viewer labels are displayed +#' } +`col_labels<-` <- function(x, value) { + stopifnot( + is.data.frame(x), + is.character(value), + ncol(x) == length(value) + ) + + theseq <- if (!is.null(names(value))) names(value) else seq_along(x) + # across columns of x + for (j in theseq) { + attr(x[[j]], "label") <- if (!is.na(value[j])) { + value[j] + } else { + NULL + } + } + + x +} + +#' Copy and Change Variable Labels of a \code{data.frame} +#' +#' Relabel a subset of the variables +#' +#' @inheritParams col_labels<- +#' @param ... name-value pairs, where name corresponds to a variable name in +#' \code{x} and the value to the new variable label +#' +#' @return a copy of \code{x} with changed labels according to \code{...} +#' +#' @source This function was taken 1-1 from +#' \href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +#' the dependency tree. +#' +#' @seealso [col_labels()] [`col_labels<-`] +#' +#' @export +#' +#' @examples +#' x <- col_relabel(iris, Sepal.Length = "Sepal Length of iris flower") +#' col_labels(x) +#' +col_relabel <- function(x, ...) { + stopifnot(is.data.frame(x)) + if (missing(...)) { + return(x) + } + dots <- list(...) + varnames <- names(dots) + stopifnot(!is.null(varnames)) + + map_varnames <- match(varnames, colnames(x)) + + if (any(is.na(map_varnames))) { + stop("variables: ", paste(varnames[is.na(map_varnames)], collapse = ", "), " not found") + } + + if (any(vapply(dots, Negate(is.character), logical(1)))) { + stop("all variable labels must be of type character") + } + + for (i in seq_along(map_varnames)) { + attr(x[[map_varnames[[i]]]], "label") <- dots[[i]] + } + + x +} diff --git a/R/teal.data.R b/R/teal.data.R index 73f08c960..83037785d 100644 --- a/R/teal.data.R +++ b/R/teal.data.R @@ -15,5 +15,4 @@ #' @importFrom stats setNames #' @importFrom shinyjs show #' @importFrom logger log_trace -#' @importFrom formatters var_labels NULL diff --git a/_pkgdown.yml b/_pkgdown.yml index 102c6da73..1557baf6d 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -100,6 +100,9 @@ reference: - title: Helpers desc: Other useful functions for users and developers. contents: + - col_labels + - col_labels<- + - col_relabel - data_label - data_label<- - example_cdisc_data diff --git a/inst/WORDLIST b/inst/WORDLIST index bb8a2852e..603e469f0 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -11,3 +11,4 @@ iteratively pre repo reproducibility +formatters diff --git a/man/CodeClass.Rd b/man/CodeClass.Rd index 4918fdcba..068fb7e24 100644 --- a/man/CodeClass.Rd +++ b/man/CodeClass.Rd @@ -138,10 +138,10 @@ Set code in form of character \describe{ \item{\code{code}}{(\code{character}) vector of code text to be set} -\item{\code{deps}}{optional, (\code{character}) vector of \code{datanames} that given code depends on} - -\item{\code{`dataname`}}{optional, (\code{character}) vector of \code{datanames} to assign code to. If empty then the code +\item{\code{dataname}}{optional, (\code{character}) vector of \code{datanames} to assign code to. If empty then the code is considered to be "global"} + +\item{\code{deps}}{optional, (\code{character}) vector of \code{datanames} that given code depends on} } \if{html}{\out{}} } @@ -161,10 +161,10 @@ Get the code for a given data names \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{deparse}}{optional, (\code{logical}) whether to return the deparsed form of a call} - -\item{\code{`dataname`}}{optional, (\code{character}) vector of \code{datanames} for which the code is extracted. +\item{\code{dataname}}{optional, (\code{character}) vector of \code{datanames} for which the code is extracted. If \code{NULL} then get the code for all data names} + +\item{\code{deparse}}{optional, (\code{logical}) whether to return the deparsed form of a call} } \if{html}{\out{
}} } diff --git a/man/col_labels-set.Rd b/man/col_labels-set.Rd new file mode 100644 index 000000000..5781b0659 --- /dev/null +++ b/man/col_labels-set.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/formatters_var_labels.R +\name{col_labels<-} +\alias{col_labels<-} +\title{Set Label Attributes of All Variables in a \code{data.frame}} +\source{ +This function was taken 1-1 from +\href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +the dependency tree. +} +\usage{ +col_labels(x) <- value +} +\arguments{ +\item{x}{a \code{data.frame} object} + +\item{value}{new variable labels, \code{NA} removes the variable label} +} +\value{ +modifies the variable labels of \code{x} +} +\description{ +Variable labels can be stored as a \code{label} attribute for each variable. +This functions sets all non-missing (non-NA) variable labels in a \code{data.frame} +} +\examples{ +x <- iris +col_labels(x) +col_labels(x) <- paste("label for", names(iris)) +col_labels(x) + +if (interactive()) { + View(x) # in RStudio data viewer labels are displayed +} +} +\seealso{ +\code{\link[=col_labels]{col_labels()}} \code{\link[=col_relabel]{col_relabel()}} +} diff --git a/man/col_labels.Rd b/man/col_labels.Rd new file mode 100644 index 000000000..6561ef04a --- /dev/null +++ b/man/col_labels.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/formatters_var_labels.R +\name{col_labels} +\alias{col_labels} +\title{Get Label Attributes of Variables in a \code{data.frame}} +\source{ +This function was taken 1-1 from +\href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +the dependency tree. +} +\usage{ +col_labels(x, fill = FALSE) +} +\arguments{ +\item{x}{a \code{data.frame} object} + +\item{fill}{boolean in case the \code{label} attribute does not exist if +\code{TRUE} the variable names is returned, otherwise \code{NA}} +} +\value{ +a named character vector with the variable labels, the names +correspond to the variable names +} +\description{ +Variable labels can be stored as a \code{label} attribute for each variable. +This functions returns a named character vector with the variable labels +(empty sting if not specified) +} +\examples{ +x <- iris +col_labels(x) +col_labels(x) <- paste("label for", names(iris)) +col_labels(x) +} +\seealso{ +\code{\link[=col_relabel]{col_relabel()}} \code{\link{col_labels<-}} +} diff --git a/man/col_relabel.Rd b/man/col_relabel.Rd new file mode 100644 index 000000000..df1497e02 --- /dev/null +++ b/man/col_relabel.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/formatters_var_labels.R +\name{col_relabel} +\alias{col_relabel} +\title{Copy and Change Variable Labels of a \code{data.frame}} +\source{ +This function was taken 1-1 from +\href{https://cran.r-project.org/web/packages/formatters/index.html}{formatters} package, to reduce the complexity of +the dependency tree. +} +\usage{ +col_relabel(x, ...) +} +\arguments{ +\item{x}{a \code{data.frame} object} + +\item{...}{name-value pairs, where name corresponds to a variable name in +\code{x} and the value to the new variable label} +} +\value{ +a copy of \code{x} with changed labels according to \code{...} +} +\description{ +Relabel a subset of the variables +} +\examples{ +x <- col_relabel(iris, Sepal.Length = "Sepal Length of iris flower") +col_labels(x) + +} +\seealso{ +\code{\link[=col_labels]{col_labels()}} \code{\link{col_labels<-}} +} diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index aa1758cea..4cdc94883 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -6,9 +6,6 @@ upstream_repos: insightsengineering/teal.logger: repo: insightsengineering/teal.logger host: https://github.com - insightsengineering/formatters: - repo: insightsengineering/formatters - host: https://github.com downstream_repos: insightsengineering/teal.slice: repo: insightsengineering/teal.slice diff --git a/tests/testthat/test-CDISCTealDataset.R b/tests/testthat/test-CDISCTealDataset.R index 715b61dda..96c01c466 100644 --- a/tests/testthat/test-CDISCTealDataset.R +++ b/tests/testthat/test-CDISCTealDataset.R @@ -1,7 +1,7 @@ ## CDISCTealDataset ==== testthat::test_that("CDISCTealDataset basics", { x <- data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = TRUE) - formatters::var_labels(x) <- c("X", "Y") + col_labels(x) <- c("X", "Y") testthat::expect_error( teal.data:::CDISCTealDataset$new(dataname = "abc", x = x) diff --git a/tests/testthat/test-TealDataset.R b/tests/testthat/test-TealDataset.R index e384e5a10..77272d917 100644 --- a/tests/testthat/test-TealDataset.R +++ b/tests/testthat/test-TealDataset.R @@ -1,7 +1,7 @@ ## TealDataset ===== testthat::test_that("TealDataset basics", { x <- data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = TRUE) - formatters::var_labels(x) <- c("X", "Y") + col_labels(x) <- c("X", "Y") testthat::expect_silent({ test_ds <- TealDataset$new( diff --git a/tests/testthat/test-TealDatasetConnector.R b/tests/testthat/test-TealDatasetConnector.R index a79398ce5..c26744e02 100644 --- a/tests/testthat/test-TealDatasetConnector.R +++ b/tests/testthat/test-TealDatasetConnector.R @@ -271,7 +271,7 @@ testthat::test_that("csv_dataset_connector attritubes", { RACE = c("sth1|sth2", "sth", "sth"), stringsAsFactors = FALSE ) - formatters::var_labels(ADSL_ns) <- letters[1:4] # nolint + col_labels(ADSL_ns) <- letters[1:4] # nolint temp_file_csv <- tempfile(fileext = ".csv") write.table(ADSL_ns, file = temp_file_csv, row.names = FALSE, sep = ",") @@ -288,7 +288,7 @@ testthat::test_that("csv_dataset_connector attritubes", { testthat::expect_null(attributes(data[[1]])$label) # we should use mutate_dataset - data <- (x %>% mutate_dataset("formatters::var_labels(ADSL) <- letters[1:4]"))$get_raw_data() + data <- (x %>% mutate_dataset("col_labels(ADSL) <- letters[1:4]"))$get_raw_data() testthat::expect_identical(attributes(data[[1]])$label, "a") }) @@ -396,7 +396,7 @@ testthat::test_that("fun_cdisc_dataset_connector", { ) x$w <- as.numeric(rnorm(40, 0, 1)) x$ww <- as.numeric(rnorm(40, 0, 1)) - formatters::var_labels(x) <- c("STUDYID", "USUBJID", "z", "zz", "NAs", "w", "ww") + col_labels(x) <- c("STUDYID", "USUBJID", "z", "zz", "NAs", "w", "ww") x } @@ -413,7 +413,7 @@ testthat::test_that("fun_cdisc_dataset_connector", { ) x$w <- as.numeric(rnorm(40, 0, 1)) x$ww <- as.numeric(rnorm(40, 0, 1)) - formatters::var_labels(x) <- c("STUDYID", "USUBJID", "z", "zz", "NAs", "w", "ww") + col_labels(x) <- c("STUDYID", "USUBJID", "z", "zz", "NAs", "w", "ww") x }