# Create formated numbers class
#Import vctrs in NAMESPACE :
#' Internal vctrs methods
#'
#' @import vctrs
#' @keywords internal
#' @name tabxplor-vctrs
NULL
# binding for global variables not found by R CMD check
. = NULL
globalVariables(c(":=", ".SD", ".N"))
# EXPORTED FUNCTIONS TO WORK WITH CLASS FMT ##############################################
#' Create a vector of class formatted numbers
#' @description \code{fmt} vectors, of class \code{tabxplor_fmt}, powers \pkg{tabxplor}
#' and \code{\link{tab}} tibbles.
#' As a \code{\link[vctrs:new_rcrd]{record}}, they stores all data necessary to
#' calculate percentages, Chi2 metadata or confidence intervals, but also to format and
#' color the table to help the user read it. You can access this data with
#' \code{\link[vctrs:field]{vctrs::field}}, or change it with
#' \code{\link[vctrs:field]{vctrs:field<-}}. A \code{fmt} vector have 13 fields :
#' \code{n}, \code{digits}, \code{display}, \code{wn}, \code{pct}, \code{mean},
#' \code{diff}, \code{ctr}, \code{var}, \code{ci}, \code{in_totrow}, \code{in_tottab},
#' \code{in_refrow}. Other arguments are attributes, attached not to each value, but to
#' the whole vector, like \code{type}, \code{totcol} or \code{color}. You can get them
#' with \code{\link[base:attr]{attr}} and modify them with
#' \code{\link[base:attr]{attr<-}}. Special functions listed below are made to
#' facilitate programming with with \pkg{tabxplor} formatted numbers.
#' \code{taxplfmt} vectors can use all standard operations, like +, -, sum(), or c(),
#' using \pkg{vctrs}.
#'
#' @param n The underlying count, as an integer vector of length \code{n()}. It is used
#' to calculate confidence intervals.
#' @param type The type of the column, which defines the type of background calculation
#' to be made (as a single string, since it's not a field but an attribute) :
#' \itemize{
#' \item \code{"n"}: counts
#' \item \code{"mean"}: mean column (from numeric variables)
#' \item \code{"row"}: row percentages
#' \item \code{"col"}: column percentages
#' \item \code{"all"}: frequencies by subtable/group (i.e. by \code{tab_vars})
#' \item \code{"all_tabs"}: frequencies for the whole table
#' }
#' @param digits The number of digits, as an integer, or an integer vector the length
#' of \code{n}.
#' @param display The display type : the name of the field you want to show when printing
#' the vector. Among \code{"n"}, \code{"wn"}, \code{"pct"}, \code{"diff"}, \code{"ctr"},
#' \code{"mean"}, \code{"var"}, \code{"ci"},
#' \code{"pct_ci"} (percentages with visible confidence interval),
#' \code{"mean_ci"} (means with visible confidence interval). As a single string, or a
#' character vector the length of \code{n}.
#' @param wn The underlying weighted counts, as a double vector the length of
#' \code{n}. It is used in certain operations on \code{\link{fmt}}, like means.
#' @param pct The percentages, as a double vector the length of \code{n}.
#' Calculate with \code{\link{tab_pct}}.
#' @param mean The means, as a double vector the length of \code{n}.
#' @param diff The differences (from totals or first cells),
#' as a double vector the length of \code{n}. Used to set colors for means and
#' row or col percentages. Calculate with \code{\link{tab_pct}}.
#' @param ctr The contributions of cells to (sub)tables variances,
#' as a double vector the length of \code{n}. Used to print colors when
#' \code{color = "contrib"}. The mean contribution of each (sub)table is written on
#' total rows (then, colors don't print well without total rows).
#' Calculate with \code{\link{tab_chi2}}.
#' @param var The cells variances, as a double vector the length of \code{n}.
#' Used with \code{type = "mean"} to calculate confidence intervals.
#' Calculate with \code{tab_plain}.
#' @param ci The confidence intervals, as a double vector the length of \code{n}.
#' Used to print colors (\code{"diff_ci"}, \code{"after_ci"}).
#' Calculate with \code{tab_ci}.
#' @param rr The relative risk, as a double vector the length of \code{n}.
#' @param or The odds ratio or relative risk ratio, as a double vector the length of \code{n}.
#' @param in_totrow \code{TRUE} when the cell is part of a total row
#' @param in_tottab \code{TRUE} when the cell is part of a total table
#' @param in_refrow \code{TRUE} when the cell is part of a reference row
#' (cf. \code{ref})
#' @param comp_all \code{FALSE} when the comparison level is the subtable/group,
#' \code{TRUE} when it is the whole table
#' @param ref The type of difference of the vector. Cf. \code{\link{tab}}.
#' @param ci_type The type of confidence intervals of the vector (calculate
#' with \code{\link{tab_ci}}) :
#' \itemize{
#' \item \code{""} or \code{"no"}: no ci have been calculated
#' \item \code{"cell"}: absolute confidence intervals of cells percentages.
#' \item \code{"diff"}: confidence intervals of the difference between a cell and the
#' relative total cell (or relative first cell when \code{ref = "first"}).
#' \item \code{"auto"}: \code{"diff"} for means and row/col percentages,
#' \code{"cell"} for frequencies ("all", "all_tabs").
#' }
#' @param col_var The name of the \code{col_var} used to calculate the vector
#' @param totcol \code{TRUE} when the vector is a total column
#' @param refcol \code{TRUE} when the vector is a reference column
# @param fmt A fmt vector to test or to modify fields.
#' @param x The object to test, to get a field in, or to modify.
#' @param ... Used in methods to add arguments in the future.
#' @param color The type of color to print :
#' \itemize{
#' \item \code{"no"}: no colors are printed.
#' \item \code{"diff"}: color percentages and means based on cells differences from
#' totals (or from first cells when \code{ref = "first"}).
#' \item \code{"diff_ci"}: color pct and means based on cells differences from totals
#' or first cells, removing coloring when the confidence interval of this difference
#' is higher than the difference itself.
#' \item \code{"after_ci"}: idem, but cut off the confidence interval from the
#' difference first.
#' \item \code{"contrib"}: color cells based on their contribution to variance
#' (except mean columns, from numeric variables).
#' }
#' @return A vector of class \code{tabxplor_fmt}.
#' @export
#'
#' @examples
#' library(dplyr)
#'
#' f <- fmt(n = c(7, 19, 2), type = "row", pct = c(0.25, 0.679, 0.07))
#' f
#'
#' # To get the currently displayed field :
#' get_num(f)
#'
#' # To modify the currently displayed field :
#' set_num(f, c(1, 0, 0))
#'
#'
#' # See all the underlying fields of a fmt vector (a data frame with a number of rows
#' # equal to the length of the vector) :
#' vctrs::vec_data(f)
#'
#' # To get the numbers of digits :
#' vctrs::field(f, "digits")
#' f$digits
#'
#' # To get the count :
#' vctrs::field(f, "n")
#' f$n
#'
#' # To get the display :
#' vctrs::field(f, "display")
#' f$display
#'
#' # To modify a field, you can use `dplyr::mutate` on the fmt vector,
#' # referring to the names of the columns of the underlying data.frame (`vctrs::vec_data`) :
#' vctrs::`field<-`(f, "pct", c(1, 0, 0))
#' mutate(f, pct = c(1, 0, 0))
#'
#' # See all the attributes of a fmt vector :
#' attributes(f)
#'
#' # To modify the "type" attribute of a fmt vector :
#' set_type(f, "col")
#'
#' # To modify the "color" attribute of a fmt vector :
#' set_color(f, "contrib")
#'
#'
#'
#'
#' tabs <- tab(starwars, sex, hair_color, gender, na = "drop", pct = "row",
#' other_if_less_than = 5)
#'
#' # To identify the total columns, and work with them :
#' is_totcol(tabs)
#' tabs %>% mutate(across(where(is_totcol), ~ "total column"))
#'
#' # To identify the total rows, and work with them :
#' is_totrow(tabs)
#' tabs %>%
#' mutate(across(
#' where(is_fmt),
#' ~ if_else(is_totrow(.), true = "into_total_row", false = "normal_cell")
#' ))
#'
#' # To identify the total tables, and work with them :
#' tottabs <- is_tottab(tabs)
#' tabs %>% tibble::add_column(tottabs) %>%
#' mutate(total = if_else(tottabs, "part of a total table", "normal cell"))
#'
#' # To access the displayed numbers, as numeric vectors :
#' tabs %>% mutate(across(where(is_fmt), get_num))
#'
#' # To access the displayed numbers, as character vectors (without colors) :
#' tabs %>% mutate(across(where(is_fmt), format))
#'
#' # To access the (non-displayed) differences of the cells percentages from totals :
#' tabs %>% mutate(across(where(is_fmt), ~ vctrs::field(., "diff")))
#'
#'
#' # To do more complex operations, like creating a new column with standard deviation and
#' # print it with 2 decimals, use `dplyr::mutate` on all the fmt columns of a table :
#'
#' tab_num(forcats::gss_cat, race, c(age, tvhours), marital, digits = 1L, comp = "all") |>
#' dplyr::mutate(dplyr::across( #Mutate over the whole table.
#' c(age, tvhours),
#' ~ dplyr::mutate(., #Mutate over each fmt vector's underlying data.frame.
#' var = sqrt(var),
#' display = "var",
#' digits = 2L) |>
#' set_color("no"),
#' .names = "{.col}_sd"
#' ))
fmt <- function(n = integer(),
type = "n",
digits = rep(0L , length(n)),
display = dplyr::case_when(
type == "mean" ~ "mean",
type %in% c("row", "col", "all", "all_tabs") ~ "pct" ,
TRUE ~ "n" ),
wn = rep(NA_real_, length(n)),
pct = rep(NA_real_, length(n)),
mean = rep(NA_real_, length(n)),
diff = rep(NA_real_, length(n)),
ctr = rep(NA_real_, length(n)),
var = rep(NA_real_, length(n)),
ci = rep(NA_real_, length(n)),
rr = rep(NA_real_, length(n)),
or = rep(NA_real_, length(n)),
in_totrow = rep(FALSE, length(n)),
in_tottab = rep(FALSE, length(n)),
in_refrow = rep(FALSE, length(n)),
comp_all = NA ,
ref = "" ,
ci_type = "" ,
col_var = "" ,
totcol = FALSE,
refcol = FALSE,
color = "" ) {
max_size <- list(n, wn, pct, digits, ctr, mean, var, ci) %>% #display
purrr::map_int(length) %>% max()
display <- vctrs::vec_recycle(vctrs::vec_cast(display, character()), size = max_size)
n <- vctrs::vec_recycle(vctrs::vec_cast(n , integer()) , size = max_size)
wn <- vctrs::vec_recycle(vctrs::vec_cast(wn , double()) , size = max_size) #anything coercible as a double
pct <- vctrs::vec_recycle(vctrs::vec_cast(pct , double()) , size = max_size)
diff <- vctrs::vec_recycle(vctrs::vec_cast(diff , double()) , size = max_size)
digits <- vctrs::vec_recycle(vctrs::vec_cast(digits , integer()) , size = max_size)
ctr <- vctrs::vec_recycle(vctrs::vec_cast(ctr , double()) , size = max_size)
mean <- vctrs::vec_recycle(vctrs::vec_cast(mean , double()) , size = max_size)
var <- vctrs::vec_recycle(vctrs::vec_cast(var , double()) , size = max_size)
ci <- vctrs::vec_recycle(vctrs::vec_cast(ci , double()) , size = max_size)
rr <- vctrs::vec_recycle(vctrs::vec_cast(rr , double()) , size = max_size)
or <- vctrs::vec_recycle(vctrs::vec_cast(or , double()) , size = max_size)
in_totrow <- vctrs::vec_recycle(vctrs::vec_cast(in_totrow, logical()), size = max_size)
in_tottab <- vctrs::vec_recycle(vctrs::vec_cast(in_tottab, logical()), size = max_size)
in_refrow <- vctrs::vec_recycle(vctrs::vec_cast(in_refrow, logical()), size = max_size)
type <- vctrs::vec_recycle(vctrs::vec_cast(type , character()), size = 1)
comp_all <- vctrs::vec_recycle(vctrs::vec_cast(comp_all , logical() ), size = 1)
ref <- vctrs::vec_recycle(vctrs::vec_cast(ref, character()), size = 1)
ci_type <- vctrs::vec_recycle(vctrs::vec_cast(ci_type , character()), size = 1)
col_var <- vctrs::vec_recycle(vctrs::vec_cast(col_var , character()), size = 1)
totcol <- vctrs::vec_recycle(vctrs::vec_cast(totcol , logical() ), size = 1)
refcol <- vctrs::vec_recycle(vctrs::vec_cast(totcol , logical() ), size = 1)
color <- vctrs::vec_recycle(vctrs::vec_cast(color , character()), size = 1)
new_fmt(n = n, display = display, digits = digits,
wn = wn, pct = pct, mean = mean,
diff = diff, ctr = ctr,var = var, ci = ci, rr = rr, or = or,
in_totrow = in_totrow, in_tottab = in_tottab, in_refrow = in_refrow,
type = type, comp_all = comp_all, ref = ref,
ci_type = ci_type, col_var = col_var, totcol = totcol, refcol = refcol,
color = color)
}
#' @describeIn fmt a test function for class fmt.
#' @return A logical vector.
#' @export
is_fmt <- function(x) {
inherits(x, "tabxplor_fmt")
}
# #' A function to convert vectors to class fmt.
# #' @param x A vector coercible to double, or a character vector with numbers.
# #' @param ... The number of digits as an integer, to be passed to the method.
# #'
# #' @export
# as_fmt <- function(x, ...) {
# UseMethod("as_fmt")
# }
# # @describeIn as_fmt
# #' @export
# as_fmt.default <- function(x, digits = rep(0L, length(x)), #display = rep("count", length(x)),
# # n = rep(NA_integer_, length(x)), wn = rep(NA_real_, length(x)),
# # var = rep(NA_real_, length(x)), ci = rep(NA_real_, length(x)),
# ...) {
# new_fmt(vec_data(x))
# }
#' @describeIn fmt get the currently displayed field
#' @return A double vector.
#' @export
get_num <- function(x) {
out <- get_n(x)
display <- get_display(x)
nas <- is.na(display)
out[!nas & display == "wn" ] <- get_wn (x)[!nas & display == "wn" ]
out[!nas & display == "pct" ] <- get_pct (x)[!nas & display == "pct" ]
out[!nas & display == "pvalue" ] <- get_pct (x)[!nas & display == "pvalue" ]
out[!nas & display == "diff" ] <- get_diff(x)[!nas & display == "diff" ]
out[!nas & display == "pct_ci" ] <- get_pct (x)[!nas & display == "pct_ci" ]
out[!nas & display == "ctr" ] <- get_ctr (x)[!nas & display == "ctr" ]
out[!nas & display == "mean" ] <- get_mean(x)[!nas & display == "mean" ]
out[!nas & display == "mean_ci"] <- get_mean(x)[!nas & display == "mean_ci"]
out[!nas & display == "var" ] <- get_var (x)[!nas & display == "var" ]
out[!nas & display == "ci" ] <- get_ci (x)[!nas & display == "ci" ]
out[!nas & display == "rr" ] <- get_rr (x)[!nas & display == "rr" ]
out[!nas & display %in% c("or", "OR")] <- get_or(x)[!nas & display %in% c("or", "OR") ]
out[!nas & display == "or_pct" ] <- get_or (x)[!nas & display == "or_pct" ]
out
}
#' @describeIn fmt set the currently displayed field (not changing display type)
#' @param value The value you want to inject in some \code{fmt} vector's vctrs::field
#' or attribute using a given "set" function.
#' @return A modified fmt vector.
#' @export
set_num <- function(x, value) {
value <- vctrs::vec_recycle(value, length(x))
out <- x
display <- get_display(x)
nas <- is.na(display)
out[!nas & display == "n" ] <- set_n (x[!nas & display == "n" ], value[!nas & display == "n" ])
out[!nas & display == "wn" ] <- set_wn (x[!nas & display == "wn" ], value[!nas & display == "wn" ])
out[!nas & display == "pct" ] <- set_pct (x[!nas & display == "pct" ], value[!nas & display == "pct" ])
out[!nas & display == "diff"] <- set_pct (x[!nas & display == "diff"], value[!nas & display == "diff"])
out[!nas & display == "ctr" ] <- set_ctr (x[!nas & display == "ctr" ], value[!nas & display == "ctr" ])
out[!nas & display == "mean"] <- set_mean(x[!nas & display == "mean"], value[!nas & display == "mean"])
out[!nas & display == "var" ] <- set_var (x[!nas & display == "var" ], value[!nas & display == "var" ])
out[!nas & display == "ci" ] <- set_ci (x[!nas & display == "ci" ], value[!nas & display == "ci" ])
out[!nas & display == "rr" ] <- set_rr (x[!nas & display == "rr" ], value[!nas & display == "rr" ])
out[!nas & display %in% c("or", "OR")] <- set_or(x[!nas & display %in% c("or", "OR") ], value[!nas & display == "or" ])
out
}
#' @describeIn fmt get types of fmt columns (at \code{fmt} level or \code{tab} level)
#' @param x The object to test, to get a field in, or to modify.
#' @param ... Used in methods to add arguments in the future.
#' @return A character vector with the vectors type.
#' @export
get_type <- function(x, ...) UseMethod("get_type")
#' Get types of fmt columns
#' @inheritParams fmt
#' @return An empty character vector.
#' @export
get_type.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("type")(x)),
yes = purrr::attr_getter("type")(x),
no = "") #NA_character_
}
#' Get types of fmt columns
#' @method get_type tabxplor_fmt
#' @inheritParams fmt
#' @return A single string with the vector's type.
#' @export
get_type.tabxplor_fmt <- function(x, ...) attr(x, "type", exact = TRUE)
#' Get types of fmt columns
#' @inheritParams fmt
#' @return A character vector with the data.frame column's types.
#' @export
get_type.data.frame <- function(x, ...) purrr::map_chr(x, ~ get_type(.))
#' @describeIn fmt set the column type attribute of a \code{fmt} vector
#' @return A modified fmt vector.
#' @export
set_type <- function(x, type) {
if (type %in% c("no", "", NA_character_)) type <- "n"
stopifnot(type %in% c("row", "col", "all", "all_tabs", "mean", "n"))
`attr<-`(x ,"type" , type)
}
#' @describeIn fmt test function to detect cells in total rows
#' (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors totrow field.
#' @export
is_totrow <- function(x, ...) UseMethod("is_totrow")
#' Test function to detect cells in total rows
#' @inheritParams fmt
#' @return A logical vector with \code{FALSE}.
#' @export
is_totrow.default <- function(x, ...) rep(FALSE, length(x)) #{
#' Test function to detect cells in total rows
#' @method is_totrow tabxplor_fmt
#' @inheritParams fmt
#' @return A logical vector with the totrow field.
#' @export
is_totrow.tabxplor_fmt <- function(x, ...) vctrs::field(x, "in_totrow")
#' Test function to detect cells in total rows
#' @inheritParams fmt
#' @param partial Should partial total rows be counted as total rows ? Default to FALSE.
#' @return A list of logical vectors, with the data.frame column's totrow fields.
#' @export
is_totrow.data.frame <- function(x, ..., partial = FALSE) {
totrow_cells_test <- dplyr::ungroup(x) %>% dplyr::select(where(is_fmt)) %>%
purrr::map_df(~ is_totrow(.))
if (partial == TRUE) {
totrow_cells_test |>
dplyr::transmute(var = dplyr::if_any(.cols = dplyr::everything())) |>
tibble::deframe()
} else {
test_result <- totrow_cells_test %>%
dplyr::transmute(complete = dplyr::if_all(.cols = dplyr::everything() ),
partial = dplyr::if_all(-"complete") & !.data$complete)
if (tidyr::replace_na(any(test_result$partial), FALSE)) {
warning("partial total rows (with some fmt cells not tagged 'totrow') ",
"were not taken into account ")
}
test_result$complete
}
}
#' @describeIn fmt set the "in_totrow" field (belong to total row)
#' @return A modified fmt vector with totrow field changed.
#' @export
as_totrow <- function(x, in_totrow = TRUE) {
vctrs::vec_assert(in_totrow, logical())
vctrs::`field<-`(x, "in_totrow", vctrs::vec_recycle(in_totrow, length(x)))
}
#' Complete partial total rows
#'
#' @param tabs A table or data.framate containting `tabxplor_fmt` columns.
#'
#' @return The table with completed total rows, total tables, and reference rows.
#' @export
#'
# @examples
complete_partial_totals <- function(tabs) {
.diff_totrows <- suppressWarnings(is_totrow(tabs)) != is_totrow(tabs, partial = TRUE)
if (any(.diff_totrows)) {
tabs <- tabs |>
tibble::add_column(.diff_totrows) |>
dplyr::mutate(dplyr::across(where(is_fmt), ~ dplyr::if_else(
condition = .diff_totrows,
true = as_totrow(.),
false = .
))) |>
select(-.diff_totrows)
}
.diff_tottabs <- suppressWarnings(is_tottab(tabs)) != is_tottab(tabs, partial = TRUE)
if (any(.diff_tottabs)) {
tabs <- tabs |>
tibble::add_column(.diff_tottabs) |>
dplyr::mutate(dplyr::across(where(is_fmt), ~ dplyr::if_else(
condition = .diff_tottabs,
true = as_tottab(.),
false = .
))) |>
select(-.diff_tottabs)
}
.diff_refrows <- suppressWarnings(is_refrow(tabs)) != is_refrow(tabs, partial = TRUE)
if (any(.diff_refrows)) {
tabs <- tabs |>
tibble::add_column(.diff_refrows) |>
dplyr::mutate(dplyr::across(where(is_fmt), ~ dplyr::if_else(
condition = .diff_refrows,
true = as_refrow(.),
false = .
))) |>
select(-.diff_refrows)
}
tabs
}
#' @describeIn fmt test function to detect cells in total tables
#' (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors tottab field.
#' @export
is_tottab <- function(x, ...) UseMethod("is_tottab")
#' Test function to detect cells in total tables
#' @method is_tottab default
#' @inheritParams fmt
#' @return A logical vector with \code{FALSE}.
#' @export
is_tottab.default <- function(x, ...) rep(FALSE, length(x)) #{
#' Test function to detect cells in total tables
#' @method is_tottab tabxplor_fmt
#' @inheritParams fmt
#' @return A logical vector with the tottab field.
#' @export
is_tottab.tabxplor_fmt <- function(x, ...) vctrs::field(x, "in_tottab")
#' Test function to detect cells in total tables
#' @param partial Should partial total tabs be counted as total tabs ? Default to FALSE.
#' @inheritParams fmt
#' @return A list of logical vectors, with the data.frame column's tottab fields.
#' @export
is_tottab.data.frame <- function(x, ..., partial = FALSE) {
tottab_cells_test <- dplyr::ungroup(x) %>% dplyr::select(where(is_fmt)) %>%
purrr::map_df(~ is_tottab(.))
if (partial == TRUE) {
tottab_cells_test %>%
dplyr::transmute(var = dplyr::if_any(.cols = dplyr::everything())) %>%
tibble::deframe()
} else {
test_result <- tottab_cells_test %>%
dplyr::transmute(complete = dplyr::if_all(.cols = dplyr::everything() ),
partial = dplyr::if_all(-"complete") & !.data$complete)
if (tidyr::replace_na(any(test_result$partial), FALSE)) {
warning("partial total rows (with some fmt cells not tagged 'totrow') ",
"were not taken into account ")
}
test_result$complete
}
}
#' @describeIn fmt set the "in_tottab" field (belong to total table)
#' @return A modified fmt vector with tottab field changed.
#' @export
as_tottab <- function(x, in_tottab = TRUE) {
vctrs::vec_assert(in_tottab, logical())
vctrs::`field<-`(x, "in_tottab", vctrs::vec_recycle(in_tottab, length(x)))
}
#' @describeIn fmt set the "display" vctrs::field of a \code{fmt} vector, or of
#' all of them in the whole tibble.
#' @return The entered objects, with all fmt vectors with the wanted display.
#' @export
set_display <- function(x, value) UseMethod("set_display")
#' Set the "display" vctrs::field of a \code{fmt} vector.
#' @inheritParams fmt
#' @return The entered vector (nothing happens).
#' @export
set_display.default <- function(x, value) {
return(x)
}
#' Set the "display" vctrs::field of a \code{fmt} vector.
#' @inheritParams fmt
#' @return A fmt vectors with the wanted display.
#' @export
set_display.tabxplor_fmt <- function(x, value) {
value <- vctrs::vec_cast(value, character()) %>% vctrs::vec_recycle(size = length(x))
vctrs::`field<-`(x, "display", value)
}
#' Set the "display" vctrs::field of a \code{fmt} vector.
#' @inheritParams fmt
#' @return The entered objects, with all fmt vectors with the wanted display.
#' @export
set_display.data.frame <- function(x, value) {
x |>
dplyr::mutate(dplyr::across(
dplyr::where(is_fmt) & -(tidyselect::any_of(c("n", "wn")) &
dplyr::where(~ get_type(.) == "n")),
~ set_display(., value)
))
}
#' @describeIn fmt test function for total columns
#' (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors totcol attribute.
#' @export
is_totcol <- function(x, ...) UseMethod("is_totcol")
#' Test function for total columns
#' @inheritParams fmt
#' @return A single logical vector with the totcol attribute
#' @export
is_totcol.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("totcol")(x)),
yes = purrr::attr_getter("totcol")(x),
no = FALSE)
}
#' Test function for total columns
#' @inheritParams fmt
#' @return A single logical vector with the totcol attribute
#' @export
is_totcol.tabxplor_fmt <- function(x, ...) attr(x, "totcol", exact = TRUE)
#' Test function for total columns
#' @inheritParams fmt
#' @return A logical vector, with the data.frame column's totcol attributes.
#' @export
is_totcol.data.frame <- function(x, ...) purrr::map_lgl(x, ~ is_totcol(.))
#' @describeIn fmt set the "totcol" attribute of a \code{fmt} vector
#' @return A modified fmt vector with totcol attribute changed.
#' @export
as_totcol <- function(x, totcol = TRUE) {
vctrs::vec_assert(totcol, logical(), size = 1)
`attr<-`(x ,"totcol" , totcol)
}
#' @describeIn fmt test function to detect cells in reference rows
#' (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors in_refrow field
#' @export
is_refrow <- function(x, ...) UseMethod("is_refrow")
#' Test function to detect cells in reference rows
#' @method is_refrow default
#' @inheritParams fmt
#' @return A logical vector with FALSE, the length of x.
#' @export
is_refrow.default <- function(x, ...) rep(FALSE, length(x)) #{
#' Test function to detect cells in reference rows
#' @method is_refrow tabxplor_fmt
#' @inheritParams fmt
#' @return A logical vector with the in_refrow field.
#' @export
is_refrow.tabxplor_fmt <- function(x, ...) vctrs::field(x, "in_refrow")
#' Test function to detect cells in reference rows
#' @method is_refrow data.frame
#' @param partial Should partial reference rows be counted as reference rows ? Default to FALSE.
#' @inheritParams fmt
#' @return A list of logical vectors with the in_refrow fields.
#' @export
is_refrow.data.frame <- function(x, ..., partial = TRUE) {
refrow_cells_test <- dplyr::ungroup(x) %>% dplyr::select(where(is_fmt)) %>%
purrr::map_df(~ is_refrow(.))
if (partial == TRUE) {
refrow_cells_test %>%
dplyr::transmute(var = dplyr::if_any(.cols = dplyr::everything() )) %>%
tibble::deframe()
} else {
test_result <- refrow_cells_test %>%
dplyr::transmute(complete = dplyr::if_all(.cols = dplyr::everything() ),
partial = dplyr::if_all(-"complete") & !.data$complete)
if (tidyr::replace_na(any(test_result$partial), FALSE)) {
warning("partial total rows (with some fmt cells not tagged 'refrow') ",
"were not taken into account ")
}
test_result$complete
}
}
#' @describeIn fmt set the "in_refrow" field (belong to reference row)
#' @return A modified fmt vector with in_refrom field changed.
#' @export
as_refrow <- function(x, in_refrow = TRUE) {
vctrs::vec_assert(in_refrow, logical())
vctrs::`field<-`(x, "in_refrow", vctrs::vec_recycle(in_refrow, length(x)))
}
#' @describeIn fmt get comparison level of fmt columns
#' @inheritParams fmt
#' @param replace_na By default, \code{\link{get_comp_all}} takes NA in comparison level
#' to be a \code{FALSE} (=comparison at subtables/groups level). Set to \code{FALSE}
#' to avoid this behavior.
# @keywords internal
#' @export
get_comp_all <- function(x, replace_na = TRUE) {
comp <- attr(x, "comp_all", exact = TRUE)
if (is.null(comp)) return(NA)
if (replace_na & is.na(comp)) comp <- FALSE
comp
}
#' @describeIn fmt set the comparison level attribute of a \code{fmt} vector
# @param fmt The fmt object to modify.
# @param value One of "tab" (comparison inside subtables) or "all" (comparison with
# total table).
#' @return A modified fmt vector with comp attribute changed.
#' @export
set_comp_all <- function(x, comp_all = FALSE) { #comp_all = c("tab", "all")
`attr<-`(x, "comp_all", comp_all) # comp_all == "all"
}
#' @describeIn fmt get differences type of fmt columns (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors type attributes
#' @export
get_ref_type <- function(x, ...) UseMethod("get_ref_type")
#' Get differences type of fmt columns
#' @method get_ref_type default
#' @inheritParams fmt
#' @return A single character with the ref attribute.
#' @export
get_ref_type.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("ref")(x)),
yes = purrr::attr_getter("ref")(x),
no = "") #NA_character_
}
#' Get differences type of fmt columns
#' @method get_ref_type tabxplor_fmt
#' @inheritParams fmt
#' @return A single character with the ref attribute.
#' @export
get_ref_type.tabxplor_fmt <- function(x, ...) attr(x, "ref", exact = TRUE)
#' Get differences type of fmt columns
#' @method get_ref_type data.frame
#' @inheritParams fmt
#' @return A character vector with the ref attribute.
#' @export
get_ref_type.data.frame <- function(x, ...) {
purrr::map_chr(x, ~ get_ref_type(.))
}
#' @describeIn fmt set the differences type attribute of a \code{fmt} vector
#' @return A modified fmt vector.
#' @export
set_diff_type <- function(x, ref) {
#stopifnot(ref %in% c("tot", "first", "no", "", NA_character_))
`attr<-`(x ,"ref" , ref)
}
#' @describeIn fmt get confidence intervals type of fmt columns (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors ci_type attributes
#' @export
get_ci_type <- function(x, ...) UseMethod("get_ci_type")
#' Get confidence intervals type of fmt columns
#' @method get_ci_type default
#' @inheritParams fmt
#' @return A single character with the ci_type attribute.
#' @export
get_ci_type.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("ci_type")(x)),
yes = purrr::attr_getter("ci_type")(x),
no = "") #NA_character_
}
#' Get confidence intervals type of fmt columns
#' @method get_ci_type tabxplor_fmt
#' @inheritParams fmt
#' @return A single character with the ci_type attribute.
#' @export
get_ci_type.tabxplor_fmt <- function(x, ...) attr(x, "ci_type", exact = TRUE)
#' Get confidence intervals type of fmt columns
#' @method get_ci_type data.frame
#' @inheritParams fmt
#' @return A character vector with the ci_type attributes.
#' @export
get_ci_type.data.frame <- function(x, ...) {
purrr::map_chr(x, ~ get_ci_type(.))
}
#' @describeIn fmt set the confidence intervals type attribute of a \code{fmt} vector
# @param ci_type The type of confidence interval calculated in "ci", as a single string.
#' @return A modified fmt vector.
#' @export
set_ci_type <- function(x, ci_type) {
stopifnot(ci_type %in% c("cell", "diff", "diff_row", "diff_col",
"no", "", NA_character_))
`attr<-`(x ,"ci_type" , ci_type)
}
#' @describeIn fmt get names of column variable of fmt columns (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors col_var attributes
#' @export
get_col_var <- function(x, ...) UseMethod("get_col_var")
#' Get names of column variable of fmt columns
#' @method get_col_var default
#' @inheritParams fmt
#' @return A single character with the col_var attribute.
#' @export
get_col_var.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("col_var")(x)),
yes = purrr::attr_getter("col_var")(x),
no = "") #NA_character_
}
#' Get names of column variable of fmt columns
#' @method get_col_var tabxplor_fmt
#' @inheritParams fmt
#' @return A single character with the col_var attribute.
#' @export
get_col_var.tabxplor_fmt <- function(x, ...) attr(x, "col_var", exact = TRUE)
#' Get names of column variable of fmt columns
#' @method get_col_var data.frame
#' @inheritParams fmt
#' @return A character vector with the col_var attributes.
#' @export
get_col_var.data.frame <- function(x, ...) purrr::map_chr(x, ~ get_col_var(.))
#' @describeIn fmt set the "col_var" attribute of a \code{fmt} vector
# @param col_var The name of the column variable, as a single string.
#' @return A modified fmt vector.
#' @export
set_col_var <- function(x, col_var) {
vctrs::vec_assert(col_var, character(), size = 1)
`attr<-`(x ,"col_var" , col_var)
}
#' @describeIn fmt test function for reference columns (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors is_refcol attributes
#' @export
is_refcol <- function(x, ...) UseMethod("is_refcol")
#' Test function for reference columns
#' @method is_refcol default
#' @inheritParams fmt
#' @return A single character with the ref_col attribute.
#' @export
is_refcol.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("refcol")(x)),
yes = purrr::attr_getter("refcol")(x),
no = FALSE)
}
#' Test function for reference columns
#' @method is_refcol tabxplor_fmt
#' @inheritParams fmt
#' @return A single character with the ref_col attribute.
#' @export
is_refcol.tabxplor_fmt <- function(x, ...) attr(x, "refcol", exact = TRUE)
#' Test function for reference columns
#' @method is_refcol data.frame
#' @inheritParams fmt
#' @return A character vector with the ref_col attributes.
#' @export
is_refcol.data.frame <- function(x, ...) purrr::map_lgl(x, ~ is_refcol(.))
#' @describeIn fmt set the "ref_col" attribute of a \code{fmt} vector
# @param refcol Is the vector a reference column ? As a logical vector of length one.
#' @return A modified fmt vector.
#' @export
as_refcol <- function(x, refcol = TRUE) {
vctrs::vec_assert(refcol, logical(), size = 1)
`attr<-`(x ,"refcol" , refcol)
}
#' @describeIn fmt get color (at \code{fmt} level or \code{tab} level)
#' @return A logical vector with the fmt vectors color attributes
#' @export
get_color <- function(x, ...) UseMethod("get_color")
#' Get color
#' @method get_color default
#' @inheritParams fmt
#' @return A single character with the color attribute.
#' @export
get_color.default <- function(x, ...) {
ifelse(! is.null(purrr::attr_getter("color")(x)),
yes = purrr::attr_getter("color")(x),
no = "") #NA_character_
}
#' Get color
#' @method get_color tabxplor_fmt
#' @inheritParams fmt
#' @return A single character with the color attribute.
#' @export
get_color.tabxplor_fmt <- function(x, ...) attr(x, "color", exact = TRUE)
#' Get color
#' @method get_color data.frame
#' @inheritParams fmt
#' @return A character vector with the color attributes.
#' @export
get_color.data.frame <- function(x, ...) {
purrr::map_chr(x, ~ get_color(.))
}
#' @describeIn fmt set the "color" attribute of a \code{fmt} vector
# @param color The type of color to print in tibbles, as a single string.
#' @return A modified fmt vector.
#' @export
# @keywords internal
#' @export
set_color <- function(x, color) {
if (color %in% c("no", NA_character_)) color <- "" #NA_character_
if (color == "or") color <- "OR"
stopifnot(color %in% c("diff", "diff_ci", "after_ci", "contrib", "ci", "OR",
"", NA_character_))
`attr<-`(x ,"color", color)
}
# fmt_get_color_code() doen't work in mutate with groups.
#' Get HTML Color Code of a fmt vector
#' @param x The fmt vector to get the html color codes from.
#'
#' @param type The style type in \code{set_color_style} and \code{get_color_style},
#' \code{"text"} to color the text, \code{"bg"} to color the background.
#' @param theme For \code{set_color_style} and \code{get_color_style}, is your console
#' or html table background \code{"light"} or \code{"dark"} ? Default to RStudio theme.
#' @param html_24_bit Use 24bits colors palettes for html tables : set to `"green_red"`
#' or `"blue_red"`. Only with `mode = "color_code"` (not `mode = "crayon"`) and
#' `theme = "light`. Default to \code{getOption("tabxplor.color_html_24_bit")}.
#' @return A character vector with html color codes, of the length of the initial vector.
#' @export
#'
#' @examples
#' \donttest{
#' tabs <- tab(forcats::gss_cat, race, marital, pct = "row", color = "diff")
#' dplyr::mutate(tabs, across(where(is_fmt), fmt_get_color_code))
#'}
fmt_get_color_code <- function(x, type = "text", theme = "light", html_24_bit = NULL) {
html_24_bit <- if (is.null(html_24_bit)) {getOption("tabxplor.color_html_24_bit")} else {html_24_bit}
color <- get_color(x)
if (color %in% c("no", "") | is.na(color)) return(rep(NA_character_, length(x)))
column_type <- get_type(x)
pct_diff <- color %in% c("diff", "diff_ci", "after_ci") & !column_type %in% c("n", "mean")
color_selection <- fmt_color_selection(x) %>% purrr::map(which)
color_styles <- select_in_color_style(names(color_selection), pct_diff = pct_diff)
color_styles <- get_color_style("color_code", type = type, theme = theme,
html_24_bit = html_24_bit)[color_styles]
color_positions <- color_selection %>%
purrr::map2(color_styles, ~ purrr::set_names(.x, stringr::str_to_upper(.y))) %>%
purrr::flatten_int()
no_color <- 1:length(x)
no_color <- purrr::set_names(no_color[!no_color %in% color_positions], NA_character_)
names(sort(c(color_positions, no_color)))
}
# INTERNAL FUNCTIONS #####################################################################
# @describeIn
#' fmt a constructor for class fmt.
#' @param class Subclasses to assign to the new object, default: none.
#' @keywords internal
# @export
new_fmt <- function(n = integer(),
type = "n" ,
digits = rep(0L , length(n)),
display = dplyr::case_when(
type == "mean" ~ "mean",
type %in% c("row", "col", "all", "all_tabs") ~ "pct" ,
TRUE ~ "n" ),
wn = rep(NA_real_, length(n)),
pct = rep(NA_real_, length(n)),
mean = rep(NA_real_, length(n)),
diff = rep(NA_real_, length(n)),
ctr = rep(NA_real_, length(n)),
var = rep(NA_real_, length(n)),
ci = rep(NA_real_, length(n)),
rr = rep(NA_real_, length(n)),
or = rep(NA_real_, length(n)),
in_totrow = rep(FALSE , length(n)),
in_tottab = rep(FALSE , length(n)),
in_refrow = rep(FALSE , length(n)),
comp_all = NA ,
ref = "" ,
ci_type = "" ,
col_var = "" ,
totcol = FALSE,
refcol = FALSE,
color = "" ,
..., class = character()
) {
# stopifnot(
# all(display %in% c("n", "wn", "pct", "pct_ci", "ctr", "mean", "mean_ci", "var", "ci")),
# type %in% c("row", "col", "all", "all_tabs", "mixed", NA_character_)
# )
# list(display, n, wn, pct, digits, ctr, mean, var, ci, col_var, totcol, type) %>%
# purrr::map(print)
# cat("\n")
# list(n = n, display = display, digits = digits,
# wn = wn, pct = pct, mean = mean,
# diff = diff, ctr = ctr, var = var, ci = ci,
# in_totrow = in_totrow, in_tottab = in_tottab,
# in_refrow = in_refrow) |>
# purrr::map(length) |> print()
# cat("\n")
#vctrs::vec_assert(display, character()) #check display or size
display <- vctrs::vec_recycle(display, size = length(n))
# vctrs::vec_assert(n , integer()) #, size = length(n)
# vctrs::vec_assert(wn , double() ) #, size = length(n)
# vctrs::vec_assert(pct , double() ) #, size = length(n)
# vctrs::vec_assert(digits, integer()) #, size = length(n)
# vctrs::vec_assert(ctr , double() ) #, size = length(n)
# vctrs::vec_assert(mean , double() ) #, size = length(n)
# vctrs::vec_assert(var , double() ) #, size = length(n)
# vctrs::vec_assert(ci , double() ) #, size = length(n)
#
# vctrs::vec_assert(in_totrow, logical())
# vctrs::vec_assert(in_tottab, logical())
#
# vctrs::vec_assert(type , character(), size = 1)
# vctrs::vec_assert(comp_all, logical() , size = 1)
# vctrs::vec_assert(ci_type , character(), size = 1)
# vctrs::vec_assert(col_var , character(), size = 1)
# vctrs::vec_assert(totcol , logical() , size = 1)
# vctrs::vec_assert(color , character(), size = 1)
vctrs::new_rcrd(
list(n = n, display = display, digits = digits,
wn = wn, pct = pct, mean = mean,
diff = diff, ctr = ctr, var = var, ci = ci, rr = rr, or = or,
in_totrow = in_totrow, in_tottab = in_tottab,
in_refrow = in_refrow),
type = type, comp_all = comp_all, ref = ref,
ci_type = ci_type, col_var = col_var, totcol = totcol, refcol = refcol,
color = color, class = c(class, "tabxplor_fmt"))
#access with fields() n_fields() vctrs::field() vctrs::`field<-`() ;
#vec_data() return the tibble with all fields
}
#' @keywords internal
fmt0 <- function(display = "n", digits = 0, type = "n") {
new_fmt(n = 0L, display = display, digits = as.integer(digits), type = type)
# switch (display,
# "n" = new_fmt(display = display, n = 0L, digits = as.integer(digits)),
# "wn" = new_fmt(display = display, n = 0L, wn = 0, digits = as.integer(digits)),
# "pct" = ,
# "pct_ci" = new_fmt(display = display, n = 0L, wn = 0, pct = 0, digits = as.integer(digits)),
# "ctr" = new_fmt(display = display, n = 0L, wn = 0, pct = 0, ctr = 0, digits = as.integer(digits)),
# "mean" = ,
# "mean_ci" = new_fmt(display = display, n = 0L, wn = 0, mean = 0, var = 0, digits = as.integer(digits)),
# "var" = new_fmt(display = display, n = 0L, wn = 0, mean = 0, var = 0, digits = as.integer(digits)),
# "ci" = new_fmt(display = display, n = 0L, ci = 0, digits = as.integer(digits)),
# )
}
# Internal functions to get fields and attributes of class fmt
#' @keywords internal
fmt_field_factory <- function(.field) {
function(x) vctrs::field(x, .field)
}
# @describeIn fmt
#' get the "display" field of a \code{fmt} vector
#' @param x The formatted number in which you want to find data for "get" functions,
#' to modify data for "set" functions.
#' @keywords internal
# @export
get_display <- fmt_field_factory("display")
# @describeIn fmt get the "n" field (unweighted counts)
#' @keywords internal
# @export
get_n <- fmt_field_factory("n")
# @describeIn fmt get the "wn" field (weighted counts)
#' @keywords internal
# @export
get_wn <- function(x) { #If there is no weighted counts, take counts
out <- vctrs::field(x, "wn")
if (any(is.na(out))) {
counts <- vctrs::field(x, "n") %>% as.double()
out[is.na(out)] <- counts[is.na(out)]
}
out
}
# @describeIn fmt get the "pct" field
#' @keywords internal
# @export
get_pct <- fmt_field_factory("pct")
# @describeIn fmt get the "diff" field (differences from totals or first cells)
#' @keywords internal
# @export
get_diff <- fmt_field_factory("diff")
#get_pct_ci <- function(x) vctrs::field("pct")
#' @describeIn fmt get the "digits" field
# @keywords internal
#' @export
get_digits <- fmt_field_factory("digits")
# @describeIn fmt get the "ctr" field (relative contributions of cells to variance)
#' @keywords internal
# @export
get_ctr <- fmt_field_factory("ctr")
# @describeIn fmt get the "mean" field
#' @keywords internal
# @export
get_mean <- fmt_field_factory("mean")
# @describeIn fmt get the "var" field (cell variances of means)
#' @keywords internal
# @export
get_var <- fmt_field_factory("var")
# @describeIn fmt get the "ci" field (confidence intervals)
#' @keywords internal
# @export
get_ci <- fmt_field_factory("ci")
# @describeIn fmt get the "rr" field (relative risk)
#' @keywords internal
# @export
get_rr <- fmt_field_factory("rr")
# @describeIn fmt get the "or" field (odds ratio or relative risk ratio)
#' @keywords internal
# @export
get_or <- fmt_field_factory("or")
#' @keywords internal
get_mean_contrib <- function(x) {
comp <- get_comp_all(x)
totrows <- is_totrow(x)
tottabs <- is_tottab(x)
ctr <- get_ctr(x)
if (!any(totrows)) return(rep(NA_real_, length(x)))
if (comp) {
rep(ctr[totrows & tottabs], length(x))
} else {
tibble::tibble(
ctr = ctr,
gr = cumsum(as.integer(totrows)) - as.integer(totrows) ) %>%
dplyr::mutate(nb = dplyr::row_number()) %>%
dplyr::with_groups("gr", ~ dplyr::mutate(., nb = dplyr::last(.data$nb))) %>%
dplyr::mutate(mean_ctr = .data$ctr[.data$nb]) %>% dplyr::pull(.data$mean_ctr)
}
}
#' @keywords internal
get_ref_means <- function(x) {
comp <- get_comp_all(x)
ref <- get_ref_type(x)
refrows <- if (ref == "tot") { is_totrow(x) } else { is_refrow(x) }
tottabs <- is_tottab(x)
mean <- get_mean(x)
if (comp) {
refs <- refrows & tottabs
if (!any(refs)) {rep(NA_real_, length(x))} else {rep(mean[refs], length(x))}
#refs <- mean[refrows & tottabs]
#if (length(refs) == 0) {rep(NA_real_, length(x))} else {rep(mean[refs], length(x))}
} else {
tibble::tibble(
mean = mean,
gr = cumsum(as.integer(refrows)) - as.integer(refrows) ) %>%
dplyr::mutate(nb = dplyr::row_number()) %>%
dplyr::with_groups("gr", ~ dplyr::mutate(., nb = dplyr::last(.data$nb))) %>%
dplyr::mutate(ref_means = .data$mean[.data$nb]) %>%
dplyr::pull(.data$ref_means)
}
}
#' @keywords internal
get_ref_pct <- function(x) {
comp <- get_comp_all(x)
ref <- get_ref_type(x)
refrows <- if (ref == "tot") { is_totrow(x) } else { is_refrow(x) }
tottabs <- is_tottab(x)
pct <- get_pct(x)
if (comp) {
refs <- refrows & tottabs # pct[refrows & tottabs]
if (!any(refs)) {rep(NA_real_, length(x))} else {rep(pct[refs], length(x))}
} else {
tibble::tibble(
pct = pct,
gr = cumsum(as.integer(refrows)) - as.integer(refrows) ) %>%
dplyr::mutate(nb = dplyr::row_number()) %>%
dplyr::with_groups("gr", ~ dplyr::mutate(., nb = dplyr::last(.data$nb))) %>%
dplyr::mutate(ref_pcts = .data$pct[.data$nb]) %>%
dplyr::pull(.data$ref_pcts)
}
}
# "every S3 method must be exported, even if the generic is not" Really (->CRAN pb) ??
# #' @return A character vector with the vectors type.
# #' @return An empty character vector.
# #' @return A single string with the vector's type.
# #' @return A character vector with the data.frame column's types.
# #' @return A modified fmt vector.
#
# #' @return A logical vector with the fmt vectors tottab field.
# #' @return A logical vector with \code{FALSE}.
# #' @return A logical vector with the tottab field.
# #' @return A list of logical vectors, with the data.frame column's tottab fields.
# #' @return A modified fmt vector with tottab field changed.
#
# #' @return A logical vector with the fmt vectors totcol attribute.
# #' @return A logical vector with \code{FALSE}.
# #' @return A single logical vector with the totcol attribute
# #' @return A logical vector, with the data.frame column's totcol attributes.
# #' @return A modified fmt vector with totcol attribute changed.
#' @keywords internal
detect_firstcol <- function(tabs) {
col_vars <- get_col_var(tabs)
firstcol <- which(col_vars != dplyr::lag(col_vars, default = NA_character_))
if (any(col_vars == "all_col_vars")) firstcol <- firstcol %>%
purrr::discard(names(.) == names(col_vars)[col_vars == "all_col_vars"])
res <- purrr::map(1:ncol(tabs), function(.i)
tidyr::replace_na(
dplyr::last(names(firstcol[firstcol <= .i]) ),
"")) %>%
rlang::syms() %>%
purrr::set_names(names(tabs))
if (any(col_vars == "all_col_vars")) {
# res_all_col <- tabs[as.character(res[col_vars == "all_col_vars"])]
#
# if (get_type(res_all_col) == "mean") res[col_vars == "all_col_vars"] <-
# rlang::syms("")
res[col_vars == "all_col_vars"] <- rlang::syms("")
}
res
}
#For each column, detect which total column it depends on
#' @keywords internal
detect_totcols <- function(tabs) {
#detect totcols by col vars names, no position ? ----
tot <- which(is_totcol(tabs) | get_col_var(tabs) == "no_col_var")
purrr::map(1:ncol(tabs), function(.i)
tidyr::replace_na(names(tot[tot >= .i])[1], "")) %>%
rlang::syms() %>%
purrr::set_names(names(tabs))
}
# Internal functions to modify class tabxplor_fmt
#' @keywords internal
fmt_set_field_factory <- function(.field, cast) {
function(x, value) {
value <- vctrs::vec_cast(value, cast) %>% vctrs::vec_recycle(size = length(x))
vctrs::`field<-`(x, .field, value)
}
}
# @describeIn fmt set the "n" field (unweighted counts)
#' @keywords internal
# @export
set_n <- fmt_set_field_factory("n" , cast = integer() )
# @describeIn fmt set the "wn" field (weighted counts)
#' @keywords internal
# @export
set_wn <- fmt_set_field_factory("wn" , cast = double() )
# @describeIn fmt set the "pct" field
#' @keywords internal
# @export
set_pct <- fmt_set_field_factory("pct" , cast = double() )
# @describeIn fmt set the "diff" field (differences from totals or first cells)
#' @keywords internal
# @export
set_diff <- fmt_set_field_factory("diff" , cast = double() )
#' @describeIn fmt set the "digits" field
# @keywords internal
#' @export
set_digits <- fmt_set_field_factory("digits" , cast = integer() )
# @describeIn fmt set the "ctr" field (relative contributions of cells to variance)
# @keywords internal
# @export
set_ctr <- fmt_set_field_factory("ctr" , cast = double() )
# @describeIn fmt set the "mean" field
#' @keywords internal
# @export
set_mean <- fmt_set_field_factory("mean" , cast = double() )
# @describeIn fmt set the "var" field (cell variances of means)
#' @keywords internal
# @export
set_var <- fmt_set_field_factory("var" , cast = double() )
# @describeIn fmt set the "ci" field (confidence intervals)
#' @keywords internal
# @export
set_ci <- fmt_set_field_factory("ci" , cast = double() )
# @describeIn fmt set the "rr" field (relative risk)
#' @keywords internal
# @export
set_rr <- fmt_set_field_factory("rr" , cast = double() )
# @describeIn fmt set the "or" field (odds ratio or relative risk ratio)
#' @keywords internal
# @export
set_or <- fmt_set_field_factory("or" , cast = double() )
# METHODS FOR CLASS tabxplor_fmt #########################################################
#' @keywords internal
print_num <- function(num, digits) {
sprintf(paste0("%-0.", digits, "f"), num) %>%
stringr::str_replace("^0.0+$|^-0.0+$", "0") %>%
stringr::str_replace("^100.0+$", "100")
}
# WORKING ON CONSOLE AND RMARKDOWN BUT NOT IN JAMOVI
ci_html_subscript <- function(x, html = FALSE) {
if (html) x <- dplyr::if_else(
condition = stringr::str_detect(x,"^ *$" ),
true = "",
false = x #paste0("$_{", x, "}$")
# paste0('<span style="vertical-align: baseline; position: relative;top: -0.5em;>', x, '</span>')
# paste0("<p><sub>", x, "</sub></p>")
)
x
}
# Format/printing methods for class tabxplor_fmt -----------------------------------------
#The first method for every class should almost always be a format() method.
#This should return a character vector the same length as x.
#' Print method for class tabxplor_fmt
#'
#' @param x A fmt object.
#' @param ... Other parameters.
#' @param html Should html tags be added (to print confidence intervals as subscripts) ?
#' @param na How `NA`s should be printed. Default to `NA`.
#' @param special_formatting Set to `TRUE` to print more verbose results,
#' like indicating which is the reference row or col for differences.
#'
#' @return The fmt printed in a character vector.
#' @export
format.tabxplor_fmt <- function(x, ..., html = FALSE, na = NA,
special_formatting = FALSE) {
out <- get_num(x)
na_out <- is.na(out)
display <- get_display(x)
nas <- is.na(display)
digits <- get_digits(x)
digits[!nas & display == "n"] <- 0
digits[!nas & display %in% c("or", "or_pct", "OR", "OR_pct") & # no "var" (used in chi2_table)
digits < 2L] <- 2L
ok <- !na_out & !nas
type <- get_type(x)
ci_type <- get_ci_type(x)
pm <- stringi::stri_unescape_unicode("\\u00b1") # sign "plus minus"
pct_or_ci <- ok & display %in% c("pct", "pct_ci", "diff", "ci", "ctr") &
!(display %in% c("ci", "diff") & type == "mean")
pct_ci <- ok & display == "pct_ci"
mean_ci <- ok & display == "mean_ci"
diff_mean <- ok & display == "diff" & type == "mean"
disp_ci <- display == "ci" & ci_type == "diff" & !nas
plus_ci <- (pct_ci | mean_ci) # ci_pct_mean
plus_disp_ci <- (plus_ci | disp_ci)
# plus_ci <- (ci_pct_mean | disp_ci)# & !is.na(get_ci(x))
#pct_or_pct_ci <- ok & display %in% c("pct", "pct_ci", "diff", "ctr")
pct_no_ci <- ok & display %in% c("pct", "diff", "ctr") & !(display == "diff" & type == "mean")
diff_pct <- ok & display == "diff" & type != "mean"
n_wn <- ok & (display %in% c("n", "wn", "mean", "mean_ci", "var", "rr", "or", "or_pct",
"OR", "OR_pct") |
(display == "ci" & type == "mean") )
type_ci <- ok & display == "ci"
pvalue <- ok & display == "pvalue"
out[pct_or_ci] <- out[pct_or_ci] * 100
digits[diff_mean] <- dplyr::if_else(digits[diff_mean] == 0, 1, digits[diff_mean])
ci_print_moe <- getOption("tabxplor.ci_print") == "moe"
if (any(plus_ci | disp_ci)) {
if (any(plus_ci) & ci_print_moe) {
ci <- dplyr::if_else(condition = mean_ci[plus_ci],
true = get_ci(x)[plus_ci] ,
false = get_ci(x)[plus_ci] * 100)
ci_print_trim <- function(x) {
x <- stringr::str_remove_all(x, paste0("^", pm, "0$|^", pm, "0.0+$|^", pm, "-0.0+$|^",
pm, "NA"))
stringr::str_pad(x, max(stringr::str_length(x)))
}
# ci_print_pad <- function(x) {
# stringr::str_pad(x, max(stringr::str_length(x)))
# }
out_ci <-
paste0(print_num(out[plus_ci], digits[plus_ci]),
dplyr::if_else(pct_ci[plus_ci], "%", ""),
ci_print_trim(paste0(pm, sprintf(
paste0("%-0.",
digits[plus_ci] + dplyr::if_else(pct_ci[plus_ci] & digits[plus_ci] == 0, 1L, 0L),
"f"), ci
)) ) %>% ci_html_subscript(html = html)
)
} else if (any(plus_disp_ci) ) { # !ci_print_moe
ci <- dplyr::if_else(condition = plus_disp_ci[plus_disp_ci] & type == "mean", # mean_ci[plus_disp_ci],
true = get_ci(x)[plus_disp_ci] ,
false = get_ci(x)[plus_disp_ci] * 100)
# # it the formula for mean wrong ?
# ci[disp_ci[plus_disp_ci & type == "mean"]] <-
# ci[disp_ci[plus_disp_ci & type == "mean"]] *
# get_ref_means(x)[disp_ci[plus_disp_ci & type == "mean"]]
# # color selection formulas for means :
# # diff_ci
# means & !neg ~ diff > brk &
# abs(1 - diff) * ref_means_pct - ci > 0,
#
# means & neg ~ diff < brk &
# abs(1 - diff) * ref_means_pct - ci > 0,
#
# # after_ci
# means & !neg
# ~ diff >= 1 &
# ref_means_pct + abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * brk, # &
#
# means & neg
# ~ diff < 1 &
# ref_means_pct + abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * -brk, # &
ref_for_ci <- dplyr::if_else(
disp_ci[plus_disp_ci],
true = dplyr::if_else(plus_disp_ci[plus_disp_ci] & type == "mean",
true = get_diff(x)[plus_disp_ci],
false = get_diff(x)[plus_disp_ci] * 100 ),
false = out[plus_disp_ci])
lower <- ref_for_ci - ci
upper <- ref_for_ci + ci
lower <- dplyr::if_else(pct_ci[plus_disp_ci], pmax(lower, 0), lower)
upper <- dplyr::if_else(pct_ci[plus_disp_ci], pmin(upper, 100), upper)
out_ci <- dplyr::if_else(
condition = is.na(lower) | is.na(upper) |
round(lower, digits[plus_disp_ci]) == round(upper, digits[plus_disp_ci]),
true = print_num(ref_for_ci, digits[plus_disp_ci]),
false = paste0("[",
sprintf(paste0("%-0.", digits[plus_disp_ci], "f"), lower),
";", #", ",
#stringi::stri_unescape_unicode("\\u00b7"), # middle-point
sprintf(paste0("%-0.", digits[plus_disp_ci], "f"), upper),
"]"
)
)
out_ci <- paste0(out_ci, dplyr::if_else(plus_disp_ci[plus_disp_ci] & type == "mean", "", "%")) # pct_ci[plus_disp_ci]
}
}
# }
out[!na_out] <- print_num(out[!na_out], digits[!na_out])
out[na_out] <- NA
if (any(plus_ci | disp_ci)) {
if (any(plus_ci) | ci_print_moe) {
out[plus_ci] <- out_ci
} else if (any(plus_disp_ci)) {
out[plus_disp_ci] <- out_ci
}
}
out[n_wn] <- out[n_wn] %>% prettyNum(big.mark = " ", preserve.width = "individual")
out[pct_no_ci] <- paste0(out[pct_no_ci], "%") #pillar::style_subtle()
if (any(pvalue)) {
p <- get_pct(x[pvalue])
out[pvalue] <- paste0(
dplyr::if_else(
p < 0.0001,
true = "<0.01",
false = print_num(p * 100, digits = 2L)
),
"%"
)
}
out[diff_pct] <- dplyr::if_else( # "+" sign on positive pct diffs
!stringr::str_detect(out[diff_pct], "^-"), # !out[diff_pct] %in% c("0%", ) &
true = paste0("+", out[diff_pct]),
false = out[diff_pct]
)
out[diff_mean] <- paste0(mult_sign, out[diff_mean]) # multiply sign on mean diffs
if (ci_print_moe) {
out[type_ci] <- switch(
type,
"n" = ,
"mean" = paste0(pm, out[type_ci]),
"row" = ,
"col" = ,
"all" = ,
"all_tabs"= paste0(pm, out[type_ci], "%") |> stringr::str_replace_all("%%", "%")
)
}
if (special_formatting) {
disp_diff <- display == "diff" & !nas
disp_moe <- disp_ci & ci_print_moe # no if `ci_print = "ci"`
disp_ctr <- display == "ctr" & !nas
disp_or <- display %in% c("or", "OR") & !nas
disp_or_pct <- display %in% c("or_pct", "OR_pct") & !nas
disp_mean_sd <- display == "mean" & type == "mean" & !nas & !is.na(x$var)
if (any (disp_mean_sd)) {
sd <-
print_num(get_num(set_display(set_var(x[disp_mean_sd],
suppressWarnings(sqrt(get_var(x[disp_mean_sd]))) ), "var")),
digits = x[disp_mean_sd]$digits) # + 1L
sd <- sd |>
stringr::str_pad(width = max(stringr::str_length(sd)), side = "right")
out[disp_mean_sd] <- paste0(out[disp_mean_sd], unbrk, "(", sigma_sign, sd, ")")
}
if (any(disp_diff)) {
ref <- get_reference(x[disp_diff], mode = "cells")
reffmt <- set_display(x[disp_diff],
ifelse(type %in% c("n", "mean"), "mean", "pct")) %>%
format() #%>% stringr::str_trim()
out[disp_diff] <- dplyr::if_else(ref,
paste0("ref:", reffmt),
out[disp_diff])
}
if (any(disp_moe)) {
ref <- get_reference(x[disp_moe], mode = "cells")
reffmt <- set_display(x[disp_moe],
ifelse(type %in% c("n", "mean"), "mean", "pct")) %>%
format()
out[disp_moe] <- dplyr::if_else(ref,
paste0("ref:x-", reffmt),
out[disp_moe])
}
if (any(disp_ctr)) {
comp <- get_comp_all(x)
totcol <- is_totcol(x)
totrows <- is_totrow(x)
tottabs <- is_tottab(x)
mctr <- if (comp) {
disp_ctr & totrows & tottabs & !totcol
} else {
disp_ctr & totrows & !totcol
}
out[mctr] <- paste0("mean:", stringr::str_trim(out[mctr])) %>%
stringr::str_remove("mean:Inf%|NA")
}
if (any(disp_or)) {
# refcol <- is_refcol(x)
refer <- get_reference(x[disp_or], mode = "all_totals")
reffmt <- set_display(x[disp_or], "pct") |> # ifelse(refcol, "pct", "rr")
set_digits(0L) |> format() #%>% stringr::str_trim()
reffmt <- suppressWarnings(
stringr::str_pad(reffmt,
suppressWarnings(
max(stringr::str_length(reffmt), na.rm = TRUE)
)
)
)
out[disp_or] <- dplyr::if_else(
refer & !is.na(reffmt),
paste0(stringr::str_replace(out[disp_or], "1.0+", "1"),
" (", reffmt, ")"),
out[disp_or]
)
# out[disp_or] <- dplyr::case_when(
# ref & type == "row" & refcol ~ paste0("1 (ref)"),
# ref & type == "row" ~ paste0("1 (rel ", reffmt, ")"),
# ref & type == "col" & refrows~ paste0("1 (ref)"),
# ref & type == "col" ~ paste0("1 (rel ", reffmt, ")"),
# TRUE ~ out[disp_or]
# )
}
if (any(disp_or_pct)) {
reffmt <- set_display(x[disp_or_pct], "pct") |> set_digits(0L) |> format()
out[disp_or_pct] <- paste0(out[disp_or_pct], " (", reffmt, ")")
}
}
#out <- stringr::str_pad(out, max(stringr::str_length(out), na.rm = TRUE))
out
}
#' Pillar_shaft method to print class fmt in a \code{\link[tibble:tibble]{tibble}} column
#'
#' @param x A fmt object.
#' @param ... Other parameter.
#'
#'
#' @return A fmt printed in a pillar.
#' @importFrom pillar pillar_shaft
#' @export
pillar_shaft.tabxplor_fmt <- function(x, ...) {
# print color type somewhere (and brk legend beneath ?) ----
out <- format(x, special_formatting = TRUE)
display <- get_display(x)
nas <- is.na(display)
color <- get_color(x)
type <- get_type(x)
#totcol <- is_totcol(x)
totrows <- is_totrow(x)
#tottabs <- is_tottab(x)
#
# comp <- get_comp_all(x)
#
# ci_type <- get_ci_type(x)
# pct <- get_type(x)
#
# disp_diff <- display == "diff" & !nas
# disp_ci <- display == "ci" & ci_type == "diff" & !nas
# disp_ctr <- display == "ctr" & !nas
# disp_or <- display == "or" & !nas
# disp_or_pct<-display == "or_pct" & !nas
#
# if (any(disp_diff)) {
# ref <- get_reference(x[disp_diff], mode = "cells")
# reffmt <- set_display(x[disp_diff],
# ifelse(type %in% c("n", "mean"), "mean", "pct")) %>%
# format() #%>% stringr::str_trim()
# out[disp_diff] <- dplyr::if_else(ref,
# paste0("ref:", reffmt),
# out[disp_diff])
# }
#
# if (any(disp_ci)) {
# ref <- get_reference(x[disp_ci], mode = "cells")
# reffmt <- set_display(x[disp_ci],
# ifelse(type %in% c("n", "mean"), "mean", "pct")) %>%
# format()
# out[disp_ci] <- dplyr::if_else(ref,
# paste0("ref:x-", reffmt),
# out[disp_ci])
# }
#
# if (any(disp_ctr)) {
# mctr <- if (comp) {
# disp_ctr & totrows & tottabs & !totcol
# } else {
# disp_ctr & totrows & !totcol
# }
# out[mctr] <- paste0("mean:", stringr::str_trim(out[mctr])) %>%
# stringr::str_remove("mean:Inf%|NA")
# }
#
# if (any(disp_or)) {
# # refcol <- is_refcol(x)
# ref <- get_reference(x[disp_or], mode = "all_totals")
# reffmt <- set_display(x[disp_or], "pct") %>% # ifelse(refcol, "pct", "rr")
# set_digits(0L) |> format() #%>% stringr::str_trim()
# reffmt <- stringr::str_pad(reffmt, max(stringr::str_length(reffmt)) )
# out[disp_or] <- dplyr::if_else(
# ref,
# paste0(stringr::str_replace(out[disp_or], "1.0+", "1"),
# " (", reffmt, ")"),
# out[disp_or]
# )
# # out[disp_or] <- dplyr::case_when(
# # ref & type == "row" & refcol ~ paste0("1 (ref)"),
# # ref & type == "row" ~ paste0("1 (rel ", reffmt, ")"),
# # ref & type == "col" & refrows~ paste0("1 (ref)"),
# # ref & type == "col" ~ paste0("1 (rel ", reffmt, ")"),
# # TRUE ~ out[disp_or]
# # )
# }
#
# if (any(disp_or_pct)) {
# reffmt <- set_display(x[disp_or_pct], "pct") |> set_digits(0L) |> format()
# out[disp_or_pct] <- paste0(out[disp_or_pct], " (", reffmt, ")")
# }
if (color == "contrib" & !any(totrows)) warning(
"cannot print color == 'contrib' with no total rows to store ",
"information about mean contributions to variance"
) # store mean_contrib in a vctrs::field of fmt ? ----
na_out <- is.na(out)
ok <- !na_out & !nas
if (!is.na(color) & ! color %in% c("no", "") & !(color == "contrib" & !any(totrows))) {
color_selection <- fmt_color_selection(x)
pct_diff <- color %in% c("diff", "diff_ci", "after_ci") & !type %in% c("n", "mean")
color_styles <- select_in_color_style(names(color_selection), pct_diff = pct_diff)
color_styles <- get_color_style()[color_styles]
unselected <- purrr::transpose(color_selection) %>%
purrr::map_lgl(~ ! any(purrr::flatten_lgl(.)))
out[ok] <-
purrr::reduce2(.init = out[ok], .x = purrr::map(color_selection, ~ .[ok]),
.y = color_styles,
~ dplyr::if_else(..2, rlang::exec(..3, ..1), ..1) )
totals <- get_reference(x, mode = "all_totals") #c("cells", "lines")
out[ok & unselected & !totals] <- #fmtgrey3
pillar::style_subtle(out[ok & unselected & !totals])
#Columns with no color
} else {
# - problem with bold in console : it offsets all column unaesthetically
# - use underline and | to make the imitate the borders of a table
# if (any(totrows)) out <- dplyr::if_else(totrows & ! totcol,
# crayon::underline(out), out)
# if (totcol) out <- dplyr::if_else(totrows,
# paste0(crayon::underline(out), "|"),
# paste0(out, "|"))
# # - normal cells a bit grayer to see the totals better
# totals <- get_reference(x, mode = "all_totals")
# out[ok & !totals] <- fmtgrey4(out[ok & !totals])
out[ok] <- out[ok] %>%
stringr::str_replace("^0%$|^-0%$", pillar::style_subtle("0%")) %>% # 0 in gray
stringr::str_replace("^0$|^0$", pillar::style_subtle("0"))
}
pillar::new_pillar_shaft_simple(out, align = "right", na = "")
}
#' Print Chi2 tables columns
#' @param x A fmt object.
#' @param ... Other parameter.
#' @export
#' @return A Chi2 table column printed in a pillar.
# @keywords internal
# @method pillar_shaft tab_chi2_fmt
pillar_shaft.tab_chi2_fmt <- function(x, ...) {
# print color type somewhere (and brk legend beneath ?) ----
out <- format(x)
display <- get_display(x)
nas <- is.na(display)
color_style <- get_color_style()
pvalues <- out[!nas & display == "pvalue"]
p_values <- get_num(x)[!nas & display == "pvalue"]
out[!nas & display == "pvalue"] <-
dplyr::if_else(condition = p_values >= 0.05,
true = color_style$neg5(pvalues),
false = color_style$pos5(pvalues) )
pillar::new_pillar_shaft_simple(out, align = "right", na = "")
}
#' mutate method to access vctrs::fields of tabxplor_fmt vectors
#' @importFrom dplyr mutate
#' @method mutate tabxplor_fmt
#' @param .data A tabxplor_fmt column.
#' @param ... Name-value pairs.
#' The name gives the name of the column in the output (do not change it).
#'
#' The value can be:
#'
#' * A vector of length 1, which will be recycled to the correct length.
#' * A vector the same length as the current group (or the whole data frame
#' if ungrouped).
#' @return An object of class \code{tabxplor_fmt}.
#' @export
mutate.tabxplor_fmt <- function(.data, ...) {
dots <- rlang::enquos(...)
.data |>
vctrs::vec_proxy() |>
dplyr::mutate(!!!dots, .keep = "all", .before = NULL, .after = NULL) |>
vctrs::vec_restore(.data)
}
#' $ method for class tabxplor_fmt
#' @param x A tabxplor_fmt object.
#' @param name The name of the field to extract.
# @method `$` tabxplor_fmt
#' @return The relevant field of the tabxplor_fmt.
#' @export
`$.tabxplor_fmt` <- function(x, name) {
if (name == "wn" & all(is.na( dplyr::pull(vctrs::vec_proxy(x), "wn")))) {
dplyr::pull(vctrs::vec_proxy(x), "n")
} else {
dplyr::pull(vctrs::vec_proxy(x), name)
}
}
# #Problem : dplyr::last doesn't work anymore with fmt, because it relies on `[[`
# #' Extract method for class tabxplor_fmt
# #' @param x A tabxplor_fmt object.
# #' @param i,j,... Indices of names of the field to extract.
# #' @method `[[` tabxplor_fmt
# #' @return The relevant field of the tabxplor_fmt.
# #' @export
# `[[.tabxplor_fmt` <- function(x, i, j, ...) {
# if (missing(j)) {
# suppressWarnings(`[[`(vctrs::vec_proxy(x), i = i, ..., exact = TRUE))
# } else {
# suppressWarnings(`[[`(vctrs::vec_proxy(x), i = i, j = j, ..., exact = TRUE))
# }
# }
#' @keywords internal
fmt_color_selection <- function(x, force_color, force_breaks) {
type <- get_type (x)
type_ci <- get_ci_type(x)
color <- if (missing(force_color)) get_color(x) else force_color
# color <- get_color(x)
if (!missing(force_breaks)) {
mean_breaks <- force_breaks$mean_breaks
pct_breaks <- force_breaks$pct_breaks
mean_ci_breaks <- force_breaks$mean_ci_breaks
pct_ci_breaks <- force_breaks$pct_ci_breaks
contrib_breaks <- force_breaks$contrib_breaks
# mean_brksup <- force_breaks$mean_brksup
# pct_brksup <- force_breaks$pct_brksup
# mean_ci_brksup <- force_breaks$mean_ci_brksup
# pct_ci_brksup <- force_breaks$pct_ci_brksup
# contrib_brksup <- force_breaks$contrib_brksup
# pct_ratio_breaks<- force_breaks$pct_ratio_breaks
# pct_ratio_brksup<- force_breaks$pct_ratio_brksup
} else {
tabxplor_color_breaks <- getOption("tabxplor.color_breaks")
mean_breaks <- tabxplor_color_breaks$mean_breaks
pct_breaks <- tabxplor_color_breaks$pct_breaks
mean_ci_breaks <- tabxplor_color_breaks$mean_ci_breaks
pct_ci_breaks <- tabxplor_color_breaks$pct_ci_breaks
contrib_breaks <- tabxplor_color_breaks$contrib_breaks
# mean_brksup <- tabxplor_color_breaks$mean_brksup
# pct_brksup <- tabxplor_color_breaks$pct_brksup
# mean_ci_brksup <- tabxplor_color_breaks$mean_ci_brksup
# pct_ci_brksup <- tabxplor_color_breaks$pct_ci_brksup
# contrib_brksup <- tabxplor_color_breaks$contrib_brksup
# pct_ratio_breaks<- tabxplor_color_breaks$pct_ratio_breaks
# pct_ratio_brksup<- tabxplor_color_breaks$pct_ratio_brksup
}
brk <-
switch(color,
"diff" = ,
"diff_ci" = if (type == "mean") mean_breaks else pct_breaks ,
"ci" = if (type == "mean"){
mean_ci_breaks[c(1, length(mean_ci_breaks)/2 + 1)]
} else {
pct_ci_breaks[c(1, length(pct_ci_breaks)/2 + 1)]
} ,
"after_ci" = if (type == "mean") mean_ci_breaks else pct_ci_breaks,
"contrib" = contrib_breaks,
"or" = ,
"OR" = mean_breaks,
)
brk_no_zero <- dplyr::na_if(brk, 0) # otherwise 0 rule is considered as positive...
negative_breaks <- (type != "mean" & !color %in% c("or", "OR") & brk_no_zero < 0) |
( (type == "mean" | color %in% c("or", "OR") ) & brk_no_zero < 1)
negative_breaks <- tibble::tibble(negative_breaks) |>
tidyr::fill("negative_breaks", .direction = "up") |>
dplyr::pull("negative_breaks")
pct_ratio <- !type %in% c("mean", "n") & brk > 1 # !color %in% c("or", "OR") &
# brksup <-
# switch(color,
# "diff" = ,
# "diff_ci" = if (type == "mean") mean_brksup else pct_brksup ,
# "ci" = if (type == "mean") {
# mean_ci_brksup[c(length(mean_ci_brksup)/2, length(mean_ci_brksup))]
# } else {
# pct_ci_brksup[c(length(pct_ci_brksup)/2, length(pct_ci_brksup))]
# },
# "after_ci" = if (type == "mean") mean_ci_brksup else pct_ci_brksup,
# "contrib" = contrib_brksup,
# "or" = ,
# "OR" = mean_brksup
# )
diff <- if (color %in% c("diff", "diff_ci", "after_ci", "ci") ) {
get_diff(x)
} else {
rep(NA_real_, length(x)) #vctrs::vec_recycle(NA_real_, length(x))
}
# *2 rule for pct
ratio <- if (color %in% c("diff", "diff_ci", "after_ci") & type != "mean" ) { # pct
get_mean(x)
} else {
rep(NA_real_, length(x)) #vctrs::vec_recycle(NA_real_, length(x))
}
ci <- if (color %in% c("diff_ci", "after_ci", "ci") & type_ci == "diff" ) {
get_ci(x)
} else {
NA_real_
}
ref_means_pct <- if (color %in% c("diff_ci", "after_ci", "ci") & type == "mean") {
get_ref_means(x)
} else if (color %in% c("diff", "diff_ci", "after_ci") & any(pct_ratio)) {
get_ref_pct(x)
} else {
NA_real_
}
ctr <- if (color == "contrib") {
dplyr::if_else(is_totrow(x),
true = NA_real_,
false = get_ctr(x))
} else {
NA_real_
}
mean_ctr <- if (color == "contrib") {
get_mean_contrib(x)
} else {
NA_real_
}
# rr <- if (color %in% c("OR") ) {
# get_rr(x)
# } else {
# NA_real_
# }
or <- if (color %in% c("OR", "or") ) {
get_or(x)
} else {
NA_real_
}
# pct <- if (color %in% c("OR") ) {
# get_pct(x)
# } else {
# NA_real_
# }
selection <-
purrr::pmap(list(brk, negative_breaks, pct_ratio), # brksup , # brk_ratio_condition
~ color_formula(type = type, color = color,
diff = diff,
neg = ..2,
#brksup_ratio = if(..3) {ratio} else {rep(0, length(ratio))},
pct_ratio = ..3,
ratio = ratio, # *2 pct rule
# diff_sup = if(.x > 1 & !type %in% c("mean", "n") ) {
# ratio # *2 pct rule
# } else {
# diff
# },
# diff_sub = if(..2 > 1 & !type %in% c("mean", "n") ) {
# ratio # *2 pct rule
# } else {
# diff
# },
ci = ci, ref_means_pct = ref_means_pct,
# ratio = ratio,
ctr = ctr, mean_ctr = mean_ctr,
or = or, # rr = rr, pct = pct,
brk = ..1) # brksup = ..2
) %>% purrr::set_names(as.character(round(brk, 2)))
# when many rules are TRUE, take the last one (positive and negative)
c(keep_last_break(selection[!negative_breaks]),
keep_last_break(selection[negative_breaks]) )
}
# !means & brk >= 0 & brksup > 0
# ~ diff >= 0 & abs(diff) - ci > brk & abs(diff) - ci < brksup,
#
# !means & brk <= 0 & brksup < 0
# ~ diff < 0 & abs(diff) - ci > -brk & abs(diff) - ci < -brksup),
# diff >= 1 &
# (1 + abs(1 - diff) ) * ref_means_pct > (ref_means_pct + ci) * brk[1] &
# abs(1 - diff) * ref_means_pct < (ref_means_pct + ci) * brksup[1]
#
#
# ref_means_pct + abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * brk[1]
# ref_means_pct + abs(1 - diff) * ref_means_pct > ref_means_pct * brk[1] + ci * brk[1]
# abs(1 - diff) * ref_means_pct > ref_means_pct * brk[1] - ref_means_pct + ci * brk[1]
# abs(1 - diff) * ref_means_pct > ref_means_pct * (brk[1] - 1) + ci * brk[1]
#
# (ref + ci) * brk -ref
#
# abs(1 - diff) > brk[1] - 1 + ci/ref_means_pct * brk[1]
# abs(1 - diff) + 1 > brk[1] * (ci/ref_means_pct + 1)
#
# (1 + abs(1 - diff)) * ref_means_pct > ref_means_pct * brk[1] + ci * brk[1]
#
# ctr >= brk[1] * mean_ctr & ctr < brksup[1] * mean_ctr
# ctr >= brk[2] * mean_ctr & ctr < brksup[2] * mean_ctr
#' @keywords internal
keep_last_break <- function(color_selection) {
rownames <- names(color_selection)
color_tibble <-
purrr::reduce(1:length(color_selection[[1]]),
.init = tibble::tibble(color_selection),
~ dplyr::mutate(.x, !!rlang::sym(paste0("V", .y)) := purrr::map_lgl(
color_selection,
function(.var) .var[.y]
)
)
) |>
dplyr::select(-color_selection)
color_tibble <- color_tibble |>
dplyr::arrange(-dplyr::row_number()) |>
dplyr::mutate(dplyr::across(
tidyselect::everything(),
~ dplyr::row_number() == purrr::detect_index(., isTRUE)
)) |>
dplyr::arrange(-dplyr::row_number())
color_tibble |>
as.matrix() |>
t() |>
as.data.frame() |>
as.list() |>
purrr::set_names(rownames)
# color_selection |>
# dplyr::bind_rows() |>
# as.matrix() |>
# t() |>
# as.data.frame() |>
# tibble::rownames_to_column() |>
# tibble::as_tibble() |>
# dplyr::mutate(rowname = forcats::as_factor(rowname)) |>
# dplyr::arrange(dplyr::desc(rowname)) |>
# dplyr::mutate(dplyr::across(
# dplyr::where(is.logical),
# ~ dplyr::row_number() == purrr::detect_index(., isTRUE)
# )) |>
# dplyr::arrange(rowname) |>
# tibble::column_to_rownames("rowname") |>
# as.matrix() |>
# t() |>
# as.data.frame() |>
# as.list()
}
# brk <- brk[[4]] ; brksup <- brksup[[4]]
# brk <- brk[[3]] ; brksup <- brksup[[3]]
# brk <- brk[[4]]
# neg <- negative_breaks[[4]]
# pct_ratio <- pct_ratio[[4]]
#' @keywords internal
color_formula <- function(type, color, neg,
diff, diff_sup, pct_ratio, ratio,
ci, ref_means_pct,
ctr, mean_ctr, or, brk) { # brksup
means <- type %in% c("mean", "n")
# !means & brksup > 1
res <-
switch(
color,
"diff" =
if (!neg) { # if( (!means & brk >= 0) | (means & brk >= 1) ) {
(if (pct_ratio) {ratio} else {diff}) > brk #& diff_sub < brksup # & brksup_ratio < brksup
# sup <- if (!means & brk > 1) { ratio > brk } else { diff > brk} # ratio = *2 pct rule
# sub <- if (!means & brksup > 1) { ratio < brksup } else { diff < brksup}
# sup & sub
} else {
diff < brk #& diff > brksup
},
"or" = ,
"OR" = if (!neg) { # if(brk >= 1) {
or > brk # & or < brksup
} else {
or < brk # & or > brksup
},
"diff_ci" = dplyr::case_when(
means & !neg ~ diff > brk & # brk >= 1 # diff < brksup &
abs(1 - diff) * ref_means_pct - ci > 0,
means & neg ~ diff < brk & # brk < 1 # diff > brksup &
abs(1 - diff) * ref_means_pct - ci > 0,
!means & !neg ~ (if (pct_ratio) {ratio} else {diff}) > brk & abs(diff) - ci > 0, # brk >= 0 # & diff_sub < brksup
!means & neg ~ diff < brk & abs(diff) - ci > 0 ), # brk < 0 # & diff > brksup
"ci" = dplyr::case_when(
means & !neg # (brk == 1)
~ diff >= 1 & abs(1 - diff) * ref_means_pct > ci,
means & neg # (brk == -1)
~ diff < 1 & abs(1 - diff) * ref_means_pct > ci,
!means & !neg # brk == 0 # & brksup > 0 # NOT wORKING ?
~ diff >= 0 & abs(diff) > ci,
!means & neg # brk == 0 # & brksup < 0 # # NOT wORKING ?
~ diff < 0 & abs(diff) > ci
),
"after_ci" = dplyr::case_when(
means & !neg #brk > 0
~ diff >= 1 &
ref_means_pct + abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * brk, # &
#ref_means_pct + abs(1 - diff) * ref_means_pct < (ref_means_pct + ci) * brksup ,
#wrong : abs(1 - diff) > ci * brk
#wrong : abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * brk
means & neg #brk < 0
~ diff < 1 &
ref_means_pct + abs(1 - diff) * ref_means_pct > (ref_means_pct + ci) * -brk, # &
#ref_means_pct + abs(1 - diff) * ref_means_pct < (ref_means_pct + ci) * -brksup ,
# pct ratio *2 rule : did same than with means, MAY BE TOTAL BULLSHIT
!means & !neg & pct_ratio
~ diff >= 0 &
ref_means_pct + abs(1 - ratio) * ref_means_pct > (ref_means_pct + ci) * brk,
# !means & neg & pct_ratio
# ~ diff < 0 &
# ref_means_pct + abs(1 - ratio) * ref_means_pct > (ref_means_pct + ci) * -brk,
!means & !neg # brk >= 0 #& brk <= 1 # & brksup > 0 & brksup <= 1
~ diff >= 0 & abs(diff) - ci > brk, # & abs(diff) - ci < brksup,
!means & neg # brk <= 0 # & brksup < 0
~ diff < 0 & abs(diff) - ci > -brk, # & abs(diff) - ci < -brksup
),
"contrib" = if (!neg) { #if (brk >= 0) {
ctr >= brk * mean_ctr #& ctr < brksup * mean_ctr
} else {
ctr <= brk * mean_ctr #& ctr > brksup * mean_ctr
},
rep(FALSE, length(diff))
)
tidyr::replace_na(res, FALSE)
}
#' @keywords internal
tab_color_legend <- function(x, colored = TRUE, mode = c("console", "html"),
html_theme = NULL, html_type = NULL, html_24_bit,
text_color = NULL, grey_color = NULL,
add_color_and_diff_types = FALSE, all_variables_names = FALSE) {
color <- get_color(x)
x <- x[!is.na(color) & !color %in% c("no", "")]
color <- get_color(x)
type <- get_type(x)
ref <- get_ref_type(x)
col_vars_levels <- tab_get_vars(x)$col_vars_levels %>%
purrr::discard(names(.) == "all_col_vars")
color_type <- col_vars_levels %>%
purrr::map(~ get_color_type(color[.], type[.]) %>%
purrr::flatten_chr()) %>%
purrr::map_chr(~ dplyr::if_else(
condition = length(unique(.x)) == 1,
true = .x[1],
false = "mixed" )
)
ref <- col_vars_levels %>%
purrr::map(~ ref[.]) %>%
purrr::map_chr(~ dplyr::if_else(
condition = length(unique(.x)) == 1,
true = .x[1],
false = "mixed" )
)
if (all(is.na(color_type) | color_type %in% c("", "no"))) return(NULL)
breaks_with_op <- function(breaks, color_type) {
num_breaks <- breaks |> stringr::str_remove("\\+|\\*")
num_breaks <- dplyr::if_else(stringr::str_detect(num_breaks, "%$"),
true = as.double(stringr::str_remove(num_breaks, "%$")) / 100,
false = as.double(stringr::str_remove(num_breaks, "%$")) )
purrr::map2_chr(
breaks,
num_breaks,
~ switch(
color_type,
"diff_mean" = ,
"diff_ci_mean" = dplyr::if_else(
condition = stringr::str_detect(.x, "^-"),
true = paste0("/", stringr::str_remove(.x, "^-")),
false = paste0(cross, .x) # sign * in cross
),
"diff" = ,
"diff_ci" = ,
"after_ci" = dplyr::if_else(
condition = stringr::str_detect(.x, "^-"),
true = .x,
false = dplyr::if_else(.y > 1, paste0(cross, .y), paste0("+", .x))
),
"after_ci_mean" = paste0(cross, stringr::str_remove(.x, "^-")),
"contrib" = paste0(cross, stringr::str_remove(.x, "^-")),
#"ci_mean" = ,
"ci" = "", #just 1 ?
"or" = ,
"OR" = dplyr::if_else(
condition = stringr::str_detect(.x, "^-"),
true = paste0("1/", stringr::str_remove(.x, "^-")),
false = .x
),
.x
) )
}
color_formula_chr <- function(color_type, ref, sign, breaks, mode = "console") {
if (mode == "console") {
purrr::map2_chr(breaks, sign, function(.breaks, .sign)
switch(
color_type,
"diff_mean" = , # 1/mean and sign /
"diff" = paste0("x", .sign, ref, " ", .breaks),
"diff_ci_mean" = ,
"diff_ci" = paste0("|x-", ref, "|>ci & x", .sign,
ref, " ", .breaks),
#"ci_mean" = ,
"ci" = paste0("|x-", ref, "| > ci"), #just 1 ?
"after_ci_mean" = paste0(ref, " + |x-", ref, "| > (", ref, " + ci) ", .breaks),
"after_ci" = paste0("|x-", ref, "| > ci ", .breaks), #+ -
"contrib" = paste0("contrib > mean_ctr " , .breaks),
"or" = ,
"OR" = paste0("OR", .sign, .breaks),
character()
))
} else { # mode == "html"
purrr::map2_chr(breaks, sign, function(.breaks, .sign)
switch(
color_type,
"diff_mean" = , # 1/mean and sign /
"diff" = paste0("x", .sign, ref, " ", "<b>", .breaks, "</b>"),
"diff_ci_mean" = ,
"diff_ci" = paste0("|x-", ref, "|>ci & x", .sign,
ref, " ","<b>", .breaks, "</b>"),
#"ci_mean" = ,
"ci" = paste0("|x-", ref, "| > ci"), #just 1 ?
"after_ci_mean" = paste0(ref, " + |x-", ref, "| > (", ref, " + ci) ", "<b>",
.breaks, "</b>"),
"after_ci" = paste0("|x-", ref, "| > ci ", "<b>", .breaks, "</b>"), #+ -
"contrib" = paste0("contrib > mean_ctr ", "<b>", .breaks, "</b>"),
"or" = ,
"OR" = paste0("OR", .sign, "<b>", .breaks, "</b>"),
character()
))
}
}
color_table <-
tibble::tibble(color_type, ref, names = names(color_type)) %>%
dplyr::filter(!is.na(.data$color_type) & !.data$color_type %in% c("no", "")) %>%
dplyr::group_by(.data$color_type)
if (all_variables_names == FALSE) {
color_table <- color_table |>
dplyr::mutate(etc = dplyr::if_else(dplyr::row_number() >= 3, ",...", "") ) %>%
dplyr::slice(1:3)
} else {
color_table <- color_table |> dplyr::mutate(etc = "")
}
color_table <- color_table |>
dplyr::summarise(names = paste0(.data$names, collapse = ", "),
etc = dplyr::first(.data$etc),
ref = dplyr::first(.data$ref),
.groups = "drop") %>%
dplyr::mutate(names = paste0(.data$names, .data$etc))
if (add_color_and_diff_types) {
color_table <- color_table |>
dplyr::mutate(names = paste0(
"[color:", .data$color_type, "] ",
dplyr::if_else(color_type %in% c("diff_mean", "diff", "OR", "or", "diff_ci_mean",
"diff_ci", "after_ci_mean", "after_ci"),
true = paste0("[diff:", .data$ref, "] "),
false = ""),
.data$names
))
}
if (colored == TRUE) color_table <- color_table %>%
dplyr::mutate(names = stringr::str_pad(.data$names,
max(stringr::str_length(.data$names)),
side = "right"))
color_table <- color_table %>%
dplyr::mutate(
breaks = brk_from_color(.data$color_type),
breaks = dplyr::if_else(.data$color_type %in% c("diff_mean", "diff_ci_mean", "OR", "or"),
true = purrr::map(.data$breaks,
~ .[1:(length(.)/2)] %>%
c(., purrr::map(., `-`))
),
false = .data$breaks),
breaks = dplyr::if_else(
condition = .data$color_type %in% c("diff", "diff_ci", "after_ci"),
true = purrr::map(.data$breaks,
~ paste0(sprintf("%1.0f",
purrr::map_dbl(., ~ .[1]) * 100),
"%") ),
false = purrr::map(.data$breaks,
~ sprintf("%1.3g", purrr::map_dbl(., ~ .[1]))),
) %>%
purrr::map2(.data$color_type, ~ breaks_with_op(.x, .y)),
ref = purrr::map_chr(.data$ref, ~ if (is.na(suppressWarnings(as.integer(.)))) {
switch(., "first" = "x1", "tot" = "tot", .) # error : . replaced .data$ref (error length 3 but need 1)
} else {paste0("x", as.integer(.))} ),
sign = purrr::map(.data$breaks, ~ 1:length(.)) %>%
purrr::map(~ dplyr::if_else(condition = . >= max(.)/2 +1,
true = " < ",
false = " > ")),
)
if (colored == TRUE & mode[1] == "console") color_table <- color_table %>%
dplyr::mutate(
styles = purrr::map2(.data$breaks, .data$color_type,
~ suppressWarnings(select_in_color_style(
.x,
pct_diff = .y %in% c("diff", "diff_ci", "after_ci")
))),
styles = purrr::map(.data$styles, ~ get_color_style()[.]),
breaks = purrr::map2(.data$styles, .data$breaks,
~ purrr::map2_chr(
.x, .y,
~ rlang::exec(.x, rlang::sym(.y))
))
)
# color_table %>%
# dplyr::mutate(
# styles = purrr::map2(.data$breaks, .data$color_type,
# ~ select_in_color_style(
# .x,
# pct_diff = .y %in% c("diff", "diff_ci", "after_ci")
# ))# ,
# # styles = purrr::map(.data$styles, ~ get_color_style()[.]),
# # breaks = purrr::map2(.data$styles, .data$breaks,
# # ~ purrr::map2_chr(
# # .x, .y,
# # ~ rlang::exec(.x, rlang::sym(.y))
# # ))
# ) #|>
# breaks <- color_table |> tidyr::unnest(c(breaks, sign)) |> dplyr::pull(breaks)
# pct_diff <- FALSE
if (colored == TRUE & mode[1] == "html") {
if (html_type == "text") {
color_table <- color_table %>%
dplyr::mutate(
styles = purrr::map2(.data$breaks, .data$color_type,
~ select_in_color_style(
.x,
pct_diff = .y %in% c("diff", "diff_ci", "after_ci")
)),
styles = purrr::map(.data$styles, ~ get_color_style(mode = "color_code",
theme = html_theme,
type = html_type,
html_24_bit = html_24_bit)[.]),
breaks = purrr::map2(.data$styles, .data$breaks,
~ purrr::map2_chr(
.x, .y,
~ kableExtra::text_spec(.y, color = .x)
))
)
} else {
color_table <- color_table %>%
dplyr::mutate(
styles = purrr::map2(.data$breaks, .data$color_type,
~ select_in_color_style(
.x,
pct_diff = .y %in% c("diff", "diff_ci", "after_ci")
)),
styles = purrr::map(.data$styles, ~ get_color_style(mode = "color_code",
theme = html_theme,
type = html_type)[.]),
breaks = purrr::map2(.data$styles, .data$breaks,
~ purrr::map2_chr(
.x, .y,
~ kableExtra::text_spec(.y, background = .x)
))
)
}
}
color_table <- color_table %>%
dplyr::mutate(
color_scale = list(.data$color_type, .data$ref, .data$sign, .data$breaks,
purrr::map(.data$breaks, ~ 1:length(.))) %>%
purrr::pmap(~ dplyr::if_else(
condition = ..5 %in% c(1, max(..5)/2 + 1),
true = color_formula_chr(color_type = ..1, ref = ..2,
sign = ..3, breaks = ..4, mode = mode[1]),
false = if (mode[1] == "console") { ..4 } else { paste0("<b>", ..4, "</b>")}
))
)
if (colored == TRUE) {
if (mode[1] == "console") {
color_table <- color_table %>%
dplyr::mutate(
color_scale = purrr::map_chr(.data$color_scale, ~ paste0(
.,
collapse = pillar::style_subtle("; ") )
),
names = pillar::style_subtle(paste0(.data$names, ": "))
)
} else {
color_table <- color_table %>%
dplyr::mutate(
color_scale = purrr::map_chr(.data$color_scale, ~ paste0(., collapse = "; ")
), #%>% stringr::str_replace_all(";", kableExtra::text_spec(";", color = grey_color)),
names = paste0(.data$names, ": ") %>% kableExtra::text_spec(color = grey_color)
)
}
} else {
color_table <- color_table %>%
dplyr::mutate(
color_scale = purrr::map_chr(.data$color_scale, ~ paste0(., collapse = "; " )),
names = paste0(.data$names, ": ")
)
}
paste0(color_table$names, color_table$color_scale)
# %>% cli::cat_line()
# stringr::str_remove("\\+0.0+")
}
# tab_color_legend(tabs[[7]]) %>% cli::cat_line()
# tab_color_legend(tabs[[7]], colored = FALSE)
#' @keywords internal
brk_from_color <- function(color_type) {
tabxplor_color_breaks <- getOption("tabxplor.color_breaks")
purrr::map(color_type, ~
switch(.x,
"or" = ,
"OR" = ,
"diff_mean" = ,
"diff_ci_mean" = list(tabxplor_color_breaks$mean_breaks #,
#tabxplor_color_breaks$mean_brksup
),
"after_ci_mean" = list(tabxplor_color_breaks$mean_ci_breaks #,
#tabxplor_color_breaks$mean_ci_brksup
),
"diff" = ,
"diff_ci" = list(tabxplor_color_breaks$pct_breaks #,
#tabxplor_color_breaks$pct_brksup
),
"after_ci" = list(tabxplor_color_breaks$pct_ci_breaks #,
#tabxplor_color_breaks$pct_ci_brksup
),
"contrib" = list(tabxplor_color_breaks$contrib_breaks #,
#tabxplor_color_breaks$contrib_brksup
),
"ci" = ,
"ci_mean" = list(0 # , Inf
), #list(c(0, 0), c(Inf, -Inf)),
list()
) %>%
purrr::transpose() %>%
purrr::map(purrr::flatten_dbl)
)
}
#' @keywords internal
get_color_type <- function(color, type) {
purrr::map2(color, type, ~ dplyr::case_when(
.x == "contrib" ~ "contrib",
.x == "ci" ~ "ci" ,
.x %in% c("OR", "or") ~ "OR" ,
.x %in% c("diff", "diff_ci", "after_ci") & .y == "mean"
~ paste0(.x, "_mean"),
.x %in% c("diff", "diff_ci", "after_ci") &
.y %in% c("row", "col", "all", "all_tabs")
~ .x
) %>% purrr::set_names(names(.x)))
}
#' @keywords internal
select_in_color_style <- function(breaks, pct_diff) {
breaks <- breaks |>
stringr::str_remove(paste0("\\+|\\*|", cross)) |>
stringr::str_replace_all("/", "-")
breaks <- dplyr::if_else(stringr::str_detect(breaks, "%$"),
true = as.double(stringr::str_remove(breaks, "%$")) / 100,
false = as.double(stringr::str_remove(breaks, "%$")) )
pct_x2 <- (pct_diff & breaks > 1) |> which()
if (length(pct_x2) >= 2) stop("cannot have more than one pct_breaks > 1 rule (like *2)")
length <- dplyr::if_else(
length(pct_x2) > 0,
true = length(breaks[-pct_x2]),
false = length(breaks)
)
color_code_pos1 <- get_color_style()$pos1 |> attr("_styles") |> names()
# ratio <- if (length(pct_x2) > 0) {6} else {double()}
#length <- if (length(pct_x2) > 0) {length - 1L} else {length}
res <- if (stringr::str_detect(color_code_pos1, "#CCFFCC|#000033e")) {
switch(as.character(length),
"1" = c(3) ,
"2" = c(3, 8) ,
"4" = c(1, 3, 6, 8) ,
"6" = c(1, 3, 5, 6, 8, 10),
"8" = c(1:3, 4, 6:8, 10),
"10" = 1:10 )
} else {
switch(as.character(length),
"1" = c(3) ,
"2" = c(3, 8) ,
"4" = c(3, 5, 8, 10) ,
"6" = c(3:5, 8:10) ,
"8" = c(2:5, 7:10) ,
"10" = 1:10 )
}
if (length(pct_x2) > 0) {
res <- c(res[1:(pct_x2 - 1)],
11, # get_color_style()["ratio"] : in 11th place
res[pct_x2:length(res)]
)
}
return(res)
}
# brk_from_color <- function(color, type) {
# means <- type == "mean"
# purrr::map2(color, means,
# ~ switch(
# .x,
# "diff" = ,
# "diff_ci" = if (.y) {
# list(mean_breaks, mean_brksup)
# } else {
# list(pct_breaks, pct_brksup)
# },
# "after_ci" = if (.y) {
# list(mean_ci_breaks, mean_ci_brksup)
# } else {
# list(pct_ci_breaks, pct_ci_brksup)
# },
# "contrib" = list(contrib_breaks, contrib_brksup),
# list()
# ) %>%
# purrr::transpose() %>%
# purrr::map(purrr::flatten_chr)
# )
# }
#' @keywords internal
get_reference <- function(x, mode = c("cells", "lines", "all_totals")) {
type <- get_type(x)
ref <- get_ref_type(x)
comp_all <- get_comp_all(x)
totcol <- is_totcol(x)
totrows <- is_totrow(x)
tottab_line <- is_tottab(x) & totrows
refrows <- is_refrow(x)
refcol <- is_refcol(x)
tottab_ref <- is_tottab(x) & refrows
color <- get_color(x)
if (color %in% c("OR", "or")) {
switch(mode[1],
"cells" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ refrows ,
type %in% c("row", "mean") & comp_all ~ tottab_ref ,
type == "col" ~ rep(refcol, length(x)),
TRUE ~ rep(FALSE, length(x) )
),
"lines" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ refrows ,
type %in% c("row", "mean") & comp_all ~ tottab_ref ,
type == "col" ~ rep(refcol, length(x)),
TRUE ~ rep(FALSE, length(x) )
),
"all_totals" = dplyr::case_when(
type %in% c("row", "mean") & ref == "tot" & !comp_all
~ totrows | refcol,
type %in% c("row", "mean") & ref == "tot" & comp_all
~ tottab_line | refcol,
type == "col" & ref == "tot" ~ totrows | refcol,
type %in% c("row", "mean") & !comp_all ~ refrows | refcol ,
type %in% c("row", "mean") & comp_all ~ tottab_ref | refcol ,
type == "col" ~ refrows | refcol ,
TRUE ~ rep(FALSE, length(x) )
)
)
} else if (ref == "tot") {
switch(mode[1],
"cells" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ totrows & !totcol ,
type %in% c("row", "mean") & comp_all ~ tottab_line & !totcol ,
type == "col" ~ totcol & !totrows ,
type == "all" ~ totrows & totcol ,
type == "all_tabs" ~ tottab_line & totcol ,
TRUE ~ rep(FALSE, length(x) )
),
"lines" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ totrows ,
type %in% c("row", "mean") & comp_all ~ tottab_line ,
type == "col" ~ rep(totcol, length(x)),
type == "all" ~ totrows & totcol ,
type == "all_tabs" ~ tottab_line & totcol ,
TRUE ~ rep(FALSE, length(x) )
),
"all_totals" = dplyr::case_when(
type %in% c("n", "col", "all") |
(type %in% c("row", "mean") & !comp_all)
~ totrows | totcol,
type == "all_tabs" | (type %in% c("row", "mean") & comp_all)
~ tottab_line | totcol,
# type == "col" ~ rep(totcol, length(x)),
# type == "all" ~ totrows & totcol ,
# type == "all_tabs" ~ tottab_line & totcol ,
TRUE ~ rep(FALSE, length(x) )
)
)
} else {
switch(mode[1],
"cells" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ refrows & !totcol ,
type %in% c("row", "mean") & comp_all ~ tottab_ref & !totcol ,
type == "col" ~ refcol & !totrows ,
TRUE ~ rep(FALSE, length(x) )
),
"lines" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ refrows ,
type %in% c("row", "mean") & comp_all ~ tottab_ref ,
type == "col" ~ rep(refcol, length(x)),
TRUE ~ rep(FALSE, length(x) )
),
"all_totals" = dplyr::case_when(
type %in% c("row", "mean") & !comp_all ~ refrows | totcol ,
type %in% c("row", "mean") & comp_all ~ tottab_ref | totcol ,
type == "col" ~ totrows | refcol ,
TRUE ~ rep(FALSE, length(x) )
)
)
}
}
# is_RStudio <- function() Sys.getenv("RSTUDIO") == "1"
# #.Platform$GUI == "RStudio"
#
# is_dark <- ifelse(is_RStudio(), rstudioapi::getThemeInfo()$dark, FALSE)
# format.pillar_shaft_fmt <- function(x, width, ...) {
# if (get_max_extent(x$deg_min) <= width) {
# ornament <- x$deg_min
# } else {
# ornament <- x$deg
# }
#
# pillar::new_ornament(ornament, align = "right")
# }
#' Abbreviated display name for class fmt in tibbles
#' @param x A fmt object.
#' @param ... Other parameter.
#' @return A single string with abbreviated fmt type.
#' @export
vec_ptype_abbr.tabxplor_fmt <- function(x, ...) {
display <- get_display(x) %>% unique()
if (identical(sort(display), c("pct", "pvalue"))) display <- "pct"
display <- ifelse(length(display) > 1, "mixed", display)
type <- get_type(x)
row_mean <- type %in% c("row", "mean")
if (type %in% c("row", "col", "all", "all_tabs")) type <- paste0(type, "%")
ci <- get_ci_type(x)
if (display == "ci" & ci %in% c("cell", "diff")) display <- paste0("ci_", ci)
out <- paste0(type, "-", display) %>%
stringr::str_replace("^n-n", "n") %>%
stringr::str_replace("^mean-mean", "mean") %>%
stringr::str_replace("^mixed-mixed", "mixed") %>%
stringr::str_replace("([^%]+%)-pct", "\\1") %>%
stringr::str_remove("^NA") %>%
stringr::str_remove("_ci$")
#if (get_comp_all(x)) out <- paste0(out, "-all")
out
}
#' Printed type for class fmt
#' @param x A fmt object.
#' @param ... Other parameter.
#' @return A single string with full fmt type.
#' @export
vec_ptype_full.tabxplor_fmt <- function(x, ...) {
display <- get_display(x) %>% unique()
display <- ifelse(length(display) > 1, "mixed", display)
type <- get_type(x)
row_mean <- type %in% c("row", "mean")
if (type %in% c("row", "col", "all", "all_tabs")) type <- paste0(type, "%")
ci <- get_ci_type(x)
if (display == "ci" & ci %in% c("cell", "diff")) display <- paste0("ci_", ci)
out <- paste0("fmt-", type, "-", display) %>%
stringr::str_replace("-n-n", "-n") %>%
stringr::str_replace("-mean-mean", "-mean") %>%
stringr::str_replace("-mixed-mixed", "-mixed") %>%
stringr::str_replace("([^%]+%)-pct", "\\1") %>%
stringr::str_remove("-NA") %>%
stringr::str_remove("_ci$")
#if (get_comp_all(x)) out <- paste0(out, "-all")
out
}
# x <- fmt(7, "row", pct = 0.6)
# x %>% vec_data()
# x %>% attributes()
#Coertion and convertion methods for formatted numbers -------------------------
#Make our tabxplor_fmt class coercible with herself, and back and forth with double and
# integer vectors :
#' Find common ptype between fmt and fmt
#' @param x A fmt object.
#' @param y A fmt object.
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_ptype2.tabxplor_fmt.tabxplor_fmt <- function(x, y, ...) {
type_x <- get_type(x)
same_type <- type_x == get_type(y)
comp_x <- get_comp_all(x, replace_na = FALSE)
comp_y <- get_comp_all(y, replace_na = FALSE)
same_comp <- comp_x == comp_y | (is.na(comp_x) & is.na(comp_y))
diff_type_x <- get_ref_type(x)
same_diff_type <- diff_type_x == get_ref_type(y)
ci_type_x <- get_ci_type(x)
same_ci_type <- ci_type_x == get_ci_type(y)
col_var_x <- get_col_var(x)
same_col_var <- col_var_x == get_col_var(y)
totcol_x <- is_totcol(x)
same_totcol <- totcol_x == is_totcol(y)
refcol_x <- is_refcol(x)
same_refcol <- refcol_x == is_refcol(y)
color_x <- get_color(x)
same_color <- color_x == get_color(y)
#l <- length(x)
new_fmt(
type = dplyr::if_else(same_type , type_x , "mixed" ),
comp_all = dplyr::if_else(same_comp , comp_x , FALSE ),
ref= dplyr::if_else(same_diff_type, diff_type_x, "" ),
ci_type = dplyr::if_else(same_ci_type, ci_type_x, "" ),
col_var = dplyr::if_else(same_col_var, col_var_x, "several_vars"),
totcol = dplyr::if_else(same_totcol , totcol_x , FALSE ),
refcol = dplyr::if_else(same_refcol , refcol_x , FALSE ),
color = dplyr::if_else(same_color , color_x , "" )
)
# new_fmt(
# type = dplyr::if_else(same_type, true = type_x,
# false = "mixed"),
# comp_all = dplyr::if_else(same_comp,
# true = comp_x,
# false = vctrs::vec_recycle(FALSE, l )),
# ci_type = dplyr::if_else(same_ci_type,
# true = ci_type_x,
# false = vctrs::vec_recycle(NA_character_, l )),
# col_var = dplyr::if_else(same_col_var,
# true = col_var_x,
# false = vctrs::vec_recycle("several_vars", l )),
# totcol = dplyr::if_else(same_totcol,
# true = totcol_x,
# false = vctrs::vec_recycle(FALSE, l )),
# color = dplyr::if_else(same_color,
# true = color_x,
# false = vctrs::vec_recycle(NA_character_, l))
# )
}
#' Find common ptype between fmt and double
#' @param x A fmt vector
#' @param y A double vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_ptype2.tabxplor_fmt.double <- function(x, y, ...) x # new_fmt() #double()
#' Find common ptype between double and fmt
#' @param x A double vector
#' @param y A fmt vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_ptype2.double.tabxplor_fmt <- function(x, y, ...) y # new_fmt() #double()
#' Find common ptype between fmt and integer
#' @param x A fmt vector
#' @param y An integer vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_ptype2.tabxplor_fmt.integer <- function(x, y, ...) x # fmt() #double()
#' Find common ptype between integer and fmt
#' @param x An integer vector
#' @param y A fmt vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_ptype2.integer.tabxplor_fmt <- function(x, y, ...) y # new_fmt() #double()
# Conversions :
#' Convert fmt into fmt
#' @param x A fmt vector
#' @param to A fmt vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_cast.tabxplor_fmt.tabxplor_fmt <- function(x, to, ...)
new_fmt(display = get_display (x),
n = get_n (x),
wn = get_wn (x),
pct = get_pct (x),
diff = get_diff (x),
digits = get_digits (x),
ctr = get_ctr (x),
mean = get_mean (x),
var = get_var (x),
ci = get_ci (x),
rr = get_rr (x),
or = get_or (x),
in_totrow = is_totrow (x),
in_refrow = is_refrow (x),
in_tottab = is_tottab (x),
type = get_type (to),
comp_all = get_comp_all(to, replace_na = FALSE),
ref = get_ref_type(to),
ci_type = get_ci_type (to),
col_var = get_col_var (to),
totcol = is_totcol (to),
refcol = is_refcol (to),
color = get_color (to)
)
#' Convert double into fmt
#' @param x A double vector
#' @param to A fmt vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_cast.tabxplor_fmt.double <- function(x, to, ...)
fmt(n = NA_integer_ ,
display = "wn", wn = x ,
type = get_type (to),
comp_all = get_comp_all(to, replace_na = FALSE),
ref = get_ref_type(to),
ci_type = get_ci_type (to),
col_var = get_col_var (to),
totcol = is_totcol (to),
refcol = is_refcol (to),
color = get_color (to),
)
#' Convert fmt into double
#' @param x A fmt vector
#' @param to A double vector
#' @param ... Other parameter.
#' @return A double vector
#' @method vec_cast.double tabxplor_fmt
#' @export
vec_cast.double.tabxplor_fmt <- function(x, to, ...) get_num(x) %>% as.double() #vctrs::field(x, "pct")
#' Convert integer into fmt
#' @param x A integer vector
#' @param to A fmt vector
#' @param ... Other parameter.
#' @return A fmt vector
#' @export
vec_cast.tabxplor_fmt.integer <- function(x, to, ...)
fmt(n = x ,
type = get_type (to),
comp_all = get_comp_all(to, replace_na = FALSE),
ref = get_ref_type(to),
ci_type = get_ci_type (to),
col_var = get_col_var (to),
totcol = is_totcol (to),
refcol = is_refcol (to),
color = get_color (to)
) #new_fmt(pct = as.double(x))
#' Convert fmt into integer
#' @param x A integer vector
#' @param to A fmt vector
#' @param ... Other parameter.
#' @return An integer vector
#' @method vec_cast.integer tabxplor_fmt
#' @export
vec_cast.integer.tabxplor_fmt <- function(x, to, ...) get_num(x) %>% as.integer() #vctrs::field(x, "pct") %>% as.integer()
#' Convert fmt into character
#' @param x A fmt vector
#' @param to A character vector
#' @param ... Other parameter
#' @return A character vector
#' @method vec_cast.character tabxplor_fmt
#' @export
vec_cast.character.tabxplor_fmt <- function(x, to, ...) format(x)
#Comparisons and sorting :
#' Test equality with fmt vector
#' @param x A fmt vector
#' @param ... Other parameter
#' @return A double vector
#' @export
vec_proxy_equal.tabxplor_fmt <- function(x, ...) {
get_num(x)
}
#' Compare with fmt vector
#' @param x A fmt vector
#' @param ... Other parameter
#' @return A double vector
#' @export
vec_proxy_compare.tabxplor_fmt <- function(x, ...) {
get_num(x)
}
#Once you've implemented vec_ptype2() and vctrs::vec_cast(), you get vec_c(), [<-, and [[<- implementations for free.
#You'll also get mostly correct behaviour for c().
#Arithmetic operations :
# Thank you very much it works perfectly (I had tried with ```@method```, but not consistently enougth to put it in the generic) !
# Just a detail : with ```vec_arith tabxplor_fmt default``` , I have a "Warning: [D:\... ] @method can have at most 2 words"
# I replaced with ```vec_arith.tabxplor_fmt default``` and it worked.
#' Vec_arith method for fmt
#' @param op Operation to do.
#'
#' @param x fmt object.
#' @param y Second object.
#' @param ... Other parameter.
#'
#' @return A fmt vector
#' @method vec_arith tabxplor_fmt
#' @export
vec_arith.tabxplor_fmt <- function(op, x, y, ...) {
UseMethod("vec_arith.tabxplor_fmt", y)
}
#' @describeIn vec_arith.tabxplor_fmt default vec_arith method for fmt
#' @return A fmt vector
#' @method vec_arith.tabxplor_fmt default
#' @export
vec_arith.tabxplor_fmt.default <- function(op, x, y, ...) {
vctrs::vec_arith_base(op, get_num(x), vctrs::vec_data(y))
#stop_incompatible_op(op, x, y)
}
# positive_double <- function(n) n * sign(n)
# positive_integer <- function(n) as.integer(n * sign(n))
#' @describeIn vec_arith.tabxplor_fmt vec_arith method for fmt + fmt
#' @return A fmt vector
#' @method vec_arith.tabxplor_fmt tabxplor_fmt
#' @export
vec_arith.tabxplor_fmt.tabxplor_fmt <- function(op, x, y, ...) {
type_x <- get_type(x)
same_type <- type_x == get_type(y)
comp_x <- get_comp_all(x, replace_na = FALSE)
comp_y <- get_comp_all(y, replace_na = FALSE)
same_comp <- comp_x == comp_y | (is.na(comp_x) & is.na(comp_y))
diff_type_x <- get_ref_type(x)
same_diff_type <- diff_type_x == get_ref_type(y)
ci_type_x <- get_ci_type(x)
same_ci_type <- ci_type_x == get_ci_type(y)
col_var_x <- get_col_var(x)
same_col_var <- col_var_x == get_col_var(y)
l <- length(x)
rep_NA_real <- rep(NA_real_, l)
if (!same_type) warning("operation ", op,
" over columns with different pct types, ",
"or mixing pct and means (",
type_x, "/", get_type(y), ")")
if (!same_comp) warning("operation ", op,
" may mix calculations made on tabs and calculations ",
"made on all tabs (different 'comp_all')")
if (!same_col_var) warning("operation ", op,
" over columns belonging to different variables(",
col_var_x , "/", get_col_var(y), ")")
switch(
op,
"+" = ,
"-" = new_fmt(
display = get_display(x), #dplyr::if_else(get_display(x) == get_display(x)), true = get_display(x), false = "n),
n = vctrs::vec_arith_base(op, get_n(x) , get_n(y) ), #%>% positive_integer(),
wn = vctrs::vec_arith_base(op, get_wn(x) , get_wn(y) ), #%>% positive_double(),
pct = if (same_type & !type_x %in% c("col", "mean", "n") ) {
tidyr::replace_na(vctrs::vec_arith_base(op, get_pct(x), get_pct(y)), NA_real_)
} else {
rep_NA_real
},
diff = rep_NA_real,
digits = pmax(get_digits(x), get_digits(y)),
ctr = rep_NA_real, # ???
mean = vctrs::vec_arith_base(op, get_mean(x) * get_wn(x), get_mean(y) * get_wn(y)) /
vctrs::vec_arith_base("+", get_wn(x) , get_wn(y) ),# weighted mean
var = rep_NA_real,
ci = rep_NA_real,
rr = rep_NA_real,
or = rep_NA_real,
in_totrow = is_totrow(x) & is_totrow(y), # Just x ?
in_refrow = is_refrow(x) & is_refrow(y),
in_tottab = is_tottab(x) & is_tottab(y),
type = dplyr::if_else(same_type , type_x , "mixed" ),
comp_all = dplyr::if_else(same_comp , comp_x , FALSE ),
ref= dplyr::if_else(same_diff_type, diff_type_x, "" ),
ci_type = dplyr::if_else(same_ci_type, ci_type_x, "" ),
col_var = dplyr::if_else(same_col_var, col_var_x, "several_vars"),
totcol = FALSE ,
refcol = FALSE ,
color = get_color(x)
# type = dplyr::if_else(same_type,
# true = type_x,
# false = vctrs::vec_recycle("mixed", l )),
# comp_all = dplyr::if_else(same_comp,
# true = comp_x,
# false = vctrs::vec_recycle(FALSE, l )),
# ci_type = dplyr::if_else(same_ci_type,
# true = ci_type_x,
# false = vctrs::vec_recycle(NA_character_, l )),
# col_var = dplyr::if_else(same_col_var,
# true = col_var_x,
# false = vctrs::vec_recycle("several_vars", l )),
),
"/" = ,
"*" = new_fmt(
display = get_display(x),
n = get_n(x) ,
wn = get_wn(x) ,
pct = vctrs::vec_arith_base(op, get_pct(x), get_pct(y)), #Remove multiplication ?
diff = rep_NA_real,
digits = pmax(get_digits(x), get_digits(y)),
ctr = rep_NA_real,
mean = rep_NA_real,
var = rep_NA_real,
ci = rep_NA_real,
rr = rep_NA_real,
or = rep_NA_real,
in_totrow = is_totrow(x),
in_refrow = is_refrow(x),
in_tottab = is_tottab(x),
type = dplyr::if_else(same_type , type_x , "mixed" ),
comp_all = dplyr::if_else(same_comp , comp_x , FALSE ),
ref= dplyr::if_else(same_diff_type, diff_type_x, "" ),
ci_type = dplyr::if_else(same_ci_type, ci_type_x, "" ),
col_var = dplyr::if_else(same_col_var, col_var_x, "several_vars"),
totcol = FALSE ,
refcol = FALSE ,
color = get_color(x)
# type = dplyr::if_else(same_type,
# true = type_x,
# false = vctrs::vec_recycle("mixed", l )),
# comp_all = dplyr::if_else(same_comp,
# true = comp_x,
# false = vctrs::vec_recycle(FALSE, l )),
# ci_type = dplyr::if_else(same_ci_type,
# true = ci_type_x,
# false = vctrs::vec_recycle(NA_character_, l )),
# col_var = dplyr::if_else(same_col_var,
# true = col_var_x,
# false = vctrs::vec_recycle("several_vars", l )),
),
vctrs::stop_incompatible_op(op, x, y)
)
}
#' @describeIn vec_arith.tabxplor_fmt vec_arith method for fmt + numeric
#' @return A fmt vector
#' @method vec_arith.tabxplor_fmt numeric
#' @export
vec_arith.tabxplor_fmt.numeric <- function(op, x, y, ...) {
set_num(x, vctrs::vec_arith_base(op, get_num(x), y))
# new_fmt(pct = vec_arith_base(op, vctrs::field(x, "pct"), y),
# display = vctrs::field(x, "display" ),
# digits = vctrs::field(x, "digits"),
# n = vctrs::field(x, "n" ),
# wn = vctrs::field(x, "wn" ),
# var = vctrs::field(x, "var" ),
# ci = vctrs::field(x, "ci" ) )
}
#' @describeIn vec_arith.tabxplor_fmt vec_arith method for numeric + fmt
#' @return A fmt vector
#' @method vec_arith.numeric tabxplor_fmt
#' @export
vec_arith.numeric.tabxplor_fmt <- function(op, x, y, ...) {
set_num(y, vctrs::vec_arith_base(op, x, get_num(y)))
# new_fmt(pct = vec_arith_base(op, x, vctrs::field(y, "pct")),
# display = vctrs::field(y, "display" ),
# digits = vctrs::field(y, "digits"),
# n = vctrs::field(y, "n" ),
# wn = vctrs::field(y, "wn" ),
# var = vctrs::field(y, "var" ),
# ci = vctrs::field(y, "ci" ) )
}
#' @describeIn vec_arith.tabxplor_fmt vec_arith method for -fmt
#' @return A fmt vector
#' @method vec_arith.tabxplor_fmt MISSING
#' @export
vec_arith.tabxplor_fmt.MISSING <- function(op, x, y, ...) { #unary + and - operators
switch(op,
`-` = set_num(x, get_num(x) * -1),
# new_fmt(pct = vctrs::field(x, "pct" ) * -1,
# display = vctrs::field(x, "display" ),
# digits = vctrs::field(x, "digits"),
# n = vctrs::field(x, "n" ),
# wn = vctrs::field(x, "wn" ),
# var = vctrs::field(x, "var" ),
# ci = vctrs::field(x, "ci" ) ),
`+` = x,
vctrs::stop_incompatible_op(op, x, y)
)
}
#Mathematical operations :
# (direct operations on counts,
# automatically calculate weighted means for pct and means, erase var and ci)
#' Vec_math method for class fmt
#' @param .fn A function
#' @param .x A fmt object
#' @param ... Other parameter
#' @return A fmt vector
#' @export
vec_math.tabxplor_fmt <- function(.fn, .x, ...) {
if (!is.na(get_type(.x) ) & get_type(.x) == "mixed") warning(
"operation ", .fn,
" within a variable mixing different types of percentages"
)
switch(.fn,
"sum" = new_fmt(display = get_display(.x)[1],
digits = min(get_digits(.x)),
n = vctrs::vec_math_base(.fn, get_n(.x) , ...),
wn = vctrs::vec_math_base(.fn, get_wn(.x) , ...),
pct = ifelse(! get_type(.x) %in% c("row", "col"),
yes = vctrs::vec_math_base(.fn, get_pct(.x), ...),
no = NA_real_) %>%
tidyr::replace_na(NA_real_),
diff = NA_real_,
ctr = NA_real_,
mean = vctrs::vec_math_base("sum", get_mean(.x) * get_wn(.x), ...) /
vctrs:: vec_math_base("sum", get_wn(.x), ...),
var = NA_real_,
ci = NA_real_,
rr = NA_real_,
or = NA_real_,
in_totrow = all(is_totrow(.x)),
in_refrow = all(is_refrow(.x)),
in_tottab = all(is_tottab(.x)), #any ?
type = get_type (.x),
comp_all = get_comp_all(.x, replace_na = FALSE),
ref = get_ref_type(.x),
ci_type = get_ci_type (.x),
col_var = get_col_var (.x),
totcol = is_totcol (.x),
refcol = is_refcol (.x),
color = get_color (.x)
),
"mean" = new_fmt(display = get_display(.x)[1],
digits = max(get_digits(.x)),
n = vctrs::vec_math_base("sum", get_n(.x) , ...),
wn = vctrs::vec_math_base("sum", get_wn(.x) , ...),
pct = vctrs::vec_math_base("sum", get_pct(.x) * get_wn(.x), ...) /
vctrs::vec_math_base("sum", get_wn(.x), ...),
diff = NA_real_,
ctr = NA_real_,
mean = vctrs::vec_math_base("sum", get_mean(.x) * get_wn(.x), ...) /
vctrs::vec_math_base("sum", get_wn(.x), ...),
var = NA_real_,
ci = NA_real_,
rr = NA_real_,
or = NA_real_,
in_totrow = FALSE,
in_refrow = FALSE,
in_tottab = all(is_tottab(.x)), #any ?
type = get_type (.x),
comp_all = get_comp_all(.x, replace_na = FALSE),
ref = get_ref_type(.x),
ci_type = get_ci_type (.x),
col_var = get_col_var (.x),
totcol = is_totcol (.x),
refcol = is_refcol (.x),
color = get_color (.x)
),
vctrs::vec_math_base(.fn, get_num(.x), ...) )
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.