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/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..1329fd39 --- /dev/null +++ b/R/format_tt.R @@ -0,0 +1,168 @@ +# 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 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). +#' @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. +#' @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. +#' @export +format_tt <- function(x = NULL, + j = NULL, + output = 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)), + other = identity + ) { + + if (inherits(x, "tinytable")) { + 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) + 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_string(date) + assert_function(bool) + assert_function(identity) + assert_string(sprintf, null.ok = TRUE) + + output <- sanitize_output(output) + + # 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) { + + # sprintf() is self-contained + if (!is.null(sprintf)) { + x[[col]] <- base::sprintf(sprintf, x[[col]]) + + } else { + + # 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]]) + } else { + return(x) + } + +} + + + +format_num_suffix <- function(x, digits, num_mark_big, num_mark_dec, num_zero) { + suffix <- number <- rep("", length(x)) + 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 > 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/sanity.R b/R/sanity.R index d1804d9e..9897c1d0 100644 --- a/R/sanity.R +++ b/R/sanity.R @@ -182,3 +182,24 @@ 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) + } +} + +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/R/tt.R b/R/tt.R index 9b6cf9c7..96cd75d8 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 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. @@ -32,6 +33,7 @@ #' @export tt <- function(x, output = NULL, + digits = getOption("digits"), align = NULL, caption = NULL, width = NULL, @@ -45,7 +47,13 @@ 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) + # 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/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 86111b4a..27bcabda 100644 Binary files a/inst/tinytest/_tinysnapshot/html-heatmap.rds and b/inst/tinytest/_tinysnapshot/html-heatmap.rds differ diff --git a/inst/tinytest/_tinysnapshot/html-issue58.rds b/inst/tinytest/_tinysnapshot/html-issue58.rds index 1d6e3c98..0cd373d8 100644 Binary files a/inst/tinytest/_tinysnapshot/html-issue58.rds and b/inst/tinytest/_tinysnapshot/html-issue58.rds differ diff --git a/inst/tinytest/_tinysnapshot/html-striped.rds b/inst/tinytest/_tinysnapshot/html-striped.rds index 260e7287..6f67d582 100644 Binary files a/inst/tinytest/_tinysnapshot/html-striped.rds and b/inst/tinytest/_tinysnapshot/html-striped.rds differ diff --git a/inst/tinytest/_tinysnapshot/html-striped_orange.rds b/inst/tinytest/_tinysnapshot/html-striped_orange.rds index 2a1e3557..a9459637 100644 Binary files a/inst/tinytest/_tinysnapshot/html-striped_orange.rds and b/inst/tinytest/_tinysnapshot/html-striped_orange.rds differ diff --git a/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds b/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds index 90e6d07c..adcae1c8 100644 Binary files a/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds and b/inst/tinytest/_tinysnapshot/html-vectorized_color_j.rds differ diff --git a/inst/tinytest/_tinysnapshot/latex-align.txt b/inst/tinytest/_tinysnapshot/latex-align.txt index d50e7a2d..21ffd88f 100644 --- a/inst/tinytest/_tinysnapshot/latex-align.txt +++ b/inst/tinytest/_tinysnapshot/latex-align.txt @@ -18,9 +18,9 @@ cell{1}{5}={}{halign=r,}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-cell_color.txt b/inst/tinytest/_tinysnapshot/latex-cell_color.txt index accc7388..272afc56 100644 --- a/inst/tinytest/_tinysnapshot/latex-cell_color.txt +++ b/inst/tinytest/_tinysnapshot/latex-cell_color.txt @@ -16,9 +16,9 @@ cell{3}{4}={}{,fg=orange,}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-col_color.txt b/inst/tinytest/_tinysnapshot/latex-col_color.txt index c2aa551f..63b16b69 100644 --- a/inst/tinytest/_tinysnapshot/latex-col_color.txt +++ b/inst/tinytest/_tinysnapshot/latex-col_color.txt @@ -12,9 +12,9 @@ column{4}={,fg=orange,}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-default.txt b/inst/tinytest/_tinysnapshot/latex-default.txt index 183f3d55..60238f56 100644 --- a/inst/tinytest/_tinysnapshot/latex-default.txt +++ b/inst/tinytest/_tinysnapshot/latex-default.txt @@ -9,9 +9,9 @@ row{even}={bg=black!5!white}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-group_style_order.txt b/inst/tinytest/_tinysnapshot/latex-group_style_order.txt index 29db966a..2ad17d73 100644 --- a/inst/tinytest/_tinysnapshot/latex-group_style_order.txt +++ b/inst/tinytest/_tinysnapshot/latex-group_style_order.txt @@ -15,9 +15,9 @@ cell{1}{3}={c=2,}{halign=c,}, \toprule blah & & bar & \\ \cmidrule[lr]{1-2}\cmidrule[lr]{3-4} mpg & cyl & disp & hp \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 \\ -21 & 6 & 160 & 110 \\ -22.8 & 4 & 108 & 93 \\ +21.0 & 6 & 160 & 110 \\ +21.0 & 6 & 160 & 110 \\ +22.8 & 4 & 108 & 93 \\ 21.4 & 6 & 258 & 110 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-nohead.txt b/inst/tinytest/_tinysnapshot/latex-nohead.txt index 3eb3bdc6..38fdb397 100644 --- a/inst/tinytest/_tinysnapshot/latex-nohead.txt +++ b/inst/tinytest/_tinysnapshot/latex-nohead.txt @@ -7,9 +7,9 @@ colspec={Q[]Q[]Q[]Q[]Q[]}, } %% tabularray inner close \toprule -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-row_color.txt b/inst/tinytest/_tinysnapshot/latex-row_color.txt index 4ad4e5cf..231eedc7 100644 --- a/inst/tinytest/_tinysnapshot/latex-row_color.txt +++ b/inst/tinytest/_tinysnapshot/latex-row_color.txt @@ -12,9 +12,9 @@ row{5}={,fg=orange,}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-theme_grid.txt b/inst/tinytest/_tinysnapshot/latex-theme_grid.txt index 314dfc4a..26608930 100644 --- a/inst/tinytest/_tinysnapshot/latex-theme_grid.txt +++ b/inst/tinytest/_tinysnapshot/latex-theme_grid.txt @@ -9,9 +9,9 @@ colspec={Q[]Q[]Q[]Q[]Q[]}, hlines={},vlines={}, } %% tabularray inner close mpg & cyl & disp & hp & drat \\ -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \end{tblr} \end{table} diff --git a/inst/tinytest/_tinysnapshot/latex-theme_striped.txt b/inst/tinytest/_tinysnapshot/latex-theme_striped.txt index 183f3d55..60238f56 100644 --- a/inst/tinytest/_tinysnapshot/latex-theme_striped.txt +++ b/inst/tinytest/_tinysnapshot/latex-theme_striped.txt @@ -9,9 +9,9 @@ row{even}={bg=black!5!white}, } %% tabularray inner close \toprule mpg & cyl & disp & hp & drat \\ \midrule %% TinyTableHeader -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \bottomrule \end{tblr} diff --git a/inst/tinytest/_tinysnapshot/latex-theme_void.txt b/inst/tinytest/_tinysnapshot/latex-theme_void.txt index ed206c82..268baf35 100644 --- a/inst/tinytest/_tinysnapshot/latex-theme_void.txt +++ b/inst/tinytest/_tinysnapshot/latex-theme_void.txt @@ -7,9 +7,9 @@ colspec={Q[]Q[]Q[]Q[]Q[]}, } %% tabularray inner close mpg & cyl & disp & hp & drat \\ -21 & 6 & 160 & 110 & 3.9 \\ -21 & 6 & 160 & 110 & 3.9 \\ -22.8 & 4 & 108 & 93 & 3.85 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +21.0 & 6 & 160 & 110 & 3.90 \\ +22.8 & 4 & 108 & 93 & 3.85 \\ 21.4 & 6 & 258 & 110 & 3.08 \\ \end{tblr} \end{table} diff --git a/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt b/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt index 5097abce..a597e794 100644 --- a/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt +++ b/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt @@ -1,11 +1,11 @@ | 5.1 | 3.5 | 1.4 | 0.2 | setosa | -| 4.9 | 3 | 1.4 | 0.2 | setosa | +| 4.9 | 3.0 | 1.4 | 0.2 | setosa | | 4.7 | 3.2 | 1.3 | 0.2 | setosa | | 4.6 | 3.1 | 1.5 | 0.2 | setosa | -| 5 | 3.6 | 1.4 | 0.2 | setosa | +| 5.0 | 3.6 | 1.4 | 0.2 | setosa | | 5.4 | 3.9 | 1.7 | 0.4 | setosa | | 4.6 | 3.4 | 1.4 | 0.3 | setosa | -| 5 | 3.4 | 1.5 | 0.2 | setosa | +| 5.0 | 3.4 | 1.5 | 0.2 | setosa | | 4.4 | 2.9 | 1.4 | 0.2 | setosa | | 4.9 | 3.1 | 1.5 | 0.1 | setosa | diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R new file mode 100644 index 00000000..3c9b96f9 --- /dev/null +++ b/inst/tinytest/test-format_tt.R @@ -0,0 +1,12 @@ +# 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() +# dat |> +# format_tt(digits = 2, num_suffix = TRUE) |> +# tt() diff --git a/inst/tinytest/test-latex.R b/inst/tinytest/test-latex.R index 94b7e487..1e934dd4 100644 --- a/inst/tinytest/test-latex.R +++ b/inst/tinytest/test-latex.R @@ -46,10 +46,10 @@ expect_snapshot_print( # Lazy style: group after style is respected -a <- tt(mtcars[1:4, 1:4], "latex") |> +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 new file mode 100644 index 00000000..d9fe049f --- /dev/null +++ b/man/format_tt.Rd @@ -0,0 +1,60 @@ +% 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 = 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)), + other = identity +) +} +\arguments{ +\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.} + +\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_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{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}} + +\item{bool}{A function to format logical columns. Defaults to title case.} + +\item{other}{A function to format columns of other types. Defaults to identity (no formatting).} +} +\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. +} diff --git a/man/tt.Rd b/man/tt.Rd index e6b19286..f7074bc1 100644 --- a/man/tt.Rd +++ b/man/tt.Rd @@ -7,6 +7,7 @@ tt( x, output = NULL, + digits = getOption("digits"), 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 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}.} \item{caption}{A string that will be used as the caption of the table.} diff --git a/vignettes/tutorial.qmd b/vignettes/tutorial.qmd index ef288104..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) ``` @@ -95,6 +101,60 @@ 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(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)) + +tt(dat, digits = 2) +``` + +We can get more fine-grained control over formatting with calling `format_tt()` before `tt()`: + +```{r} +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() +``` + +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: @@ -289,6 +349,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: