diff --git a/NAMESPACE b/NAMESPACE index 93dc9004e..228966891 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -22,6 +22,7 @@ export("obj_format<-") export("obj_label<-") export("obj_na_str<-") export("obj_name<-") +export("obj_round_type<-") export("page_titles<-") export("prov_footer<-") export("subtitles<-") @@ -83,6 +84,7 @@ export(obj_format) export(obj_label) export(obj_na_str) export(obj_name) +export(obj_round_type) export(open_font_dev) export(padstr) export(pag_indices_inner) @@ -107,6 +109,7 @@ export(subtitles) export(table_inset) export(toString) export(undebug_font_dev) +export(valid_round_type) export(var_labels) export(var_labels_remove) export(var_relabel) @@ -123,6 +126,7 @@ exportMethods("obj_align<-") exportMethods("obj_format<-") exportMethods("obj_label<-") exportMethods("obj_na_str<-") +exportMethods("obj_round_type<-") exportMethods("page_titles<-") exportMethods("prov_footer<-") exportMethods("subtitles<-") @@ -139,6 +143,7 @@ exportMethods(obj_align) exportMethods(obj_format) exportMethods(obj_label) exportMethods(obj_na_str) +exportMethods(obj_round_type) exportMethods(page_titles) exportMethods(prov_footer) exportMethods(subtitles) diff --git a/NEWS.md b/NEWS.md index dc4a3a2e2..1c524ea4e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,8 +2,12 @@ * Optimized pagination sub-routines to avoid `matrix_form()` calls when not needed. * Optimized pagination sub-routine `.compress_mat()` to reduce computing time for long listings. * Fixed bug in pagination of listings caused by newlines in column values. -* Added `"default"` format label which will behave like `"xx"` but can - inherit formatting from parent structures in upstream code. +* Added `"default"` format label which behaves like `"xx"` in `format_value` but indicates formatting behavior can be inherited from parent structures in upstream code. +* `round_type = "sas"` no longer displays a negative sign when negative values are rounded to zero. +* Added new `round_type`, `"iec_mod"`. Provides IEC style rounding but will not display negative sign when rounding to zero. +* New exported `valid_round_type` object for use as default value/with `match.arg` in upstream packages. +* New `obj_round_type` and `obj_round_type<-` generics for objects which carry around a round_type. +* Updated default round type value to retrieve the object's round type for all generics and relevant methods which accept round_type. ## formatters 0.5.11 * Fixed a bug in `mform_handle_newlines` that caused string matrix column names to be removed. This prevented paginated listing key column info from being repeated when vertically spanning multiple pages. diff --git a/R/format_value.R b/R/format_value.R index a781ee78c..a6c09f2da 100644 --- a/R/format_value.R +++ b/R/format_value.R @@ -164,6 +164,10 @@ sprintf_format <- function(format) { } } +#' @rdname round_fmt +#' @export +valid_round_type <- c("iec", "iec_mod", "sas") + #' Round and prepare a value for display #' #' This function is used within [format_value()] to prepare numeric values within @@ -173,9 +177,14 @@ sprintf_format <- function(format) { #' @param digits (`numeric(1)`)\cr number of digits to round to, or `NA` to convert to a #' character value with no rounding. #' @param na_str (`string`)\cr the value to return if `x` is `NA`. -#' @param round_type (`"iec"` or `"sas"`)\cr the type of rounding to perform. iec, -#' the default, peforms rounding compliant with IEC 60559 (see details), while -#' sas performs nearest-value rounding consistent with rounding within SAS. +#' @param round_type (`string`)\cr . +#' \cr The type of rounding to perform. Allowed values: (`"iec"`, `"iec_mod"` or `"sas"`) +#' \cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +#' (see notes in [round_fmt()]), while +#' sas performs nearest-value rounding consistent with rounding within SAS.\cr +#' In addition, the rounding of a negative number that rounds to zero will be presented as 0 +#' (with the appropriate number of trailing zeros) for both `sas` and `iec_mod`, +#' while for `iec`, it will be presented as -0 (with the appropriate number of trailing zeros). #' #' @details #' This function combines rounding behavior with the strict decimal display of @@ -214,10 +223,13 @@ sprintf_format <- function(format) { #' round_fmt(2.765923, digits = NA) #' round_fmt(0.845, digits = 2) #' round_fmt(0.845, digits = 2, round_type = "sas") +#' round_fmt(-0.001, digits = 2, round_type = "iec") +#' round_fmt(-0.001, digits = 2, round_type = "sas") +#' round_fmt(-0.001, digits = 2, round_type = "iec_mod") #' #' @export #' @aliases rounding -round_fmt <- function(x, digits, na_str = "NA", round_type = c("iec", "sas")) { +round_fmt <- function(x, digits, na_str = "NA", round_type = valid_round_type) { round_type <- match.arg(round_type) if (!is.na(digits) && digits < 0) { stop("round_fmt currently does not support non-missing values of digits < 0") @@ -229,6 +241,7 @@ round_fmt <- function(x, digits, na_str = "NA", round_type = c("iec", "sas")) { } else { rndx <- switch(round_type, iec = round(x, digits), + iec_mod = round_iec_mod(x, digits), sas = round_sas(x, digits) ) sprfmt <- paste0("%.", digits, "f") @@ -248,13 +261,28 @@ round_sas <- function(x, z <- z + 0.5 + sqrt(.Machine$double.eps) z <- trunc(z) z <- z / 10^digits - z <- z * posneg + # only include sign when rounded value is not zero + if (z != 0) z <- z * posneg + ## return numeric vector of rounded values + z +} + +#' @inheritParams round_fmt +#' +round_iec_mod <- function(x, + digits = 0) { + # perform default rounding ---------------------------------------------------- + posneg <- sign(x) + z <- round(abs(x), digits) + # only include sign when rounded value is not zero + if (z != 0) z <- z * posneg ## return numeric vector of rounded values z } -val_pct_helper <- function(x, dig1, dig2, na_str, pct = TRUE, round_type = c("iec", "sas")) { +val_pct_helper <- function(x, dig1, dig2, na_str, pct = TRUE, round_type = valid_round_type) { + round_type <- match.arg(round_type) if (pct) { x[2] <- x[2] * 100 } @@ -269,7 +297,8 @@ val_pct_helper <- function(x, dig1, dig2, na_str, pct = TRUE, round_type = c("ie ) } -sep_2d_helper <- function(x, dig1, dig2, sep, na_str, wrap = NULL, round_type = c("iec", "sas")) { +sep_2d_helper <- function(x, dig1, dig2, sep, na_str, wrap = NULL, round_type = valid_round_type) { + round_type <- match.arg(round_type) ret <- paste(mapply(round_fmt, x = x, digits = c(dig1, dig2), na_str = na_str, round_type = round_type), collapse = sep ) @@ -312,7 +341,7 @@ sep_2d_helper <- function(x, dig1, dig2, sep, na_str, wrap = NULL, round_type = #' format_value(c(NA, 1, NA), format = "xx.x (xx.x - xx.x)", na_str = c("NE", "")) #' #' @export -format_value <- function(x, format = NULL, output = c("ascii", "html"), na_str = "NA", round_type = c("iec", "sas")) { +format_value <- function(x, format = NULL, output = c("ascii", "html"), na_str = "NA", round_type = valid_round_type) { ## if(is(x, "CellValue")) ## x = x[[1]] diff --git a/R/generics.R b/R/generics.R index fb5e46dfd..9f6a70ba9 100644 --- a/R/generics.R +++ b/R/generics.R @@ -68,7 +68,7 @@ setGeneric("make_row_df", function(tt, colwidths = NULL, visible_only = TRUE, max_width = NULL, fontspec = font_spec(), col_gap = 3L, - round_type = c("iec", "sas")) { + round_type = obj_round_type(tt)) { standardGeneric("make_row_df") }) @@ -85,7 +85,7 @@ setMethod("make_row_df", "MatrixPrintForm", function(tt, colwidths = NULL, visib max_width = NULL, fontspec = font_spec(), col_gap = mf_colgap(tt) %||% 3L, - round_type = c("iec", "sas")) { + round_type = obj_round_type(tt)) { msg <- paste0( "make_row_df can be used only on {rtables} table objects, and not on `matrix_form`-", "generated objects (MatrixPrintForm)." @@ -127,7 +127,7 @@ setGeneric("matrix_form", function(obj, indent_size = 2, fontspec = NULL, col_gap = NULL, - round_type = c("iec", "sas")) { + round_type = obj_round_type(obj)) { standardGeneric("matrix_form") }) @@ -140,7 +140,7 @@ setMethod("matrix_form", "MatrixPrintForm", function(obj, indent_size = 2, fontspec = NULL, col_gap = NULL, - round_type = c("iec", "sas")) { + round_type = obj_round_type(obj)) { if (!is.null(fontspec)) { mf_fontspec(obj) <- fontspec } @@ -737,3 +737,66 @@ setMethod( obj } ) + + +# obj_round_type --------------------------------------------------------------- + +#' Rounding Type +#' +#' When called on a table-like object using the formatters framework, this method returns the +#' rounding type of the object. +#' +#' @param obj (`ANY`)\cr a table-like object. +#' +#' @return The rounding type of the object (see [round_fmt()] for details). +#' @rdname obj_round_type +#' @export +setGeneric("obj_round_type", function(obj) standardGeneric("obj_round_type")) + +#' @rdname obj_round_type +#' @export +setMethod( + "obj_round_type", "MatrixPrintForm", function(obj) obj$round_type +) + +#' @rdname obj_round_type +#' @export +setMethod("obj_round_type", "list", function(obj) { + if (!.is_list_of_tables_or_listings(obj)) { + stop("got a list that doesn't appear to contain (only) tables or listings") + } + obj_round_type(obj[[1]]) +}) + +# obj_round_type setter --------------------------------------------------------------- +#' @rdname obj_round_type +#' @param value The new rounding type of the object (see [round_fmt()] for details) +#' @note The setter method should only be created/used for pre-MatrixPrintForm objects, +#' as resetting the rounding type after rounding occurs (which is during MPF creation) +#' will not effect output when printing/exporting. +#' @export +setGeneric("obj_round_type<-", function(obj, value) standardGeneric("obj_round_type<-")) + +#' @rdname obj_round_type +#' @export +setMethod("obj_round_type<-", "list", function(obj, value) { + if (!.is_list_of_tables_or_listings(obj)) { + stop("got a list that doesn't appear to contain (only) tables or listings") + } + obj <- lapply(obj, function(x) { + obj_round_type(x) <- value + x + }) + obj +}) + +#' @rdname obj_round_type +#' @export +#' @note round_type cannot not be updated on a `MatrixPrintForm` object +#' as rounding occurs during creation of MatrixPrintForm object +setMethod("obj_round_type<-", "MatrixPrintForm", function(obj, value) { + stop( + "Cannot alter round type on a `MatrixPrintForm` object as it was ", + "constructed after rounding occurred." + ) +}) diff --git a/R/matrix_form.R b/R/matrix_form.R index 3310c4a52..a884c32c6 100644 --- a/R/matrix_form.R +++ b/R/matrix_form.R @@ -260,6 +260,9 @@ disp_from_spans <- function(spans) { #' @param indent_size (`numeric(1)`)\cr number of spaces to be used per level of indent (if supported by #' the relevant method). Defaults to 2. #' @param rep_cols (`numeric(1)`)\cr number of columns to be repeated as context during horizontal pagination. +#' @param round_type (`string`)\cr +#' The type of rounding to perform. Allowed values: (`"iec"`, `"iec_mod"` or `"sas"`) +#' See [round_fmt()] for details. #' #' @return An object of class `MatrixPrintForm`. Currently this is implemented as an S3 class inheriting #' from list with the following elements: @@ -325,7 +328,9 @@ MatrixPrintForm <- function(strings = NULL, colwidths = NULL, indent_size = 2, fontspec = font_spec(), - rep_cols = 0L) { + rep_cols = 0L, + round_type = valid_round_type) { + round_type <- match.arg(round_type) display <- disp_from_spans(spans) ncs <- if (has_rowlabs) ncol(strings) - 1 else ncol(strings) @@ -353,7 +358,8 @@ MatrixPrintForm <- function(strings = NULL, indent_size = indent_size, col_widths = colwidths, fontspec = fontspec, - num_rep_cols = rep_cols + num_rep_cols = rep_cols, + round_type = round_type ), nrow_header = nrow_header, ncols = ncs, @@ -915,6 +921,9 @@ mf_has_rlabels <- function(mf) ncol(mf$strings) > mf_ncol(mf) #' @param num_rep_cols (`numeric(1)`)\cr Number of columns to be treated as repeating columns. #' Defaults to `0` for `basic_matrix_form` and `length(keycols)` for #' `basic_listing_mf`. Note repeating columns are separate from row labels if present. +#' @param round_type (`string`)\cr +#' The type of rounding to perform. Allowed values: (`"iec"`, `"iec_mod"` or `"sas"`) +#' See [round_fmt()] for details. #' #' @return A valid `MatrixPrintForm` object representing `df` that is ready for #' ASCII rendering. @@ -950,7 +959,8 @@ basic_matrix_form <- function(df, fontspec = font_spec(), split_labels = NULL, data_labels = NULL, - num_rep_cols = 0L) { + num_rep_cols = 0L, + round_type = valid_round_type) { checkmate::assert_data_frame(df) checkmate::assert_flag(indent_rownames) checkmate::assert_character(parent_path, null.ok = TRUE) @@ -958,6 +968,7 @@ basic_matrix_form <- function(df, checkmate::assert_flag(add_decoration) checkmate::assert_character(split_labels, null.ok = TRUE) checkmate::assert_character(data_labels, null.ok = TRUE) + round_type <- match.arg(round_type) # Some defaults row_classes <- "DataRow" # Default for all rows @@ -1016,7 +1027,7 @@ basic_matrix_form <- function(df, bodystrs <- mapply(function(x, coli_fmt) { coli_fmt[coli_fmt == "-"] <- "xx" sapply(seq_along(x), function(y) { - format_value(x[y], format = coli_fmt[y]) + format_value(x[y], format = coli_fmt[y], round_type = round_type) }) }, x = df, coli_fmt = fmts) @@ -1111,7 +1122,8 @@ basic_matrix_form <- function(df, fontspec = fontspec, col_gap = 3, indent_size = indent_size, - rep_cols = num_rep_cols + rep_cols = num_rep_cols, + round_type = round_type ) # Check for ncols @@ -1146,7 +1158,9 @@ basic_matrix_form <- function(df, basic_listing_mf <- function(df, keycols = names(df)[1], add_decoration = TRUE, - fontspec = font_spec()) { + fontspec = font_spec(), + round_type = valid_round_type) { + round_type <- match.arg(round_type) checkmate::assert_data_frame(df) checkmate::assert_subset(keycols, colnames(df)) @@ -1156,7 +1170,8 @@ basic_listing_mf <- function(df, ignore_rownames = TRUE, add_decoration = add_decoration, num_rep_cols = length(keycols), - fontspec = fontspec + fontspec = fontspec, + round_type = round_type ) # keycols addition to MatrixPrintForm (should happen in the constructor) diff --git a/R/mpf_exporters.R b/R/mpf_exporters.R index 56d3753b9..873787f40 100644 --- a/R/mpf_exporters.R +++ b/R/mpf_exporters.R @@ -60,7 +60,7 @@ export_as_txt <- function(x, page_num = default_page_number(), fontspec = font_spec(font_family, font_size, lineheight), col_gap = 3, - round_type = c("iec", "sas")) { + round_type = obj_round_type(x)) { # Processing lists of tables or listings if (.is_list_of_tables_or_listings(x)) { if (isFALSE(paginate)) { @@ -186,7 +186,7 @@ prep_header_line <- function(mf, i) { ## ) ## } -mpf_to_dfbody <- function(mpf, colwidths, fontspec, round_type = c("iec", "sas")) { +mpf_to_dfbody <- function(mpf, colwidths, fontspec, round_type = obj_round_type(mpf)) { mf <- matrix_form(mpf, indent_rownames = TRUE, fontspec = fontspec, round_type = round_type) nlr <- mf_nlheader(mf) if (is.null(colwidths)) { @@ -232,7 +232,7 @@ mpf_to_rtf <- function(mpf, font_size = 8, lineheight = 1, fontspec = font_spec(font_family, font_size, lineheight), - round_type = round_type, + round_type = obj_round_type(mpf), ...) { if (!requireNamespace("r2rtf")) { stop("RTF export requires the 'r2rtf' package, please install it.") @@ -420,6 +420,7 @@ export_as_rtf <- function(x, lineheight = 1, fontspec = font_spec(font_family, font_size, lineheight), paginate = TRUE, + round_type = obj_round_type(x), ...) { # Processing lists of tables or listings if (.is_list_of_tables_or_listings(x)) { @@ -464,6 +465,7 @@ export_as_rtf <- function(x, margins = c(bottom = 0, left = 0, top = 0, right = 0), lineheight = 1.25, colwidths = colwidths, + round_type = round_type, ... ) @@ -474,7 +476,8 @@ export_as_rtf <- function(x, pg_width = pg_width, pg_height = pg_height, font_size = fontspec$size, - margins = c(top = 0, left = 0, bottom = 0, right = 0) + margins = c(top = 0, left = 0, bottom = 0, right = 0), + round_type = round_type )) }) restxt <- paste( @@ -563,7 +566,7 @@ export_as_pdf <- function(x, colwidths = NULL, fontspec = font_spec(font_family, font_size, lineheight), ttype_ok = FALSE, - round_type = c("iec", "sas")) { + round_type = obj_round_type(x)) { ## this has to happen at the very beginning before the first use of fontspec ## which happens in the default value of colwidths. yay lazy evaluation... if (missing(font_size) && !missing(fontsize)) { diff --git a/R/pagination.R b/R/pagination.R index e86cba39c..a0a61543c 100644 --- a/R/pagination.R +++ b/R/pagination.R @@ -590,7 +590,7 @@ vert_pag_indices <- function(mf, rep_cols = 0L, fontspec, nosplitin = character(), - round_type = c("iec", "sas")) { + round_type = obj_round_type(mf)) { if (is.list(nosplitin)) { nosplitin <- nosplitin[["cols"]] } @@ -1001,7 +1001,7 @@ paginate_indices <- function(obj, rep_cols = num_rep_cols(obj), col_gap = 3, fontspec = font_spec(font_family, font_size, lineheight), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), verbose = FALSE) { ## this preserves backwards compatibility ## could start deprecation cycle of char input @@ -1201,7 +1201,7 @@ paginate_to_mpfs <- function(obj, # col_gap = 3, # this could be change in default - breaking change col_gap = 3, fontspec = font_spec(font_family, font_size, lineheight), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), verbose = FALSE) { newdev <- open_font_dev(fontspec) if (newdev) { @@ -1502,7 +1502,7 @@ diagnose_pagination <- function(obj, font_size, lineheight ), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), ...) { new_dev <- open_font_dev(fontspec) if (new_dev) { diff --git a/R/tostring.R b/R/tostring.R index f471bcc83..cea31f0e7 100644 --- a/R/tostring.R +++ b/R/tostring.R @@ -445,7 +445,7 @@ do_cell_fnotes_wrap <- function(mat, widths, max_width, tf_wrap, fontspec, expan } } # Final return - return(list("mfs" = mfs, "cell_widths_mat" = cell_widths_mat)) + list("mfs" = mfs, "cell_widths_mat" = cell_widths_mat) } ## take a character vector and return whether the value is @@ -646,7 +646,7 @@ setMethod("toString", "MatrixPrintForm", function(x, hsep = NULL, fontspec = font_spec(), ttype_ok = FALSE, - round_type = c("iec", "sas")) { + round_type = obj_round_type(x)) { checkmate::assert_flag(tf_wrap) ## we are going to use the pdf device and grid to understand the actual @@ -902,7 +902,7 @@ setMethod("toString", "MatrixPrintForm", function(x, stop("max_width must be NULL, a numeric value, or \"auto\".") } } - return(max_width) + max_width } .do_inset <- function(x, inset) { @@ -1100,10 +1100,10 @@ wrap_string <- function(str, width, collapse = NULL, fontspec = font_spec()) { } if (!is.null(collapse)) { - return(paste0(ret, collapse = collapse)) + ret <- paste0(ret, collapse = collapse) } - return(ret) + ret } .go_stri_wrap <- function(str, w) { @@ -1379,7 +1379,7 @@ spans_to_viscell <- function(spans) { propose_column_widths <- function(x, indent_size = 2, fontspec = font_spec(), - round_type = c("iec", "sas")) { + round_type = obj_round_type(x)) { new_dev <- open_font_dev(fontspec) if (new_dev) { on.exit(close_font_dev()) diff --git a/inst/WORDLIST b/inst/WORDLIST index fa54687d7..d34016701 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -7,6 +7,7 @@ IEC LPP MPF MPFs +MatrixPrintForm ORCID RTF Rua @@ -22,8 +23,8 @@ mandatorily monospace paginations pathing -peforms pre +repo sas subtable subtables diff --git a/man/MatrixPrintForm.Rd b/man/MatrixPrintForm.Rd index 67a9c9631..d47a84465 100644 --- a/man/MatrixPrintForm.Rd +++ b/man/MatrixPrintForm.Rd @@ -31,7 +31,8 @@ MatrixPrintForm( colwidths = NULL, indent_size = 2, fontspec = font_spec(), - rep_cols = 0L + rep_cols = 0L, + round_type = valid_round_type ) } \arguments{ @@ -110,6 +111,10 @@ the relevant method). Defaults to 2.} calculating string widths and heights, as returned by \code{\link[=font_spec]{font_spec()}}.} \item{rep_cols}{(\code{numeric(1)})\cr number of columns to be repeated as context during horizontal pagination.} + +\item{round_type}{(\code{string})\cr +The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +See \code{\link[=round_fmt]{round_fmt()}} for details.} } \value{ An object of class \code{MatrixPrintForm}. Currently this is implemented as an S3 class inheriting diff --git a/man/export_as_pdf.Rd b/man/export_as_pdf.Rd index 26ae91643..3eb58ba3b 100644 --- a/man/export_as_pdf.Rd +++ b/man/export_as_pdf.Rd @@ -31,7 +31,7 @@ export_as_pdf( colwidths = NULL, fontspec = font_spec(font_family, font_size, lineheight), ttype_ok = FALSE, - round_type = c("iec", "sas") + round_type = obj_round_type(x) ) } \arguments{ @@ -109,9 +109,14 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo allowed via \code{fontspec}. Defaults to \code{FALSE}. This parameter is primarily for internal testing and generally should not be set by end users.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \description{ The PDF output from this function is based on the ASCII output created with \code{\link[=toString]{toString()}}. diff --git a/man/export_as_rtf.Rd b/man/export_as_rtf.Rd index 689c35271..249ec0035 100644 --- a/man/export_as_rtf.Rd +++ b/man/export_as_rtf.Rd @@ -18,6 +18,7 @@ export_as_rtf( lineheight = 1, fontspec = font_spec(font_family, font_size, lineheight), paginate = TRUE, + round_type = obj_round_type(x), ... ) } @@ -57,6 +58,15 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo \item{paginate}{(\code{flag})\cr whether pagination should be performed. Defaults to \code{TRUE} if page size is specified (including the default).} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} + \item{...}{additional parameters passed to \code{\link[=paginate_to_mpfs]{paginate_to_mpfs()}}.} } \description{ diff --git a/man/export_as_txt.Rd b/man/export_as_txt.Rd index d566cc936..f3f08d074 100644 --- a/man/export_as_txt.Rd +++ b/man/export_as_txt.Rd @@ -32,7 +32,7 @@ export_as_txt( page_num = default_page_number(), fontspec = font_spec(font_family, font_size, lineheight), col_gap = 3, - round_type = c("iec", "sas") + round_type = obj_round_type(x) ) } \arguments{ @@ -113,9 +113,14 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo \item{col_gap}{(\code{numeric(1)})\cr The number of spaces to be placed between columns in the rendered table (and assumed for horizontal pagination).} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ If \code{file} is \code{NULL}, the full paginated and concatenated string value is returned, diff --git a/man/format_value.Rd b/man/format_value.Rd index 6060927fa..6a624d849 100644 --- a/man/format_value.Rd +++ b/man/format_value.Rd @@ -9,7 +9,7 @@ format_value( format = NULL, output = c("ascii", "html"), na_str = "NA", - round_type = c("iec", "sas") + round_type = valid_round_type ) } \arguments{ @@ -23,9 +23,14 @@ apply to \code{x}.} \item{na_str}{(\code{character})\cr character vector to display when the values of \code{x} are missing. If only one string is provided, it is applied for all missing values. Defaults to \code{"NA"}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ Formatted text representing the cell \code{x}. diff --git a/man/make_row_df.Rd b/man/make_row_df.Rd index 1f3183d16..14e3f06c0 100644 --- a/man/make_row_df.Rd +++ b/man/make_row_df.Rd @@ -20,7 +20,7 @@ make_row_df( max_width = NULL, fontspec = font_spec(), col_gap = 3L, - round_type = c("iec", "sas") + round_type = obj_round_type(tt) ) \S4method{make_row_df}{MatrixPrintForm}( @@ -38,7 +38,7 @@ make_row_df( max_width = NULL, fontspec = font_spec(), col_gap = mf_colgap(tt) \%||\% 3L, - round_type = c("iec", "sas") + round_type = obj_round_type(tt) ) } \arguments{ @@ -73,9 +73,14 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo \item{col_gap}{(\code{numeric(1)})\cr the gap to be assumed between columns, in number of spaces with font specified by \code{fontspec}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ A \code{data.frame} of row/column-structure information used by the pagination machinery. diff --git a/man/matrix_form.Rd b/man/matrix_form.Rd index 037846abb..82bab814d 100644 --- a/man/matrix_form.Rd +++ b/man/matrix_form.Rd @@ -12,7 +12,7 @@ matrix_form( indent_size = 2, fontspec = NULL, col_gap = NULL, - round_type = c("iec", "sas") + round_type = obj_round_type(obj) ) \S4method{matrix_form}{MatrixPrintForm}( @@ -22,7 +22,7 @@ matrix_form( indent_size = 2, fontspec = NULL, col_gap = NULL, - round_type = c("iec", "sas") + round_type = obj_round_type(obj) ) } \arguments{ @@ -44,9 +44,14 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo \item{col_gap}{(\code{numeric(1)})\cr the gap to be assumed between columns, in number of spaces with font specified by \code{fontspec}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ A \code{\link{MatrixPrintForm}} classed list with an additional \code{nrow_header} attribute indicating the diff --git a/man/mpf_to_rtf.Rd b/man/mpf_to_rtf.Rd index a98ff27dd..1578945a0 100644 --- a/man/mpf_to_rtf.Rd +++ b/man/mpf_to_rtf.Rd @@ -16,7 +16,7 @@ mpf_to_rtf( font_size = 8, lineheight = 1, fontspec = font_spec(font_family, font_size, lineheight), - round_type = round_type, + round_type = obj_round_type(mpf), ... ) } @@ -50,9 +50,9 @@ if the family named is not monospaced. Defaults to \code{"Courier"}.} \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for calculating string widths and heights, as returned by \code{\link[=font_spec]{font_spec()}}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr +The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +See \code{\link[=round_fmt]{round_fmt()}} for details.} \item{...}{additional parameters passed to individual methods.} } diff --git a/man/obj_round_type.Rd b/man/obj_round_type.Rd new file mode 100644 index 000000000..f80e18263 --- /dev/null +++ b/man/obj_round_type.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R +\name{obj_round_type} +\alias{obj_round_type} +\alias{obj_round_type,MatrixPrintForm-method} +\alias{obj_round_type,list-method} +\alias{obj_round_type<-} +\alias{obj_round_type<-,list-method} +\alias{obj_round_type<-,MatrixPrintForm-method} +\title{Rounding Type} +\usage{ +obj_round_type(obj) + +\S4method{obj_round_type}{MatrixPrintForm}(obj) + +\S4method{obj_round_type}{list}(obj) + +obj_round_type(obj) <- value + +\S4method{obj_round_type}{list}(obj) <- value + +\S4method{obj_round_type}{MatrixPrintForm}(obj) <- value +} +\arguments{ +\item{obj}{(\code{ANY})\cr a table-like object.} + +\item{value}{The new rounding type of the object (see \code{\link[=round_fmt]{round_fmt()}} for details)} +} +\value{ +The rounding type of the object (see \code{\link[=round_fmt]{round_fmt()}} for details). +} +\description{ +When called on a table-like object using the formatters framework, this method returns the +rounding type of the object. +} +\note{ +The setter method should only be created/used for pre-MatrixPrintForm objects, +as resetting the rounding type after rounding occurs (which is during MPF creation) +will not effect output when printing/exporting. + +round_type cannot not be updated on a \code{MatrixPrintForm} object +as rounding occurs during creation of MatrixPrintForm object +} diff --git a/man/paginate_indices.Rd b/man/paginate_indices.Rd index baea6e4ee..28c57b293 100644 --- a/man/paginate_indices.Rd +++ b/man/paginate_indices.Rd @@ -30,7 +30,7 @@ paginate_indices( rep_cols = num_rep_cols(obj), col_gap = 3, fontspec = font_spec(font_family, font_size, lineheight), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), verbose = FALSE ) @@ -57,7 +57,7 @@ paginate_to_mpfs( rep_cols = NULL, col_gap = 3, fontspec = font_spec(font_family, font_size, lineheight), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), verbose = FALSE ) @@ -85,7 +85,7 @@ diagnose_pagination( col_gap = 3, verbose = FALSE, fontspec = font_spec(font_family, font_size, lineheight), - round_type = c("iec", "sas"), + round_type = obj_round_type(obj), ... ) } @@ -150,9 +150,14 @@ in the rendered table (and assumed for horizontal pagination).} \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for calculating string widths and heights, as returned by \code{\link[=font_spec]{font_spec()}}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} \item{verbose}{(\code{flag})\cr whether additional informative messages about the search for pagination breaks should be shown. Defaults to \code{FALSE}.} diff --git a/man/propose_column_widths.Rd b/man/propose_column_widths.Rd index 89c8260f3..9ce633df2 100644 --- a/man/propose_column_widths.Rd +++ b/man/propose_column_widths.Rd @@ -8,7 +8,7 @@ propose_column_widths( x, indent_size = 2, fontspec = font_spec(), - round_type = c("iec", "sas") + round_type = obj_round_type(x) ) } \arguments{ @@ -20,9 +20,14 @@ a \code{MatrixPrintForm} object in favor of information there.} \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for calculating string widths and heights, as returned by \code{\link[=font_spec]{font_spec()}}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ A vector of column widths based on the content of \code{x} for use in printing and pagination. diff --git a/man/round_fmt.Rd b/man/round_fmt.Rd index cd33ea6c2..44257219f 100644 --- a/man/round_fmt.Rd +++ b/man/round_fmt.Rd @@ -1,11 +1,18 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/format_value.R -\name{round_fmt} +\docType{data} +\name{valid_round_type} +\alias{valid_round_type} \alias{round_fmt} \alias{rounding} \title{Round and prepare a value for display} +\format{ +An object of class \code{character} of length 3. +} \usage{ -round_fmt(x, digits, na_str = "NA", round_type = c("iec", "sas")) +valid_round_type + +round_fmt(x, digits, na_str = "NA", round_type = valid_round_type) } \arguments{ \item{x}{(\code{numeric(1)})\cr value to format.} @@ -15,9 +22,14 @@ character value with no rounding.} \item{na_str}{(\code{string})\cr the value to return if \code{x} is \code{NA}.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ A character value representing the value after rounding, containing any trailing zeros @@ -59,8 +71,12 @@ round_fmt(NA, digits = 1, na_str = "-") round_fmt(2.765923, digits = NA) round_fmt(0.845, digits = 2) round_fmt(0.845, digits = 2, round_type = "sas") +round_fmt(-0.001, digits = 2, round_type = "iec") +round_fmt(-0.001, digits = 2, round_type = "sas") +round_fmt(-0.001, digits = 2, round_type = "iec_mod") } \seealso{ \code{\link[=format_value]{format_value()}}, \code{\link[=round]{round()}}, \code{\link[=sprintf]{sprintf()}} } +\keyword{datasets} diff --git a/man/test_matrix_form.Rd b/man/test_matrix_form.Rd index 7431af406..640221a80 100644 --- a/man/test_matrix_form.Rd +++ b/man/test_matrix_form.Rd @@ -15,14 +15,16 @@ basic_matrix_form( fontspec = font_spec(), split_labels = NULL, data_labels = NULL, - num_rep_cols = 0L + num_rep_cols = 0L, + round_type = valid_round_type ) basic_listing_mf( df, keycols = names(df)[1], add_decoration = TRUE, - fontspec = font_spec() + fontspec = font_spec(), + round_type = valid_round_type ) } \arguments{ @@ -58,6 +60,10 @@ for more information.} Defaults to \code{0} for \code{basic_matrix_form} and \code{length(keycols)} for \code{basic_listing_mf}. Note repeating columns are separate from row labels if present.} +\item{round_type}{(\code{string})\cr +The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +See \code{\link[=round_fmt]{round_fmt()}} for details.} + \item{keycols}{(\code{character})\cr a vector of \code{df} column names that are printed first and for which repeated values are assigned \code{""}. This format is characteristic of a listing matrix form.} } diff --git a/man/tostring.Rd b/man/tostring.Rd index 0a2237141..930b5ff5e 100644 --- a/man/tostring.Rd +++ b/man/tostring.Rd @@ -16,7 +16,7 @@ toString(x, ...) hsep = NULL, fontspec = font_spec(), ttype_ok = FALSE, - round_type = c("iec", "sas") + round_type = obj_round_type(x) ) } \arguments{ @@ -48,9 +48,9 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo allowed via \code{fontspec}. Defaults to \code{FALSE}. This parameter is primarily for internal testing and generally should not be set by end users.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr +The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +See \code{\link[=round_fmt]{round_fmt()}} for details.} } \value{ A character string containing the ASCII rendering of the table-like object represented by \code{x}. diff --git a/man/vert_pag_indices.Rd b/man/vert_pag_indices.Rd index 276bec98d..2de97c22c 100644 --- a/man/vert_pag_indices.Rd +++ b/man/vert_pag_indices.Rd @@ -12,7 +12,7 @@ vert_pag_indices( rep_cols = 0L, fontspec, nosplitin = character(), - round_type = c("iec", "sas") + round_type = obj_round_type(mf) ) } \arguments{ @@ -34,9 +34,14 @@ calculating string widths and heights, as returned by \code{\link[=font_spec]{fo \item{nosplitin}{(\code{character})\cr list of names of subtables where page breaks are not allowed, regardless of other considerations. Defaults to none.} -\item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, -the default, peforms rounding compliant with IEC 60559 (see details), while -sas performs nearest-value rounding consistent with rounding within SAS.} +\item{round_type}{(\code{string})\cr . +\cr The type of rounding to perform. Allowed values: (\code{"iec"}, \code{"iec_mod"} or \code{"sas"}) +\cr iec, the default, and iec_mod performs rounding compliant with IEC 60559 +(see notes in \code{\link[=round_fmt]{round_fmt()}}), while +sas performs nearest-value rounding consistent with rounding within SAS.\cr +In addition, the rounding of a negative number that rounds to zero will be presented as 0 +(with the appropriate number of trailing zeros) for both \code{sas} and \code{iec_mod}, +while for \code{iec}, it will be presented as -0 (with the appropriate number of trailing zeros).} } \value{ A \code{list} partitioning the vector of column indices into subsets for 1 or more horizontally paginated pages. diff --git a/tests/testthat/test-formatters.R b/tests/testthat/test-formatters.R index 063f562f3..8938098aa 100644 --- a/tests/testthat/test-formatters.R +++ b/tests/testthat/test-formatters.R @@ -1045,6 +1045,22 @@ test_that("sas-style rounding works", { "7.85" ) + expect_identical( + format_value(-0.001, "xx.xx", round_type = "iec"), + "-0.00" + ) + + expect_identical( + format_value(-0.001, "xx.xx", round_type = "sas"), + "0.00" + ) + + expect_identical( + format_value(-0.001, "xx.xx", round_type = "iec_mod"), + "0.00" + ) + + forms <- list_valid_format_labels() for (fmtset in 1:3) { for (digs in 0:3) { @@ -1100,3 +1116,43 @@ test_that("na_str works when having multiple NAs and multiple values to be chang "a (1.0 - b)" ) }) + +test_that("Methods for obj_round_type", { + inputdf <- mtcars + obj_format(inputdf$wt) <- "xx.xx" + dfmf <- basic_matrix_form(inputdf) + dfmf2 <- basic_matrix_form(inputdf, round_type = "sas") + + expect_identical(obj_round_type(dfmf), "iec") + expect_identical(obj_round_type(dfmf2), "sas") + + # check actual rounding + df_iec <- dfmf$strings[1 + seq_len(nrow(inputdf)), "wt"] + df_sas <- dfmf2$strings[1 + seq_len(nrow(inputdf)), "wt"] + + fmt_iec <- sapply(inputdf[, "wt"], function(x) format_value(x, format = "xx.xx", round_type = "iec")) + fmt_sas <- sapply(inputdf[, "wt"], function(x) format_value(x, format = "xx.xx", round_type = "sas")) + expect_identical(df_iec, fmt_iec) + expect_identical(df_sas, fmt_sas) +}) + +test_that("Methods for obj_round_type on list object", { + inputdf <- mtcars + obj_format(inputdf$wt) <- "xx.xx" + + bmf <- basic_matrix_form(inputdf, round_type = "sas") + blmf <- basic_listing_mf(inputdf, keycols = c("vs", "gear"), round_type = "sas") + l_mf <- list(bmf, blmf) + + expect_identical(obj_round_type(l_mf), "sas") + + # test setter + # current object is list of MatrixPrintForm class, round_type cannot not be updated here + # as rounding occurs during creation of MatrixPrintForm object + l_mf_iec <- l_mf + expect_error( + obj_round_type(l_mf_iec) <- "iec", + "Cannot alter round type on a `MatrixPrintForm` object" + ) + # test of setter for list of listing_obj will be included in rlistings instead +})