From 4a2416637cf4b661b95a53c4331f73a3731baca2 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Fri, 27 Dec 2024 08:10:08 -0500 Subject: [PATCH] issue #409 replace NaN --- R/format_tt.R | 19 ++++++++++------- R/sanity.R | 39 +++++++++++++++++----------------- inst/tinytest/test-format_tt.R | 14 ++++++++++++ man/format_tt.Rd | 6 +++--- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/R/format_tt.R b/R/format_tt.R index d2d5277d..9c560485 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -18,9 +18,9 @@ #' @param math Logical. If TRUE, wrap cell values in math mode `$..$`. This is useful for LaTeX output or with HTML MathJax `options(tinytable_html_mathjax=TRUE)`. #' @param other A function to format columns of other types. Defaults to `as.character()`. #' @param replace Logical, String or Named list of vectors -#' - TRUE: Replace `NA` by an empty string. -#' - FALSE: Print `NA` as the string "NA". -#' - String: Replace `NA` entries by the user-supplied string. +#' - TRUE: Replace `NA` and `NaN` by an empty string. +#' - FALSE: Print `NA` and `NaN` as strings. +#' - String: Replace `NA` and `NaN` entries by the user-supplied string. #' - Named list: Replace matching elements of the vectors in the list by theirs names. Example: #' - `list("-" = c(NA, NaN), "Tiny" = -Inf, "Massive" = Inf)` #' @param escape Logical or "latex" or "html". If TRUE, escape special characters to display them as text in the format of the output of a `tt()` table. @@ -94,7 +94,6 @@ format_tt <- function(x, quarto = get_option("tinytable_format_quarto", default = FALSE), fn = get_option("tinytable_format_fn", default = NULL), sprintf = get_option("tinytable_format_sprintf", default = NULL)) { - assert_integerish(digits, len = 1, null.ok = TRUE) assert_choice(num_fmt, c("significant", "significant_cell", "decimal", "scientific")) assert_flag(num_zero) @@ -227,11 +226,11 @@ format_tt_lazy <- function(x, if (!is.null(bool) && is.logical(ori[i, col])) { out[i, col] <- bool(ori[i, col, drop = TRUE]) - # date + # date } else if (!is.null(date) && inherits(ori[i, col], "Date")) { out[i, col] <- format(ori[i, col, drop = TRUE], date) - # numeric + # numeric } else if (!is.null(digits) && is.numeric(ori[i, col, drop = TRUE])) { tmp <- format_numeric(ori[i, col], num_suffix = num_suffix, @@ -243,7 +242,7 @@ format_tt_lazy <- function(x, ) if (!is.null(tmp)) out[i, col] <- tmp - # other + # other } else if (is.function(other)) { out[i, col] <- other(ori[i, col, drop = TRUE]) } @@ -251,7 +250,11 @@ format_tt_lazy <- function(x, for (k in seq_along(replace)) { idx <- ori[i, col, drop = TRUE] %in% replace[[k]] - out[i, col][idx] <- names(replace)[[k]] + if (identical(names(replace)[[k]], " ")) { + out[i, col][idx] <- "" + } else { + out[i, col][idx] <- names(replace)[[k]] + } } } # loop over columns diff --git a/R/sanity.R b/R/sanity.R index ff119a2f..61f7d6f7 100644 --- a/R/sanity.R +++ b/R/sanity.R @@ -13,23 +13,23 @@ sanity_align <- function(align, i) { sanitize_i <- function(i, x, pre_group_i = FALSE, lazy = TRUE) { - out <- seq_len(nrow(x)) - if (is.null(i) && isTRUE(lazy)) { - out <- NA - attr(out, "null") <- TRUE - attr(out, "body") <- seq_len(nrow(x)) - attr(out, "head") <- integer() - } else { - if (!is.null(i)) { - out <- i - } else if (inherits(x, "tinytable")) { - out <- seq_len(nrow(x@table_dataframe)) - } - attr(out, "null") <- FALSE - attr(out, "body") <- out[out > 0] - attr(out, "head") <- out[out < 1] + out <- seq_len(nrow(x)) + if (is.null(i) && isTRUE(lazy)) { + out <- NA + attr(out, "null") <- TRUE + attr(out, "body") <- seq_len(nrow(x)) + attr(out, "head") <- integer() + } else { + if (!is.null(i)) { + out <- i + } else if (inherits(x, "tinytable")) { + out <- seq_len(nrow(x@table_dataframe)) } - return(out) + attr(out, "null") <- FALSE + attr(out, "body") <- out[out > 0] + attr(out, "head") <- out[out < 1] + } + return(out) } sanitize_i <- function(i, x, pre_group_i = FALSE, lazy = TRUE) { if (is.character(i)) { @@ -430,11 +430,12 @@ sanitize_notes <- function(notes) { sanitize_replace <- function(replace) { if (isTRUE(replace)) { - replace <- stats::setNames(list(NA), "") + replace <- stats::setNames(list(NA, NaN), c(" ", " ")) } else if (isFALSE(replace)) { - replace <- stats::setNames(list(NULL), "") + replace <- list(NULL) } else if (isTRUE(check_string(replace))) { - replace <- stats::setNames(list(NA), replace) + if (identical(replace, "")) replace <- " " + replace <- stats::setNames(list(NA, NaN), c(replace, replace)) } else if (!is.list(replace) || is.null(names(replace))) { stop("`replace` should be TRUE/FALSE, a single string, or a named list.", call. = FALSE) } diff --git a/inst/tinytest/test-format_tt.R b/inst/tinytest/test-format_tt.R index f2195b35..87becb74 100644 --- a/inst/tinytest/test-format_tt.R +++ b/inst/tinytest/test-format_tt.R @@ -216,3 +216,17 @@ expect_true("16 GB" %in% tab$memory) expect_true("99%" %in% tab$speed_benchmark) expect_false("2024-01-15" %in% tab$date_lookup) + +# Issue #409: both NA and NaN should be replaced +options(tinytable_format_replace = NULL) +tab <- data.frame(x = c(1, NA, NaN, Inf)) +tab0 <- tt(tab) |> print("dataframe") +tab1 <- tt(tab) |> + format_tt() |> + print("dataframe") +tab2 <- tt(tab) |> + format_tt(replace = TRUE) |> + print("dataframe") +expect_equivalent(tab0$x, c("1", "NA", "NaN", "Inf")) +expect_equivalent(tab1$x, c("1", "NA", "NaN", "Inf")) +expect_equivalent(tab2$x, c("1", "", "", "Inf")) diff --git a/man/format_tt.Rd b/man/format_tt.Rd index 1a63747b..35294da9 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -61,9 +61,9 @@ format_tt( \item{replace}{Logical, String or Named list of vectors \itemize{ -\item TRUE: Replace \code{NA} by an empty string. -\item FALSE: Print \code{NA} as the string "NA". -\item String: Replace \code{NA} entries by the user-supplied string. +\item TRUE: Replace \code{NA} and \code{NaN} by an empty string. +\item FALSE: Print \code{NA} and \code{NaN} as strings. +\item String: Replace \code{NA} and \code{NaN} entries by the user-supplied string. \item Named list: Replace matching elements of the vectors in the list by theirs names. Example: \itemize{ \item \code{list("-" = c(NA, NaN), "Tiny" = -Inf, "Massive" = Inf)}