From 44a80d1d5cdae5afda56c830a0d266164d0cb27e Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 10:28:49 -0500 Subject: [PATCH 1/7] format_tt initial --- NAMESPACE | 1 + R/format_tt.R | 102 +++++++++++++++++++++++++++++++++ inst/tinytest/test-format_tt.R | 9 +++ man/format_tt.Rd | 62 ++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 R/format_tt.R create mode 100644 inst/tinytest/test-format_tt.R create mode 100644 man/format_tt.Rd diff --git a/NAMESPACE b/NAMESPACE index 005f995b..d88c2add 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand S3method(print,tinytable) +export(format_tt) export(group_tt) export(knit_print.tinytable) export(save_tt) diff --git a/R/format_tt.R b/R/format_tt.R new file mode 100644 index 00000000..b7c6787c --- /dev/null +++ b/R/format_tt.R @@ -0,0 +1,102 @@ +# output is selected automatically if format_tt is called in tt() +# x is inserted automatically if format_tt is called in tt() + + +#' Format columns of a data frame +#' +#' This function formats the columns of a data frame based on the column type (logical, date, numeric). +#' It allows various formatting options like significant digits, decimal points, and scientific notation. +#' It also includes custom formatting for date and boolean values. +#' +#' @param x A data frame to be formatted. +#' @param digits Number of significant digits or decimal places. +#' @param num_fmt The format for numeric values; one of 'significant', 'decimal', or 'scientific'. +#' @param num_mark_big Character to use as a thousands separator. +#' @param num_mark_dec Decimal mark character. Default is the global option 'OutDec'. +#' @param num_zero Logical; if TRUE, trailing zeros are kept. +#' @param url Logical; if TRUE, treats the column as a URL. +#' @param date A function to format Date columns. Defaults to ISO format. +#' @param bool A function to format logical columns. Defaults to title case. +#' @param ... Additional arguments are ignored. +#' @inheritParams tt +#' +#' @return A data frame with formatted columns. +#' +#' @examples +#' # Example usage +#' data_frame <- data.frame( +#' logical_col = c(TRUE, FALSE, TRUE), +#' date_col = as.Date(c('2020-01-01', '2021-02-02', '2022-03-03')), +#' numeric_col = c(12345.67, 8901.23, 4567.89) +#' ) +#' formatted_data_frame <- format_tt(data_frame) +#' +#' @export +format_tt <- function(x = NULL, + j = NULL, + output = NULL, + digits = NULL, + num_fmt = "significant", + num_mark_big = "", + num_mark_dec = getOption("OutDec"), + num_zero = TRUE, + url = FALSE, + date = function(column) format(column, "%Y-%m-%d"), + bool = function(column) tools::toTitleCase(tolower(column)), + ... + ) { + + if (is.null(x)) { + out <- match.call() + class(out) <- c("tinytable_format_tt", class(out)) + return(out) + } + + assert_data_frame(x) + + output <- sanitize_output(output) + + assert_choice(num_fmt, c("significant", "decimal", "scientific")) + + # column index NULL or regex or integer vector + if (is.null(j)) { + j <- seq_len(ncol(x)) + } else if (is.character(j) && length(j) == 1 && !is.null(names(x))) { + j <- grep(j, colnames(x), perl = TRUE) + } else { + assert_integerish(j, lower = 1, upper = ncol(x)) + } + + # format each column + for (col in j) { + + # logical + if (is.logical(x[[col]])) { + x[[col]] <- bool(x[[col]]) + + # date + } else if (inherits(x[[col]], "Date")) { + x[[col]] <- date(x[[col]]) + + # numeric + } else if (is.numeric(x[[col]])) { + if (num_fmt == "significant") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "g", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + } else if (num_fmt == "decimal") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "f", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + } else if (num_fmt == "scientific") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "e", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + } + } + + } + + return(x) + +} diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R new file mode 100644 index 00000000..e454c2ff --- /dev/null +++ b/inst/tinytest/test-format_tt.R @@ -0,0 +1,9 @@ + +N <- 10 +dat <- data.frame( + x = c(1.430, rnorm(N - 1, mean = 100000)), + y = as.Date(sample(1:1000, N)), + z = sample(c(TRUE, FALSE), N, replace = TRUE) +) +pkgload::load_all() +k = format_tt(num_fmt = "decimal", digits = 3, num_mark_big = " ", num_mark_dec = ",") diff --git a/man/format_tt.Rd b/man/format_tt.Rd new file mode 100644 index 00000000..8f306073 --- /dev/null +++ b/man/format_tt.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/format_tt.R +\name{format_tt} +\alias{format_tt} +\title{Format columns of a data frame} +\usage{ +format_tt( + x = NULL, + j = NULL, + output = NULL, + digits = NULL, + num_fmt = "significant", + num_mark_big = "", + num_mark_dec = getOption("OutDec"), + num_zero = TRUE, + url = FALSE, + date = function(column) format(column, "\%Y-\%m-\%d"), + bool = function(column) tools::toTitleCase(tolower(column)), + ... +) +} +\arguments{ +\item{x}{A data frame to be formatted.} + +\item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} + +\item{digits}{Number of significant digits or decimal places.} + +\item{num_fmt}{The format for numeric values; one of 'significant', 'decimal', or 'scientific'.} + +\item{num_mark_big}{Character to use as a thousands separator.} + +\item{num_mark_dec}{Decimal mark character. Default is the global option 'OutDec'.} + +\item{num_zero}{Logical; if TRUE, trailing zeros are kept.} + +\item{url}{Logical; if TRUE, treats the column as a URL.} + +\item{date}{A function to format Date columns. Defaults to ISO format.} + +\item{bool}{A function to format logical columns. Defaults to title case.} + +\item{...}{Additional arguments are ignored.} +} +\value{ +A data frame with formatted columns. +} +\description{ +This function formats the columns of a data frame based on the column type (logical, date, numeric). +It allows various formatting options like significant digits, decimal points, and scientific notation. +It also includes custom formatting for date and boolean values. +} +\examples{ +# Example usage +data_frame <- data.frame( + logical_col = c(TRUE, FALSE, TRUE), + date_col = as.Date(c('2020-01-01', '2021-02-02', '2022-03-03')), + numeric_col = c(12345.67, 8901.23, 4567.89) +) +formatted_data_frame <- format_tt(data_frame) + +} From 596f293c4a1936544c1df42b1bdab7a9e8b29dc3 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 11:17:26 -0500 Subject: [PATCH 2/7] format_tt doc and work --- Makefile | 3 +++ R/format_tt.R | 41 +++++++++++++++++++++------------- R/sanity.R | 9 ++++++++ R/tt.R | 2 ++ inst/tinytest/test-format_tt.R | 7 +++--- man/format_tt.Rd | 14 +++++++----- man/tt.Rd | 3 +++ 7 files changed, 54 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 81e50dc1..8f42752e 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,9 @@ help: ## Display this help screen check: ## check Rscript -e "devtools::document();devtools::check()" +document: ## document + Rscript -e "devtools::document()" + test: ## test Rscript -e "devtools::install();library(tinytable);tinytest::run_test_dir()" diff --git a/R/format_tt.R b/R/format_tt.R index b7c6787c..f007876b 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -11,14 +11,15 @@ #' @param x A data frame to be formatted. #' @param digits Number of significant digits or decimal places. #' @param num_fmt The format for numeric values; one of 'significant', 'decimal', or 'scientific'. +#' @param num_zero Logical; if TRUE, trailing zeros are kept in "decimal" format (but not in "significant" format). #' @param num_mark_big Character to use as a thousands separator. #' @param num_mark_dec Decimal mark character. Default is the global option 'OutDec'. -#' @param num_zero Logical; if TRUE, trailing zeros are kept. #' @param url Logical; if TRUE, treats the column as a URL. #' @param date A function to format Date columns. Defaults to ISO format. #' @param bool A function to format logical columns. Defaults to title case. -#' @param ... Additional arguments are ignored. +#' @param other A function to format columns of other types. Defaults to identity (no formatting). #' @inheritParams tt +#' @inheritParams style_tt #' #' @return A data frame with formatted columns. #' @@ -37,27 +38,32 @@ format_tt <- function(x = NULL, output = NULL, digits = NULL, num_fmt = "significant", - num_mark_big = "", - num_mark_dec = getOption("OutDec"), num_zero = TRUE, + num_mark_big = "", + num_mark_dec = getOption("OutDec", default = "."), url = FALSE, date = function(column) format(column, "%Y-%m-%d"), bool = function(column) tools::toTitleCase(tolower(column)), - ... + other = identity ) { - if (is.null(x)) { - out <- match.call() - class(out) <- c("tinytable_format_tt", class(out)) - return(out) + if (inherits(x, "tinytable")) { + msg <- "`format_tt()` must be called *before* `tt()`. You must format your dataset before drawing a table." } assert_data_frame(x) + assert_integerish(digits, len = 1, null.ok = TRUE) + assert_choice(num_fmt, c("significant", "decimal", "scientific")) + assert_flag(num_zero) + assert_string(num_mark_big) + assert_string(num_mark_dec) + assert_flag(url) + assert_function(date) + assert_function(bool) + assert_function(identity) output <- sanitize_output(output) - assert_choice(num_fmt, c("significant", "decimal", "scientific")) - # column index NULL or regex or integer vector if (is.null(j)) { j <- seq_len(ncol(x)) @@ -79,11 +85,12 @@ format_tt <- function(x = NULL, x[[col]] <- date(x[[col]]) # numeric - } else if (is.numeric(x[[col]])) { + } else if (is.numeric(x[[col]]) && !is.null(digits)) { if (num_fmt == "significant") { - x[[col]] <- formatC(x[[col]], - digits = digits, format = "g", drop0trailing = !num_zero, - big.mark = num_mark_big, decimal.mark = num_mark_dec) + x[[col]] <- format(x[[col]], + digits = digits, drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec, + scientific = FALSE) } else if (num_fmt == "decimal") { x[[col]] <- formatC(x[[col]], digits = digits, format = "f", drop0trailing = !num_zero, @@ -93,10 +100,12 @@ format_tt <- function(x = NULL, digits = digits, format = "e", drop0trailing = !num_zero, big.mark = num_mark_big, decimal.mark = num_mark_dec) } + + } else { + x[[col]] <- other(x[[col]]) } } return(x) - } diff --git a/R/sanity.R b/R/sanity.R index d1804d9e..fd312537 100644 --- a/R/sanity.R +++ b/R/sanity.R @@ -182,3 +182,12 @@ assert_list <- function(x, named = FALSE, null.ok = FALSE, name = as.character(s } } } + + +assert_function <- function(x, null.ok = FALSE, name = as.character(substitute(x))) { + if (isTRUE(null.ok) && is.null(x)) return(invisible(TRUE)) + if (!is.function(x)) { + msg <- sprintf("`%s` must be a function.", name) + stop(msg, call. = FALSE) + } +} diff --git a/R/tt.R b/R/tt.R index 9b6cf9c7..b951fecc 100644 --- a/R/tt.R +++ b/R/tt.R @@ -4,6 +4,7 @@ #' #' @param x A data frame or data table to be rendered as a table. #' @param output The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents. +#' @param digits Number of significant digits to keep for numeric variables. When `digits` is not `NULL` #' @param align A string specifying the alignment of columns. Each character in the string corresponds to a column; 'l' for left, 'c' for center, and 'r' for right alignment. The length of the string must match the number of columns in `x`. #' @param caption A string that will be used as the caption of the table. #' @param width A numeric value between 0 and 1 indicating the proportion of the line width that the table should cover. @@ -32,6 +33,7 @@ #' @export tt <- function(x, output = NULL, + digits = NULL, align = NULL, caption = NULL, width = NULL, diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R index e454c2ff..d343fde6 100644 --- a/inst/tinytest/test-format_tt.R +++ b/inst/tinytest/test-format_tt.R @@ -1,9 +1,10 @@ - +# pkgload::load_all() N <- 10 dat <- data.frame( x = c(1.430, rnorm(N - 1, mean = 100000)), y = as.Date(sample(1:1000, N)), z = sample(c(TRUE, FALSE), N, replace = TRUE) ) -pkgload::load_all() -k = format_tt(num_fmt = "decimal", digits = 3, num_mark_big = " ", num_mark_dec = ",") +dat |> + format_tt(digits = 4) |> + tt() diff --git a/man/format_tt.Rd b/man/format_tt.Rd index 8f306073..7de22778 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -10,37 +10,39 @@ format_tt( output = NULL, digits = NULL, num_fmt = "significant", - num_mark_big = "", - num_mark_dec = getOption("OutDec"), num_zero = TRUE, + num_mark_big = "", + num_mark_dec = getOption("OutDec", default = "."), url = FALSE, date = function(column) format(column, "\%Y-\%m-\%d"), bool = function(column) tools::toTitleCase(tolower(column)), - ... + other = identity ) } \arguments{ \item{x}{A data frame to be formatted.} +\item{j}{Column indices where the styling should be applied. Can be a single value, a vector, or a Perl-style regular expression applied to column names of the original data frame. If \code{colspan} is used, \code{j} must be of length 1.} + \item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} \item{digits}{Number of significant digits or decimal places.} \item{num_fmt}{The format for numeric values; one of 'significant', 'decimal', or 'scientific'.} +\item{num_zero}{Logical; if TRUE, trailing zeros are kept in "decimal" format (but not in "significant" format).} + \item{num_mark_big}{Character to use as a thousands separator.} \item{num_mark_dec}{Decimal mark character. Default is the global option 'OutDec'.} -\item{num_zero}{Logical; if TRUE, trailing zeros are kept.} - \item{url}{Logical; if TRUE, treats the column as a URL.} \item{date}{A function to format Date columns. Defaults to ISO format.} \item{bool}{A function to format logical columns. Defaults to title case.} -\item{...}{Additional arguments are ignored.} +\item{other}{A function to format columns of other types. Defaults to identity (no formatting).} } \value{ A data frame with formatted columns. diff --git a/man/tt.Rd b/man/tt.Rd index e6b19286..322dcf4d 100644 --- a/man/tt.Rd +++ b/man/tt.Rd @@ -7,6 +7,7 @@ tt( x, output = NULL, + digits = NULL, align = NULL, caption = NULL, width = NULL, @@ -20,6 +21,8 @@ tt( \item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} +\item{digits}{Number of significant digits to keep for numeric variables. When \code{digits} is not \code{NULL}} + \item{align}{A string specifying the alignment of columns. Each character in the string corresponds to a column; 'l' for left, 'c' for center, and 'r' for right alignment. The length of the string must match the number of columns in \code{x}.} \item{caption}{A string that will be used as the caption of the table.} From e2b6d1e7d18a26b472c68f87e4bd85139c2fbb5c Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 11:26:45 -0500 Subject: [PATCH 3/7] docs --- R/tt.R | 3 ++- man/tt.Rd | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/R/tt.R b/R/tt.R index b951fecc..d013f3d6 100644 --- a/R/tt.R +++ b/R/tt.R @@ -4,7 +4,7 @@ #' #' @param x A data frame or data table to be rendered as a table. #' @param output The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents. -#' @param digits Number of significant digits to keep for numeric variables. When `digits` is not `NULL` +#' @param digits Number of significant digits to keep for numeric variables. When `digits` is an integer, `tt()` calls `format_tt(x, digits = digits)` before proceeding to draw the table. Users who need more control can proceed in two steps: (1) format the data with `format_tt()` or other functions, and (2) pass the formatted data to `tt()` for drawing. See `?format_tt` for more details on formating options (ex: decimal, scientific notation, dates, boolean variables, etc.). #' @param align A string specifying the alignment of columns. Each character in the string corresponds to a column; 'l' for left, 'c' for center, and 'r' for right alignment. The length of the string must match the number of columns in `x`. #' @param caption A string that will be used as the caption of the table. #' @param width A numeric value between 0 and 1 indicating the proportion of the line width that the table should cover. @@ -47,6 +47,7 @@ tt <- function(x, assert_string(caption, null.ok = TRUE) assert_string(align, null.ok = TRUE) assert_numeric(width, len = 1, lower = 0, upper = 1, null.ok = TRUE) + assert_integerish(digits, len = 1, null.ok = TRUE) # notes can be a single string or a (named) list of strings if (is.character(notes) && length(notes)) { diff --git a/man/tt.Rd b/man/tt.Rd index 322dcf4d..0b119045 100644 --- a/man/tt.Rd +++ b/man/tt.Rd @@ -21,7 +21,7 @@ tt( \item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} -\item{digits}{Number of significant digits to keep for numeric variables. When \code{digits} is not \code{NULL}} +\item{digits}{Number of significant digits to keep for numeric variables. When \code{digits} is an integer, \code{tt()} calls \code{format_tt(x, digits = digits)} before proceeding to draw the table. Users who need more control can proceed in two steps: (1) format the data with \code{format_tt()} or other functions, and (2) pass the formatted data to \code{tt()} for drawing. See \code{?format_tt} for more details on formating options (ex: decimal, scientific notation, dates, boolean variables, etc.).} \item{align}{A string specifying the alignment of columns. Each character in the string corresponds to a column; 'l' for left, 'c' for center, and 'r' for right alignment. The length of the string must match the number of columns in \code{x}.} From 58c570bf68e1b3637ba32d61e9e5d854b147a596 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 12:26:07 -0500 Subject: [PATCH 4/7] docs --- R/format_tt.R | 22 ++++++++++++++++++++-- R/sanity.R | 12 ++++++++++++ inst/tinytest/test-format_tt.R | 4 ++++ man/format_tt.Rd | 2 +- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/R/format_tt.R b/R/format_tt.R index f007876b..7a6250ce 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -8,7 +8,7 @@ #' It allows various formatting options like significant digits, decimal points, and scientific notation. #' It also includes custom formatting for date and boolean values. #' -#' @param x A data frame to be formatted. +#' @param x A data frame or a vector to be formatted. #' @param digits Number of significant digits or decimal places. #' @param num_fmt The format for numeric values; one of 'significant', 'decimal', or 'scientific'. #' @param num_zero Logical; if TRUE, trailing zeros are kept in "decimal" format (but not in "significant" format). @@ -51,6 +51,19 @@ format_tt <- function(x = NULL, msg <- "`format_tt()` must be called *before* `tt()`. You must format your dataset before drawing a table." } + if (isTRUE(check_atomic_vector(x))) { + atomic_vector <- TRUE + x <- data.frame(tinytable = x) + j <- 1 + } else { + atomic_vector <- FALSE + } + + if (!inherits(x, "data.frame")) { + msg <- "`x` must be a data frame or an atomic vector." + stop(msg, call. = FALSE) + } + assert_data_frame(x) assert_integerish(digits, len = 1, null.ok = TRUE) assert_choice(num_fmt, c("significant", "decimal", "scientific")) @@ -107,5 +120,10 @@ format_tt <- function(x = NULL, } - return(x) + if (isTRUE(atomic_vector)) { + return(x[[1]]) + } else { + return(x) + } + } diff --git a/R/sanity.R b/R/sanity.R index fd312537..9897c1d0 100644 --- a/R/sanity.R +++ b/R/sanity.R @@ -191,3 +191,15 @@ assert_function <- function(x, null.ok = FALSE, name = as.character(substitute(x stop(msg, call. = FALSE) } } + +check_atomic_vector<- function(x, null.ok = FALSE, name = as.character(substitute(x))) { + if (isTRUE(null.ok) && is.null(x)) return(invisible(TRUE)) + flag <- is.atomic(x) && is.vector(x) && !is.list(x) + if (flag) { + out <- TRUE + } else { + out <- sprintf("`%s` must be an atomic vector.", name) + } + return(out) +} + diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R index d343fde6..b07a493d 100644 --- a/inst/tinytest/test-format_tt.R +++ b/inst/tinytest/test-format_tt.R @@ -5,6 +5,10 @@ dat <- data.frame( y = as.Date(sample(1:1000, N)), z = sample(c(TRUE, FALSE), N, replace = TRUE) ) + +# pkgload::load_all() dat |> format_tt(digits = 4) |> tt() + +format_tt(dat$x, digits = 1) diff --git a/man/format_tt.Rd b/man/format_tt.Rd index 7de22778..d022cf13 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -20,7 +20,7 @@ format_tt( ) } \arguments{ -\item{x}{A data frame to be formatted.} +\item{x}{A data frame or a vector to be formatted.} \item{j}{Column indices where the styling should be applied. Can be a single value, a vector, or a Perl-style regular expression applied to column names of the original data frame. If \code{colspan} is used, \code{j} must be of length 1.} From eab07ff0a6f7ff380a56378a000e785be6399fa3 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 13:47:21 -0500 Subject: [PATCH 5/7] suffix --- R/format_tt.R | 29 ++++++++++++++++++++++++++++- inst/tinytest/test-format_tt.R | 6 ++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/R/format_tt.R b/R/format_tt.R index 7a6250ce..5955aff0 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -39,6 +39,7 @@ format_tt <- function(x = NULL, digits = NULL, num_fmt = "significant", num_zero = TRUE, + num_suffix = FALSE, num_mark_big = "", num_mark_dec = getOption("OutDec", default = "."), url = FALSE, @@ -99,15 +100,23 @@ format_tt <- function(x = NULL, # numeric } else if (is.numeric(x[[col]]) && !is.null(digits)) { - if (num_fmt == "significant") { + + if (isTRUE(num_suffix)) { + x[[col]] <- format_num_suffix( + x[[col]], digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero + ) + + } else if (num_fmt == "significant") { x[[col]] <- format(x[[col]], digits = digits, drop0trailing = !num_zero, big.mark = num_mark_big, decimal.mark = num_mark_dec, scientific = FALSE) + } else if (num_fmt == "decimal") { x[[col]] <- formatC(x[[col]], digits = digits, format = "f", drop0trailing = !num_zero, big.mark = num_mark_big, decimal.mark = num_mark_dec) + } else if (num_fmt == "scientific") { x[[col]] <- formatC(x[[col]], digits = digits, format = "e", drop0trailing = !num_zero, @@ -127,3 +136,21 @@ format_tt <- function(x = NULL, } } + + + +format_num_suffix <- function(x, digits, num_mark_big, num_mark_dec, num_zero) { + suffix <- number <- rep("", length(x)) + suffix <- ifelse(x > 1e12, "T", suffix) + suffix <- ifelse(x > 1e9, "B", suffix) + suffix <- ifelse(x > 1e6, "M", suffix) + suffix <- ifelse(x > 1e3, "K", suffix) + number <- format_tt(x, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero) + number <- ifelse(x > 1e12, format_tt(x / 1e12, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e9, format_tt(x / 1e9, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e6, format_tt(x / 1e6, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e3, format_tt(x / 1e3, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e3, format_tt(x / 1e3, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- paste0(number, suffix) + return(number) +} diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R index b07a493d..8ea589c5 100644 --- a/inst/tinytest/test-format_tt.R +++ b/inst/tinytest/test-format_tt.R @@ -6,9 +6,7 @@ dat <- data.frame( z = sample(c(TRUE, FALSE), N, replace = TRUE) ) -# pkgload::load_all() +pkgload::load_all() dat |> - format_tt(digits = 4) |> + format_tt(digits = 2, num_suffix = TRUE) |> tt() - -format_tt(dat$x, digits = 1) From 8834f9e0df69cafd8ad85e37dd8d6ec90b24952a Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 15:34:42 -0500 Subject: [PATCH 6/7] docs --- R/format_tt.R | 19 ++++----------- R/tt.R | 5 ++++ inst/tinytest/test-format_tt.R | 8 +++---- man/format_tt.Rd | 17 ++++---------- vignettes/tutorial.qmd | 43 ++++++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/R/format_tt.R b/R/format_tt.R index 5955aff0..8a308277 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -14,24 +14,15 @@ #' @param num_zero Logical; if TRUE, trailing zeros are kept in "decimal" format (but not in "significant" format). #' @param num_mark_big Character to use as a thousands separator. #' @param num_mark_dec Decimal mark character. Default is the global option 'OutDec'. +#' @param num_suffix Logical; if TRUE display short numbers with `digits` significant digits and K (thousands), M (millions), B (billions), or T (trillions) suffixes. #' @param url Logical; if TRUE, treats the column as a URL. -#' @param date A function to format Date columns. Defaults to ISO format. +#' @param date A string passed to the `format()` function, such as "%Y-%m-%d". See the "Details" section in `?strptime` #' @param bool A function to format logical columns. Defaults to title case. #' @param other A function to format columns of other types. Defaults to identity (no formatting). #' @inheritParams tt #' @inheritParams style_tt #' #' @return A data frame with formatted columns. -#' -#' @examples -#' # Example usage -#' data_frame <- data.frame( -#' logical_col = c(TRUE, FALSE, TRUE), -#' date_col = as.Date(c('2020-01-01', '2021-02-02', '2022-03-03')), -#' numeric_col = c(12345.67, 8901.23, 4567.89) -#' ) -#' formatted_data_frame <- format_tt(data_frame) -#' #' @export format_tt <- function(x = NULL, j = NULL, @@ -43,7 +34,7 @@ format_tt <- function(x = NULL, num_mark_big = "", num_mark_dec = getOption("OutDec", default = "."), url = FALSE, - date = function(column) format(column, "%Y-%m-%d"), + date = "%Y-%m-%d", bool = function(column) tools::toTitleCase(tolower(column)), other = identity ) { @@ -72,7 +63,7 @@ format_tt <- function(x = NULL, assert_string(num_mark_big) assert_string(num_mark_dec) assert_flag(url) - assert_function(date) + assert_string(date) assert_function(bool) assert_function(identity) @@ -96,7 +87,7 @@ format_tt <- function(x = NULL, # date } else if (inherits(x[[col]], "Date")) { - x[[col]] <- date(x[[col]]) + x[[col]] <- format(x[[col]], date) # numeric } else if (is.numeric(x[[col]]) && !is.null(digits)) { diff --git a/R/tt.R b/R/tt.R index d013f3d6..e2208c6d 100644 --- a/R/tt.R +++ b/R/tt.R @@ -49,6 +49,11 @@ tt <- function(x, assert_numeric(width, len = 1, lower = 0, upper = 1, null.ok = TRUE) assert_integerish(digits, len = 1, null.ok = TRUE) + # formatting options are limited here + if (!is.null(digits)) { + x <- format_tt(x, digits = digits) + } + # notes can be a single string or a (named) list of strings if (is.character(notes) && length(notes)) { notes <- list(notes) diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R index 8ea589c5..3c9b96f9 100644 --- a/inst/tinytest/test-format_tt.R +++ b/inst/tinytest/test-format_tt.R @@ -6,7 +6,7 @@ dat <- data.frame( z = sample(c(TRUE, FALSE), N, replace = TRUE) ) -pkgload::load_all() -dat |> - format_tt(digits = 2, num_suffix = TRUE) |> - tt() +# pkgload::load_all() +# dat |> +# format_tt(digits = 2, num_suffix = TRUE) |> +# tt() diff --git a/man/format_tt.Rd b/man/format_tt.Rd index d022cf13..7d0f1ec2 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -11,10 +11,11 @@ format_tt( digits = NULL, num_fmt = "significant", num_zero = TRUE, + num_suffix = FALSE, num_mark_big = "", num_mark_dec = getOption("OutDec", default = "."), url = FALSE, - date = function(column) format(column, "\%Y-\%m-\%d"), + date = "\%Y-\%m-\%d", bool = function(column) tools::toTitleCase(tolower(column)), other = identity ) @@ -32,13 +33,15 @@ format_tt( \item{num_zero}{Logical; if TRUE, trailing zeros are kept in "decimal" format (but not in "significant" format).} +\item{num_suffix}{Logical; if TRUE display short numbers with \code{digits} significant digits and K (thousands), M (millions), B (billions), or T (trillions) suffixes.} + \item{num_mark_big}{Character to use as a thousands separator.} \item{num_mark_dec}{Decimal mark character. Default is the global option 'OutDec'.} \item{url}{Logical; if TRUE, treats the column as a URL.} -\item{date}{A function to format Date columns. Defaults to ISO format.} +\item{date}{A string passed to the \code{format()} function, such as "\%Y-\%m-\%d". See the "Details" section in \code{?strptime}} \item{bool}{A function to format logical columns. Defaults to title case.} @@ -52,13 +55,3 @@ This function formats the columns of a data frame based on the column type (logi It allows various formatting options like significant digits, decimal points, and scientific notation. It also includes custom formatting for date and boolean values. } -\examples{ -# Example usage -data_frame <- data.frame( - logical_col = c(TRUE, FALSE, TRUE), - date_col = as.Date(c('2020-01-01', '2021-02-02', '2022-03-03')), - numeric_col = c(12345.67, 8901.23, 4567.89) -) -formatted_data_frame <- format_tt(data_frame) - -} diff --git a/vignettes/tutorial.qmd b/vignettes/tutorial.qmd index ef288104..a32c6c24 100644 --- a/vignettes/tutorial.qmd +++ b/vignettes/tutorial.qmd @@ -95,6 +95,48 @@ To align columns, we use a single string, where each letter represents a column: tt(x, align = "ccrrl") ``` +## Formatting (numbers, dates, strings, etc.) + + +The `tt()` function is minimalist; it's inteded purpose is simply to draw nice tables. Users who want to format numbers, dates, strings, and other variables in different ways should process their data *before* supplying it to the `tt()` table-drawing function. To do so, we can use the `format_tt()` function supplied by the `tinytable`. + +In a very simple case---such as printing 2 significant digits of all numeric variables---we can use the `digits` argument of `tt()`: + +```{r} +dat <- data.frame( + w = c(1.43002, 201.399, 0.100188), + x = c(1.43002, 201.399, 0.100188), + y = as.Date(sample(1:1000, 3)), + z = c(TRUE, TRUE, FALSE)) + +tt(dat, digits = 2) +``` + +For each numeric column, `tinytable` checked how many numbers after the decimal where necessary for every number to display at least two digits. + +We can get more fine-grained control over formatting with calling `format_tt()` before `tt()`: + +```{r} +dat <- data.frame( + w = c(143002.2092, 201399.181, 100188.3883), + x = c(1.43002, 201.399, 0.100188), + y = as.Date(sample(1:1000, 3)), + z = c(TRUE, TRUE, FALSE)) + +dat |> + format_tt( + j = 2:4, + digits = 1, + date = "%B %d %Y") |> + format_tt( + j = 1, + digits = 2, + num_mark_big = " ", + num_mark_dec = ",", + num_fmt = "decimal") |> + tt() +``` + ## Width The `width` arguments accepts a number between 0 and 1, indicating what proportion of the linewidth the table should cover: @@ -289,6 +331,7 @@ tt(x) |> style_tt(i = 1, j = 3:4, color = "green") ``` + ## Colors The `color` and `background` arguments in the `style_tt()` function are used for specifying the text color and the background color for cells of a table created by the `tt()` function. This argument plays a crucial role in enhancing the visual appeal and readability of the table, whether it's rendered in LaTeX or HTML format. The way we specify colors differs slightly between the two formats: From 7cf091e9341c34c0291bf4f2fc476f9500d5a4b5 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Wed, 17 Jan 2024 16:19:32 -0500 Subject: [PATCH 7/7] format --- R/format_tt.R | 107 +++++++++++------- R/tt.R | 2 +- README.md | 1 - README.qmd | 6 + inst/tinytest/_tinysnapshot/html-heatmap.rds | Bin 1340 -> 1341 bytes inst/tinytest/_tinysnapshot/html-issue58.rds | Bin 1323 -> 1323 bytes inst/tinytest/_tinysnapshot/html-striped.rds | Bin 937 -> 940 bytes .../_tinysnapshot/html-striped_orange.rds | Bin 1089 -> 1091 bytes .../_tinysnapshot/html-vectorized_color_j.rds | Bin 1057 -> 1058 bytes inst/tinytest/_tinysnapshot/latex-align.txt | 6 +- .../_tinysnapshot/latex-cell_color.txt | 6 +- .../_tinysnapshot/latex-col_color.txt | 6 +- inst/tinytest/_tinysnapshot/latex-default.txt | 6 +- .../_tinysnapshot/latex-group_style_order.txt | 6 +- inst/tinytest/_tinysnapshot/latex-nohead.txt | 6 +- .../_tinysnapshot/latex-row_color.txt | 6 +- .../_tinysnapshot/latex-theme_grid.txt | 6 +- .../_tinysnapshot/latex-theme_striped.txt | 6 +- .../_tinysnapshot/latex-theme_void.txt | 6 +- .../_tinysnapshot/markdown-nocolnames.txt | 6 +- inst/tinytest/test-latex.R | 4 +- man/format_tt.Rd | 5 +- man/tt.Rd | 2 +- vignettes/tutorial.qmd | 44 ++++--- 24 files changed, 142 insertions(+), 95 deletions(-) diff --git a/R/format_tt.R b/R/format_tt.R index 8a308277..1329fd39 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -15,6 +15,7 @@ #' @param num_mark_big Character to use as a thousands separator. #' @param num_mark_dec Decimal mark character. Default is the global option 'OutDec'. #' @param num_suffix Logical; if TRUE display short numbers with `digits` significant digits and K (thousands), M (millions), B (billions), or T (trillions) suffixes. +#' @param sprintf String passed to the `?sprintf` function to format numbers or interpolate strings with a user-defined pattern (similar to the `glue` package, but using Base R). #' @param url Logical; if TRUE, treats the column as a URL. #' @param date A string passed to the `format()` function, such as "%Y-%m-%d". See the "Details" section in `?strptime` #' @param bool A function to format logical columns. Defaults to title case. @@ -27,12 +28,13 @@ format_tt <- function(x = NULL, j = NULL, output = NULL, - digits = NULL, + digits = getOption("digits"), num_fmt = "significant", num_zero = TRUE, num_suffix = FALSE, num_mark_big = "", num_mark_dec = getOption("OutDec", default = "."), + sprintf = NULL, url = FALSE, date = "%Y-%m-%d", bool = function(column) tools::toTitleCase(tolower(column)), @@ -57,7 +59,7 @@ format_tt <- function(x = NULL, } assert_data_frame(x) - assert_integerish(digits, len = 1, null.ok = TRUE) + assert_integerish(digits, len = 1) assert_choice(num_fmt, c("significant", "decimal", "scientific")) assert_flag(num_zero) assert_string(num_mark_big) @@ -66,6 +68,7 @@ format_tt <- function(x = NULL, assert_string(date) assert_function(bool) assert_function(identity) + assert_string(sprintf, null.ok = TRUE) output <- sanitize_output(output) @@ -81,44 +84,63 @@ format_tt <- function(x = NULL, # format each column for (col in j) { - # logical - if (is.logical(x[[col]])) { - x[[col]] <- bool(x[[col]]) - - # date - } else if (inherits(x[[col]], "Date")) { - x[[col]] <- format(x[[col]], date) - - # numeric - } else if (is.numeric(x[[col]]) && !is.null(digits)) { - - if (isTRUE(num_suffix)) { - x[[col]] <- format_num_suffix( - x[[col]], digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero - ) - - } else if (num_fmt == "significant") { - x[[col]] <- format(x[[col]], - digits = digits, drop0trailing = !num_zero, - big.mark = num_mark_big, decimal.mark = num_mark_dec, - scientific = FALSE) - - } else if (num_fmt == "decimal") { - x[[col]] <- formatC(x[[col]], - digits = digits, format = "f", drop0trailing = !num_zero, - big.mark = num_mark_big, decimal.mark = num_mark_dec) - - } else if (num_fmt == "scientific") { - x[[col]] <- formatC(x[[col]], - digits = digits, format = "e", drop0trailing = !num_zero, - big.mark = num_mark_big, decimal.mark = num_mark_dec) - } + # sprintf() is self-contained + if (!is.null(sprintf)) { + x[[col]] <- base::sprintf(sprintf, x[[col]]) } else { - x[[col]] <- other(x[[col]]) + + # logical + if (is.logical(x[[col]])) { + x[[col]] <- bool(x[[col]]) + + # date + } else if (inherits(x[[col]], "Date")) { + x[[col]] <- format(x[[col]], date) + + # numeric + } else if (is.numeric(x[[col]]) && !is.null(digits)) { + + # numeric suffix + if (isTRUE(num_suffix)) { + x[[col]] <- format_num_suffix(x[[col]], digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero) + + # non-integer numeric + } else if (is.numeric(x[[col]]) && !isTRUE(check_integerish(x[[col]]))) { + if (num_fmt == "significant") { + x[[col]] <- format(x[[col]], + digits = digits, drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec, + scientific = FALSE) + + } else if (num_fmt == "decimal") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "f", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + + if (num_fmt == "scientific") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "e", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + } + } + + # integer + } else if (isTRUE(check_integerish(x[[col]]))) { + if (num_fmt == "scientific") { + x[[col]] <- formatC(x[[col]], + digits = digits, format = "e", drop0trailing = !num_zero, + big.mark = num_mark_big, decimal.mark = num_mark_dec) + } + } + + } else { + x[[col]] <- other(x[[col]]) + } + } - } + } # loop over columns if (isTRUE(atomic_vector)) { return(x[[1]]) @@ -132,16 +154,15 @@ format_tt <- function(x = NULL, format_num_suffix <- function(x, digits, num_mark_big, num_mark_dec, num_zero) { suffix <- number <- rep("", length(x)) - suffix <- ifelse(x > 1e12, "T", suffix) - suffix <- ifelse(x > 1e9, "B", suffix) - suffix <- ifelse(x > 1e6, "M", suffix) suffix <- ifelse(x > 1e3, "K", suffix) + suffix <- ifelse(x > 1e6, "M", suffix) + suffix <- ifelse(x > 1e9, "B", suffix) + suffix <- ifelse(x > 1e12, "T", suffix) number <- format_tt(x, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero) - number <- ifelse(x > 1e12, format_tt(x / 1e12, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) - number <- ifelse(x > 1e9, format_tt(x / 1e9, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) - number <- ifelse(x > 1e6, format_tt(x / 1e6, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) - number <- ifelse(x > 1e3, format_tt(x / 1e3, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) number <- ifelse(x > 1e3, format_tt(x / 1e3, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e6, format_tt(x / 1e6, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e9, format_tt(x / 1e9, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) + number <- ifelse(x > 1e12, format_tt(x / 1e12, num_fmt = "decimal", digits = digits, num_mark_big = num_mark_big, num_mark_dec = num_mark_dec, num_zero = num_zero), number) number <- paste0(number, suffix) return(number) } diff --git a/R/tt.R b/R/tt.R index e2208c6d..96cd75d8 100644 --- a/R/tt.R +++ b/R/tt.R @@ -33,7 +33,7 @@ #' @export tt <- function(x, output = NULL, - digits = NULL, + digits = getOption("digits"), align = NULL, caption = NULL, width = NULL, diff --git a/README.md b/README.md index e8a1e999..ce99fe2d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -

diff --git a/README.qmd b/README.qmd index 12a087e4..730c7021 100644 --- a/README.qmd +++ b/README.qmd @@ -4,6 +4,12 @@ + + + + + +

diff --git a/inst/tinytest/_tinysnapshot/html-heatmap.rds b/inst/tinytest/_tinysnapshot/html-heatmap.rds index 86111b4a353233a42a0a42e77488cb43e07fbd05..27bcabdad408b1dac40123858f74423a4a8b2fe7 100644 GIT binary patch literal 1341 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 diff --git a/inst/tinytest/_tinysnapshot/html-striped.rds b/inst/tinytest/_tinysnapshot/html-striped.rds index 260e7287e566cc6d997addc85bafde85d886b1de..6f67d582e3acb5e204842b6787df74b0837c76ff 100644 GIT binary patch literal 940 zcmV;d15^ATiwFP!000001LYQ7Z`(FdU6P0TsXu`e2FL*{DT$XLO)R%-)&UzfY}hhj zgA+8gc(&+Llt$8aj3R%1*im10(pbrWz0D8ueSh$dcl@K{IL?l|ZUN1#vKA(|^p+_%SB z9vOL8S6z!;UG00<*OkIuuLAU9x@l&D&2T~E1kQ*z71@{yuhuC3@6PL^Zh{+xW%F5Z zoUv6EJ>v@!3#xVG+76=_ovMmv+s8Id%`cJS>toe<4$LhMx@j+$6s4_t^o-EC$<9ei z)s#zLz{ETtDLzeVnoAfp)cilZL+@xsM%`x zfK=!Z9cMY%Y=t{a^C@15EAjYo49vRRPacpZ zv*8g=5H!fP@lV>AhbOJNhlj1~e(-t=|9-yzpDd1o(>7OMw}yGps!Lw?+p{=5{_kqh z8fa@Rj*eTic(NAT4&k#Kw0ThwwG*h`SvV()*|}w9J%4Y`HrM8OE}4L8v9)fN$y~;U zXOc9-w#$2D)+4xeKXA6+EQbYE<&4H9#Iksv;-ev$f_X2%g6~$}v+l;k-aoGwl|=)U zE%k_B2QdhtM>7QM?Eeu14=7{Z%2}y%cRw&LH7N62PUWxJ(gugqOC#2+pDzC{Mz4v{ zYhtu|5|(Hm70ZB2uZ72lVi%YL%9RS0prv&W8p%p3z&F@823Oj6ZlLQ{fxAr#pUHEH zYw{9q4cf1=+dPwX9gD?Vwp9l=uzeYJ?5h#qm%CrApOVgAan1;Ayf2-XAMsiDfog6U OfByrfddwV@3IG7DrN}4% literal 937 zcmV;a16KSWiwFP!000001LYRmZre6gYm$fhsh_|J1LOdfl*CPtI+oWh?SKs%Hf$NN z!3i2#Jhtdklt$8aj3U22>`*s5X`*Dn-sT7S-0$S!A05YWcAVXv7tYQeg3jLW7vK4( zNPC6I7tXHp(s4SkJJ;9OpQ6O1;+WDIz#(U1eLSM1?i4DgbWj) z%mN6eU?_imo^_NyK2uWAF>4N4VsMQ{n5F z3WyGagW8ngM!?8$xi)knU>KH!k}0_SND>9p$W4sN^&kvmCWEO!bxrFMqjPH7SFx?dO z=Je)mKLmXo8o2S(RQR!NCkj8}u|z7DBqE;*oDftWGzh^)VbQ^g4aj)xedYBCey)0C z>|I`VEp~ai?_FJ03b(xq(97AjnJEs#IgJw-5N{^32^C(gQT*SY)unEN8;0fZS#XlE zbrn71OA-sJb>!L!qZpm4nr6qx4o%H3k>cxP*Le=iEehR?mrIJ)Rx>&vbYZeHl2SF} z(ibo__ehG*lA7icMh*4qoxK!vpG%n*8nwJjqZ^O z9irnb2b-;MM`=F8Bau8U7_Y_i1TpaZd6LO9<(Q)8AxWfdWoRWHF2~Sp+WlmKESZgt z4$&NYjC<0?JUlsQWgoV(`@x%s*>{uupQLaUoVIy--5TXVtL~eAdk&|^|5+b`);L=` zadh08#FL$PtOlPQo$V8XsJ(jXWrY`HacMrXtDe6w=iB>ovXD$bwb5Ev%5))P!!t>m zTei!4WZomVZa;9g-)w~?RpsT3+lOWGBE{E2Faz^mfCax@f6uyGSM~l`y`d}`sBERj z{3?h+2tA%7U@!e2G4Oyg)~%eCI=6QN<5Gh%zvWc^nyqYbcx`FKdi9&--^J)PF?vmm zR!_nT?W1BDaOt)1cwg)sb3nOLp%S#T?m;72Nd@@$`o`ccn=A};-70XmN#QejE^$9z zz>PusRd(Ao+4Qm4xaC-NaP`_JVaGlf@nO09V)KS{_KG?qu=TKXUVg;)+y|<;W&HgQ LjuDq1jS2t&|A*6* diff --git a/inst/tinytest/_tinysnapshot/html-striped_orange.rds b/inst/tinytest/_tinysnapshot/html-striped_orange.rds index 2a1e3557e6fbb683f3fbbd21ebef210e8a77ed3d..a9459637ca70c7c2a28d264c6f061107fbaad48d 100644 GIT binary patch literal 1091 zcmV-J1ibqniwFP!000001LaszZ`(EyR+r?Ve(F!)gaL8@TZ%0wNFB@Tnq|O-4I8!$ z*pLJbEz%ZUic(41juH3=>`xCnO0w*vv62mXn;&f6@$Tc@cO*~N9}Ua0_N@KASJvJE z46TFTFTZP_g6<6nudIFRwPn5et8sgKdmltP=Bviw%Wr=^zx?x;3uNRWY7ERT;#f=q zn+ki_KxhzAOw0g=G1VA_5tfSTz|P?9?J0p{8rXA27il83jlx7|D!_EXh>ik6=Pab{ zOlTn{m}Z!_RfstaI_}XLN;A#rP&2XAc*N}aHry-< zq={RG)IvJiNa!A{!-OZYj}i&o(KEElA-n=3#|tG(HBvI%#GWR6IcA)@Eb-?4Ml`Wa z)z@Q+^-NL~P<3Jt0WO1hh{s4^|Ack)1HNnLDjQ+76yd{_o@6NozbU?Z=X+Egdz#+0 z(aECLX4hqG{aISZP=OFI;l)TsuY8>=V&>j&amWQpf`ume9FOkS`HtnG9RSEB&8-> zZV%&h0w*bY-Y{J9ml;Cf>B}sm7upet^;4Fh+se>bJWa@4G%2sKLtDlgk6YoTci zL9_nA$YI$W=2+%;G&CWSgtHi)HQWi+7o3{m`{nnfxpiY7o~;{|5nY)q)D^pNL&~|j zn!>=`{XatBE+(W|ESCGM9tOsQqEi2sV)1LTFu}p?r9f(}pLqUVj7}M&Q^sig1YH0= zIOYN8t7a=cHamwLz%F+v42`aNR9Y5REx|4x(XM`(JEcOjrk5`-Q>Q+Y0OTN5S+5teBbj`ya%V J8eifN005wQ7AOD! literal 1089 zcmV-H1it$piwFP!000001LauXZ|XJ_&TiX>%hUZ6yl7IW1W6&Z>ZOF;-L8{1ZQ8VT z(x$e$HL)S)I!>-_ps30CQ{odOoXNaTo;V!C?IssLfX!R z7Gi>FhIw0snA4!+9<8A?)0_@96HARpoDRHPksCP^A5kRfIIttFQ`PspkO+6G2<2=p zT|u=c(%2g%iB?+T^xd)RyIqekrM*z8jlqqXa6w}uNqJx^z2sCyl1ZRNd+-hup2$8*BydO1&?<-U3XB{tlq}Uq$#9cuOn z1gu)cK+PxHFk{$5=QxaLA2}1Aj4*fB5!vtRY)y5U+`!BCtQn3HvMjPEY>q;Xl?rUL zF)?H(i&~pqmzA=x7bQ2}ZP+XqD$-zKx4x#y!gSA->AE~k!q{1YnX0_ZN z#_0r3QuMrIxa2Q$gv8UAc}6evBNFRfo}k-Wq4C&F$3Sn&{VYL7O@`eLz>%k{CsoGI z$x)@bQ)%|yx4X>`7yCb1Lf1X5nyFWr7 zx%7X8#9d5CvzRQ8Sv^dQ2}Py;Eyd#3WMPtnYfFLDT0iRiyBwV|N2kou#0k0pd;atWXLGXeealHO^+FIKna zWWfZ1>42?^Ij9YF6r7t5PcVVbUh>rhKDMgaX`v(N`&+9V07Un^R#Ry5$PS1WHdqWQI=^>(7= zj}hyZp8XTcA2%BCm(09jFfSh(mbL$C^G3ot$l5Oqwq8?MuRjW=KVZd-;qQL{6G2C7 H+YkT%kaj4Y diff --git a/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds b/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds index 90e6d07cf46993d5456e8de956dae74b19468df9..adcae1c81486bf4ab176dea862b67c7e033bc51f 100644 GIT binary patch delta 988 zcmV<210(#Q2%-oRABzY80000000ZS$|4-vK6weKj6IOqC6MFOWZ za%YUrGpUS=Vkrz2pgL#7BoU!A7E?bbT8If|80Nl?F{jaCaIkV^nBjC_m{=G*t|PAD}i964vu2yzt2kf4IpLMB;L=pM9V$))NdsUUZ>hlV)59V4#W zZJ_+jj0I6ni6V5L(+Giiw;~YiBiIwvs&&!jrT3NBLhy6lLYLmvRnt;eSNq=eb!o6{ zl>>i08*jQ9!CpPXaYDPu8*@3t+*?`X|I6Mg)fKpYSey+z97?h%(Gxa9F~?d*t~~%4 z(5Z4y+1;HlaVgIUH&1P~{4UBDvph6igr`RKP>R)<37^xEdBjp|oUC{*BG_R6?J6%>`(}N-?Wcblti)>Yh?UzQ9m|Z`__#YrvoV~}B@;YqFS;bE<~ z6TEvy{xIJEPZaIov~H@ST9^m5w&-1_K8n-h?X5*E(Au?VAJ<0l;#xeHgpW?p=0Se} z(pI3dFhUca<<;_;CiVJ8KH2Q!LuiYfmaVL|myD(&HcSds7276jp-BtDHQ)my`&B!b zVTFo99U&s)X$rTPU`)+9r*`=5;s?^)sM!0xRco?jpyXU%vg;tGoa@U83~aUk34jNf zkY+hqVY7S)j0sJZ`8~tpH#xW9pn894pjxZD)Td$eDvVx*(b@?*2mfGL04`?DXMC)7 z1~GtMVNfbsUh|;REVTl-6nCvzqUOb6LI zp8?yZj)HR2;}R2S=~cd(kdKYkJZvHOZbprk`~SsTdwbs6TgBGiDz?s6v2{SUitT8t z*p8mWR+OhzsmQN(MHy?r9nU_E?5hdhSG%9BpHj|Veym}z@xF52-omry1J=w6{`nVK K#fX%Y4FCY_!|aFv delta 987 zcmV<110?*S2%!iQABzY80000000ZS$|4-vK6weNk6IOrt%-MC`I?m@ujhmaB`zSFf zUpD$*e)r4S#b3XkBTElZqi_Ea$6_40RJel%Lj8ndVh1oxslg~tu+r2-ZtmXQ6bYQt z$el4d&!jRgils1Afa;tPlSG8hSWNw#Xdxz;VVL_m#+*in!NJOvVTRLzVPawMkkfux zFbW}O;ue1;ijE>TF(%X9FpP-^CYn&rW-1WWgd$7Bp_E1&g|qj^K__U3glQATTCW8` z%0vJh7b(gkSDOW=I-%68aO9jpBgj!8LxKuY3z=k1p?lDdC6}s;q=MYh9vb5KcC6%7 z5WkcRIk0+Y`Dy;fic(SQE{7VHSPR^-Vwpi&#p{0@Ws)yOjProWaMoFqHU|^^^@w6K zRa8e*pSS~Xm%DiAkCDjz8JpxseCHP|7h$~+@xz&(T#Xd$Ciw1sC#3o~H1y6-Q|`xh zw}J9AGZsWSC5q5}P9p^7-HJf4k6=$wtJX!Am)=)i3&GEI3tf6wS4~S@UG00<*QLR- zRStjjY`p1a1bg)i#|iBsZ_MQob8lsl|1W#1R9E2oVR1I>a45;5L{HcZ#T;uLx%L2H zK&Q$*Wp{VJ#HBnZ+&s0_^1CQs%<|B55uO^^Ln&5cCVWmu<`GM=akApMh+u>Lzo%Fk z>BFA44*g8Ym}<@7RC`8EsW+&i?wj@Twx54$uoA1mBUWyMbSyJ!lb03a z1%I6)0MA~hnY>buC~Th61l~r3R^#b(^v!yKUrdl?lR^6s+>z&`Cw0QZlY?6EVXe3m zyn9;wFxdY|2<_msZlhA{oE~p&9fDe%wJXs+t_|YFm3S@(A03^|69Rvv zEj?vLgzh`b%j7fN>Gd6avf03g&<{B+dsuA~8BIlOm=vh0vQ5@PlNN&h|ACSHst?St zLPdp+5RvgTh3iW&rskYeJN$O>18HuQ>iyoTCs{I3a;`7gbr4g|_2mQxw#@$ozynN3 zvz)B3Sv~~Dgr>^;o?-EuoLg{EwlsfGt<@#!(=d7!Mz6wX?F5~Je=sZn7qjLwK2|$} z7(lNuC>1TQc~EJVS^?Y!zcEI!;ndKkTLO2h5I)jp0y^Ray))pyjBb^aITHk?gKVA8 zfNfJpLAmL1i3#-bDql^=$Hr|TW71-I$J-*cC=M& zM^9oa%G0V; +a <- tt(mtcars[1:4, 1:4], output = "latex") |> style_tt(color = "orange", background = "black") |> group_tt(j = list("blah" = 1:2, "bar" = 3:4)) -b <- tt(mtcars[1:4, 1:4], "latex") |> +b <- tt(mtcars[1:4, 1:4], output = "latex") |> group_tt(j = list("blah" = 1:2, "bar" = 3:4)) |> style_tt(color = "orange", background = "black") expect_snapshot_print(a, label = "latex-group_style_order") diff --git a/man/format_tt.Rd b/man/format_tt.Rd index 7d0f1ec2..d9fe049f 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -8,12 +8,13 @@ format_tt( x = NULL, j = NULL, output = NULL, - digits = NULL, + digits = getOption("digits"), num_fmt = "significant", num_zero = TRUE, num_suffix = FALSE, num_mark_big = "", num_mark_dec = getOption("OutDec", default = "."), + sprintf = NULL, url = FALSE, date = "\%Y-\%m-\%d", bool = function(column) tools::toTitleCase(tolower(column)), @@ -39,6 +40,8 @@ format_tt( \item{num_mark_dec}{Decimal mark character. Default is the global option 'OutDec'.} +\item{sprintf}{String passed to the \code{?sprintf} function to format numbers or interpolate strings with a user-defined pattern (similar to the \code{glue} package, but using Base R).} + \item{url}{Logical; if TRUE, treats the column as a URL.} \item{date}{A string passed to the \code{format()} function, such as "\%Y-\%m-\%d". See the "Details" section in \code{?strptime}} diff --git a/man/tt.Rd b/man/tt.Rd index 0b119045..f7074bc1 100644 --- a/man/tt.Rd +++ b/man/tt.Rd @@ -7,7 +7,7 @@ tt( x, output = NULL, - digits = NULL, + digits = getOption("digits"), align = NULL, caption = NULL, width = NULL, diff --git a/vignettes/tutorial.qmd b/vignettes/tutorial.qmd index a32c6c24..36027f7f 100644 --- a/vignettes/tutorial.qmd +++ b/vignettes/tutorial.qmd @@ -9,10 +9,6 @@ format: keep-tex: true --- -```{r, include = FALSE} -options("tt_tabularray_placement" = "H") -``` - \clearpage `tinytable` is a small but powerful `R` package to draw HTML, LaTeX, PDF, Markdown, and Typst tables. The interface is minimalist, but it gives users direct and convenient access to powerful frameworks to create endlessly customizable tables. @@ -32,8 +28,18 @@ This tutorial introduces the main functions of the package. It is available in t # Tiny Tables +Load the library and set some global options: + ```{r} library(tinytable) + +options(digits = 3) # how many significant digits to print by default +options("tt_tabularray_placement" = "H") # for LaTeX +``` + +Draw a first table: + +```{r} x <- mtcars[1:4, 1:5] tt(x) ``` @@ -104,7 +110,7 @@ In a very simple case---such as printing 2 significant digits of all numeric var ```{r} dat <- data.frame( - w = c(1.43002, 201.399, 0.100188), + w = c(143002.2092, 201399.181, 100188.3883), x = c(1.43002, 201.399, 0.100188), y = as.Date(sample(1:1000, 3)), z = c(TRUE, TRUE, FALSE)) @@ -112,17 +118,9 @@ dat <- data.frame( tt(dat, digits = 2) ``` -For each numeric column, `tinytable` checked how many numbers after the decimal where necessary for every number to display at least two digits. - We can get more fine-grained control over formatting with calling `format_tt()` before `tt()`: ```{r} -dat <- data.frame( - w = c(143002.2092, 201399.181, 100188.3883), - x = c(1.43002, 201.399, 0.100188), - y = as.Date(sample(1:1000, 3)), - z = c(TRUE, TRUE, FALSE)) - dat |> format_tt( j = 2:4, @@ -137,6 +135,26 @@ dat |> tt() ``` +We can use a regular expression in `j` to select columns, and the `?sprintf` function to format strings, numbers, and to do [string interpolation:](https://en.wikipedia.org/wiki/String_interpolation) + +```{r} +dat <- data.frame( + a = c("Burger", "Halloumi", "Tofu"), + b = c(1.43002, 201.399, 0.100188)) + +dat |> + format_tt(j = "a", sprintf = "Food: %s") |> + tt() +``` + +Finally, if you like the `format_tt()` interface, you can use it with vectors too: + +```{r} +k <- c(98938272783457, 7288839482, 29111727, 93945, 922) +format_tt(k, digits = 1, num_suffix = TRUE) +``` + + ## Width The `width` arguments accepts a number between 0 and 1, indicating what proportion of the linewidth the table should cover: