Nothing
#' @title Easily make nice APA tables
#'
#' @description Make nice APA tables easily through a wrapper
#' around the `flextable` package with sensical defaults and
#' automatic formatting features.
#'
#' @details The resulting `flextable` objects can be opened in
#' Word with `print(table, preview ="docx")`, or saved to
#' Word with the `flextable::save_as_docx()` function.
#'
#' @param data The data frame, to be converted to a flextable.
#' The data frame cannot have duplicate column names.
#' @param italics Which columns headers should be italic? Useful
#' for column names that should be italic but that are not picked
#' up automatically by the function. Select with numerical range, e.g., 1:3.
#' @param highlight Highlight rows with statistically significant
#' results? Requires a column named "p" containing p-values.
#' Can either accept logical (TRUE/FALSE) OR a numeric value for
#' a custom critical p-value threshold (e.g., 0.10 or 0.001).
#' @param stars Logical. Whether to add asterisks for significant p values.
#' @param col.format.p Applies p-value formatting to columns
#' that cannot be named "p" (for example for a data frame full
#' of p-values, also because it is not possible to have more
#' than one column named "p"). Select with numerical range, e.g., 1:3.
#' @param col.format.r Applies r-value formatting to columns
#' that cannot be named "r" (for example for a data frame full
#' of r-values, also because it is not possible to have more
#' than one column named "r"). Select with numerical range, e.g., 1:3.
#' @param col.format.ci Applies 95% confidence interval formatting
#' to selected columns (e.g., when reporting more than one interval).
#' @param format.custom Applies custom formatting to columns
#' selected via the `col.format.custom` argument. This is useful
#' if one wants custom formatting other than for p- or r-values.
#' It can also be used to transform (e.g., multiply) certain values
#' or print a specific symbol along the values for instance.
#' @param col.format.custom Which columns to apply the custom
#' function to. Select with numerical range, e.g., 1:3.
#' @param width Width of the table, in percentage of the
#' total width, when exported e.g., to Word. For full width,
#' use `width = 1`.
#' @param spacing Spacing of the rows (1 = single space, 2 = double space)
#' @param broom If providing a tidy table produced with the
#' `broom` package, which model type to use if one wants
#' automatic formatting (options are "t.test", "lm", "cor.test",
#' and "wilcox.test").
#' @param report If providing an object produced with the
#' `report` package, which model type to use if one wants
#' automatic formatting (options are "t.test", "lm", and "cor.test").
#' @param short Logical. Whether to return an abbreviated
#' version of the tables made by the `report` package.
#' @param title Optional, to add a table header, if desired.
#' @param note Optional, to add one or more table footnote (APA note),
#' if desired.
#' @param separate.header Logical, whether to separate headers based
#' on name delimiters (i.e., periods ".").
#'
#' @keywords APA style table
#' @return An APA-formatted table of class "flextable"
#' @examplesIf requireNamespace("flextable", quietly = TRUE) && requireNamespace("methods", quietly = TRUE)
#' # Make the basic table
#' my_table <- nice_table(
#' mtcars[1:3, ],
#' title = c("Table 1", "Motor Trend Car Road Tests"),
#' note = c(
#' "The data was extracted from the 1974 Motor Trend US magazine.",
#' "* p < .05, ** p < .01, *** p < .001"
#' )
#' )
#' my_table
#'
#' \donttest{
#' # Save table to word
#' mypath <- tempfile(fileext = ".docx")
#' flextable::save_as_docx(my_table, path = mypath)
#' }
#'
#' # Publication-ready tables
#' mtcars.std <- lapply(mtcars, scale)
#' model <- lm(mpg ~ cyl + wt * hp, mtcars.std)
#' stats.table <- as.data.frame(summary(model)$coefficients)
#' CI <- confint(model)
#' stats.table <- cbind(
#' row.names(stats.table),
#' stats.table, CI
#' )
#' names(stats.table) <- c(
#' "Term", "B", "SE", "t", "p",
#' "CI_lower", "CI_upper"
#' )
#' nice_table(stats.table, highlight = TRUE)
#'
#' # Test different column names
#' test <- head(mtcars)
#' names(test) <- c(
#' "dR", "N", "M", "SD", "b", "np2",
#' "ges", "p", "r", "R2", "sr2"
#' )
#' test[, 10:11] <- test[, 10:11] / 10
#' nice_table(test)
#'
#' # Custom cell formatting (such as p or r)
#' nice_table(test[8:11], col.format.p = 2:4, highlight = .001)
#'
#' nice_table(test[8:11], col.format.r = 1:4)
#'
#' # Apply custom functions to cells
#' fun <- function(x) {
#' x + 11.1
#' }
#' nice_table(test[8:11], col.format.custom = 2:4, format.custom = "fun")
#'
#' fun <- function(x) {
#' paste("x", x)
#' }
#' nice_table(test[8:11], col.format.custom = 2:4, format.custom = "fun")
#'
#' # Separate headers based on periods
#' header.data <- structure(
#' list(
#' Variable = c(
#' "Sepal.Length",
#' "Sepal.Width", "Petal.Length"
#' ), setosa.M = c(
#' 5.01, 3.43,
#' 1.46
#' ), setosa.SD = c(0.35, 0.38, 0.17), versicolor.M =
#' c(5.94, 2.77, 4.26), versicolor.SD = c(0.52, 0.31, 0.47)
#' ),
#' row.names = c(NA, -3L), class = "data.frame"
#' )
#' nice_table(header.data,
#' separate.header = TRUE,
#' italics = 2:4
#' )
#'
#' @importFrom dplyr mutate %>% select matches
#' case_when relocate across contains select_if any_of
#' last_col
#' @importFrom rlang :=
#'
#' @seealso
#' Tutorial: \url{https://rempsyc.remi-theriault.com/articles/table}
#'
#' @export
nice_table <- function(data,
highlight = FALSE,
stars = TRUE,
italics,
col.format.p,
col.format.r,
col.format.ci,
format.custom,
col.format.custom,
width = NULL,
spacing = 2,
broom = NULL,
report = NULL,
short = FALSE,
title,
note,
separate.header) {
rlang::check_installed(c("flextable", "methods"),
version = c(get_dep_version("flextable"), NA),
reason = "for this function."
)
if (!inherits(data, "data.frame")) {
message("Non-dataframe detected. Attempting to coerce to dataframe")
}
dataframe <- as.data.frame(data)
format_p_internal <- ifelse(isTRUE(stars), "format_p_stars", "format_p")
# _______________________________________
# Replace possible alternative names ####
dataframe <- dataframe %>%
rename_with(
~ case_when(
. == "p.value" ~ "p",
. == "pvalue" ~ "p",
. == "chisq" ~ "chi2",
. == "conf.low" ~ "CI_lower",
. == "conf.high" ~ "CI_upper",
. == "CI_low" ~ "CI_lower",
. == "CI_high" ~ "CI_upper",
. == "df_error" ~ "df",
. == "Cohens_d" ~ "d",
. == "Coefficient" ~ "b",
. == "t.ratio" ~ "t",
. == "Std_Coefficient" ~ "B",
. == "r_rank_biserial" ~ "rrb",
. == "Eta2" ~ "n2",
. == "Eta2_partial" ~ "np2",
. == "mu" ~ "Mu",
. == "term" ~ "Term",
. == "method" ~ "Method",
. == "alternative" ~ "Alternative",
TRUE ~ .
)
)
# __________________________________
# Broom integration ####
dataframe <- prepare_broom(dataframe, broom)
# __________________________________
# Report integration ####
dataframe <- prepare_report(dataframe, report, short)
# _________________________________
# Formatting ####
if (!missing(separate.header)) {
filtered.names <- grep("[.]", names(dataframe), value = TRUE)
sh.pattern <- lapply(filtered.names, function(x) {
gsub("[^\\.]*$", "", x)
}) %>%
unlist() %>%
unique()
unique.pattern <- length(sh.pattern)
}
dataframe <- prepare_flextable(
dataframe, separate.header, col.format.ci, highlight,
sh.pattern, unique.pattern
)
# __________________________________
# Flextable ####
nice.borders <- list("width" = 0.5, color = "black", style = "solid")
table <- create_flextable(
dataframe, highlight, width, spacing, note,
separate.header, nice.borders
)
# ___________________________________
# Column formatting ####
table <- format_columns(
dataframe, table, italics, separate.header,
highlight, sh.pattern, unique.pattern,
format_p_internal
)
# _____________________________________________
# Extra features ####
table <- beautify_flextable(
dataframe, table, separate.header, col.format.p, col.format.r,
format.custom, col.format.custom, sh.pattern, unique.pattern,
format_p_internal
)
# ___________________________
# Final touch up (title) ####
table <- finalize_table(table, title, nice.borders)
# class(table) <- c("nice_table", class(table))
# remove `nice_table` class because of name collision with printing method
# of the `afex` package for `nice_table` objects
table
}
# ____________________________________________________________________________
# Other functions ####
# format_CI
format_CI <- function(dataframe, CI_low_high = c("CI_lower", "CI_upper"),
col.name = "95% CI") {
dataframe %>%
mutate(across(all_of(CI_low_high), function(x) {
x %>%
as.numeric() %>%
round(2) %>%
formatC(2, format = "f")
})) %>%
mutate(!!col.name := paste0(
"[", .[[CI_low_high[1]]],
", ", .[[CI_low_high[2]]], "]"
)) %>%
select(-all_of(CI_low_high))
}
# format_flex
format_flex <- function(table, j, digits = 2, value, fun) {
if (missing(value)) {
table <- table %>%
flextable::italic(j = j, part = "header") %>%
flextable::colformat_double(j = j, big.mark = ",", digits = digits)
} else {
rExpression <- paste0("flextable::as_paragraph(", value, ")")
table <- table %>%
flextable::compose(
i = NULL, j = j, part = "header",
value = eval(parse(text = rExpression))
)
}
if (!missing(fun)) {
table <- table %>%
parse_formatter(column = j, fun = fun)
}
table
}
# parse_formatter
parse_formatter <- function(
table,
call = "table <- table %>% flextable::set_formatter",
column, fun) {
rExpression <- paste0(call, "(`", column, "` = ", fun, ")")
eval(parse(text = rExpression))
}
# prepare_broom
prepare_broom <- function(dataframe, broom) {
if (!is.null(broom)) {
dataframe <- dataframe %>%
rename_with(
~ case_when(
. == "statistic" ~ "t",
. == "std.error" ~ "SE",
. == "parameter" ~ "df",
TRUE ~ .
)
)
if (broom == "t.test") {
dataframe <- dataframe %>%
relocate(estimate, .after = estimate2) %>%
rename_with(
~ case_when(
. == "estimate" ~ "M1 - M2",
. == "estimate1" ~ "Mean 1",
. == "estimate2" ~ "Mean 2",
TRUE ~ .
)
) %>%
relocate(df, .before = p) %>%
relocate(Method:Alternative)
} else if (broom == "lm") {
dataframe <- dataframe %>%
rename_with(
~ case_when(
. == "estimate" ~ "b",
TRUE ~ .
)
)
} else if (broom == "cor.test") {
dataframe <- dataframe %>%
rename_with(
~ case_when(
. == "estimate" ~ "r",
TRUE ~ .
)
) %>%
relocate(df, .before = p) %>%
relocate(Method:Alternative, .before = r)
} else if (broom == "wilcox.test") {
dataframe <- dataframe %>%
rename_with(
~ case_when(
. == "t" ~ "W",
TRUE ~ .
)
) %>%
relocate(Method:Alternative, .before = W)
}
}
dataframe
}
# prepare_report
prepare_report <- function(dataframe, report, short) {
if (!is.null(report) || any(class(dataframe) == "report_table")) {
# t.test, aov, and wilcox need to be done separately
# because they have no model_class attribute
if ("Method" %in% names(dataframe)) {
if (all(grepl("t-test", dataframe$Method))) {
report <- "t.test"
} else if (all(grepl("Wilcox", dataframe$Method))) {
report <- "wilcox"
}
} else if ("Sum_Squares" %in% names(dataframe)) {
report <- "aov"
} else if (length(attr(dataframe, "model_class")) > 0) {
if ("lm" %in% attr(dataframe, "model_class")) {
report <- "lm"
} else if (grepl("correlation", attr(dataframe, "title"))) {
report <- "cor.test"
}
}
dataframe <- dataframe %>%
relocate(any_of(c("method", "alternative"))) %>%
select(-any_of("CI"))
if (report == "t.test") {
dataframe <- dataframe %>%
format_CI(col.name = "95% CI (t)") %>%
relocate(`95% CI (t)`, .after = t)
dataframe <- dataframe %>%
format_CI(c("d_CI_low", "d_CI_high"),
col.name = "95% CI (d)"
)
if (short == TRUE) {
dataframe <- dataframe %>%
select_if(!names(.) %in% c(
"method", "alternative", "Mean_Group1",
"Mean_Group2", "Difference", "95% CI (t)"
))
}
} else if (report == "lm") {
dataframe <- dataframe %>%
relocate(Fit, .after = Parameter)
dataframe <- dataframe %>%
format_CI(col.name = "95% CI (b)") %>%
relocate(`95% CI (b)`, .after = b)
dataframe <- dataframe %>%
format_CI(c("Std_Coefficient_CI_low", "Std_Coefficient_CI_high"),
col.name = "95% CI (B)"
)
if (short == TRUE) {
dataframe <- select(dataframe, -c("Fit", "95% CI (b)"))
dataframe <- dataframe[-(
which(is.na(dataframe$Parameter))[1]:nrow(dataframe)), ]
}
} else if (report == "aov") {
if ("Eta2_CI_low" %in% names(dataframe)) {
dataframe <- dataframe %>%
format_CI(c("Eta2_CI_low", "Eta2_CI_high"),
col.name = "95% CI (n2)"
)
}
if ("Eta2_partial_CI_low" %in% names(dataframe)) {
dataframe <- dataframe %>%
format_CI(c("Eta2_partial_CI_low", "Eta2_partial_CI_high"),
col.name = "95% CI (np2)"
)
}
} else if (report == "wilcox") {
dataframe <- dataframe %>%
format_CI(c("rank_biserial_CI_low", "rank_biserial_CI_high"),
col.name = "95% CI (rrb)"
)
}
}
dataframe
}
# prepare_flextable
prepare_flextable <- function(dataframe, separate.header, col.format.ci,
highlight, sh.pattern, unique.pattern) {
if (!missing(col.format.ci)) {
if (!methods::is(col.format.ci, "list")) {
col.format.ci <- list(col.format.ci)
}
for (i in col.format.ci) {
ci.name <- paste0(sh.pattern[i], "95% CI")
dataframe <- format_CI(
dataframe, i,
col.name =
ci.name
) %>%
relocate(all_of(ci.name), .after = select(
., contains(sh.pattern), -last_col()
) %>%
select(last_col()) %>% names())
}
}
if ("CI_lower" %in% names(dataframe) && "CI_upper" %in% names(dataframe)) {
dataframe <- format_CI(dataframe)
}
if ("CI_lower_B" %in% names(dataframe) && "CI_upper_B" %in% names(dataframe)) {
dataframe <- dataframe %>%
rename("95% CI (b)" = "95% CI") %>%
relocate("B", .after = last_col()) %>%
format_CI(c("CI_lower_B", "CI_upper_B"),
col.name = "95% CI (B)"
)
}
if ("CI_lower_r" %in% names(dataframe) && "CI_upper_r" %in% names(dataframe)) {
dataframe <- dataframe %>%
rename("95% CI (sigma)" = "95% CI") %>%
relocate("r", .after = last_col()) %>%
format_CI(c("CI_lower_r", "CI_upper_r"),
col.name = "95% CI (r)"
)
}
if ("rmsea.ci.lower" %in% names(dataframe) && "rmsea.ci.upper" %in% names(dataframe)) {
dataframe <- format_CI(dataframe, c(
"rmsea.ci.lower", "rmsea.ci.upper"
), "90% CI (RMSEA)")
if ("rmsea" %in% names(dataframe)) {
relocate(dataframe, "90% CI (RMSEA)", .after = "rmsea")
}
}
if (!missing(separate.header)) {
CI_lower.sh <- paste0(sh.pattern, rep(
"CI_lower",
each = unique.pattern
))
CI_upper.sh <- paste0(sh.pattern, rep(
"CI_upper",
each = unique.pattern
))
CI.df <- data.frame(CI_lower.sh, CI_upper.sh)
names(CI.df) <- NULL
if (any(unlist(CI.df) %in% names(dataframe))) {
for (i in seq(nrow(CI.df))) {
ci.name <- paste0(sh.pattern[i], "95% CI")
dataframe <- format_CI(
dataframe,
CI_low_high = unlist(CI.df[i, ]),
col.name = ci.name
) %>%
relocate(all_of(ci.name), .after = select(
., contains(sh.pattern[i]), -last_col()
) %>%
select(last_col()) %>% names())
}
}
}
dataframe <- dataframe %>%
mutate(across(contains("95% CI"), ~ ifelse(
.x == "[ NA, NA]", "", .x
)))
if ("p" %in% names(dataframe) && isTRUE(highlight) ||
is.numeric(highlight)) {
highlight <- ifelse(isTRUE(highlight), .05, highlight)
dataframe <- dataframe %>%
mutate(signif = ifelse(p < highlight, TRUE, FALSE))
}
if ("Predictor" %in% names(dataframe)) {
dataframe$Predictor <- gsub(
":", " \u00D7 ", dataframe$Predictor
)
}
if ("Term" %in% names(dataframe)) {
dataframe$Term <- gsub(
":", " \u00D7 ", dataframe$Term
)
}
if ("Model Number" %in% names(dataframe)) {
dataframe <- dataframe %>%
select(-all_of("Model Number"))
}
# Capitals
cols.capitals <- c(
"cfi", "tli", "nnfi", "rfi", "nfi", "pnfi", "ifi",
"rni", "logl", "aic", "bic", "bic2", "rmsea", "rmr",
"srmr", "crmr", "gfi", "agfi", "pgfi", "mfi", "ecvi"
)
dataframe <- dataframe %>%
rename_with(.fn = toupper, .cols = any_of(cols.capitals))
dataframe
}
# create_flextable
create_flextable <- function(dataframe, highlight, width, spacing, note,
separate.header, nice.borders) {
table <- dataframe %>%
{
if ("p" %in% names(dataframe) && highlight == TRUE ||
is.numeric(highlight)) {
flextable::flextable(., col_keys = names(dataframe)[-length(dataframe)])
} else {
flextable::flextable(.)
}
}
# Merge cells for repeated dependent variables...
if ("Dependent Variable" %in% names(dataframe) &&
any(duplicated(dataframe$`Dependent Variable`))) {
model.row <- which(!duplicated(dataframe$`Dependent Variable`,
fromLast = TRUE
))
table <- table %>%
flextable::merge_v(j = "Dependent Variable") %>%
flextable::hline(i = model.row, border = nice.borders)
}
table <- table %>%
flextable::hline_top(part = "head", border = nice.borders) %>%
flextable::hline_bottom(part = "head", border = nice.borders) %>%
flextable::hline_top(part = "body", border = nice.borders) %>%
flextable::hline_bottom(part = "body", border = nice.borders) %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = 1, align = "left", part = "body") %>%
flextable::valign(valign = "center", part = "all") %>%
flextable::line_spacing(space = spacing, part = "all") %>%
flextable::fix_border_issues()
if (!is.null(width)) {
table <- table %>%
flextable::set_table_properties(layout = "autofit", width = width)
} else {
table <- table %>%
flextable::set_table_properties(layout = "autofit")
}
if (!missing(note)) {
note.list <- as.list(note)
table <- table %>%
flextable::add_footer_lines("") %>%
flextable::compose(i = 1, j = 1, value = flextable::as_paragraph(
flextable::as_i("Note. "), note[[1]]
), part = "footer") %>%
flextable::align(part = "footer", align = "left")
if (length(note.list) > 1) {
table <- table %>%
flextable::add_footer_lines(note.list[-1])
}
}
# Separate headers
if (!missing(separate.header)) {
table <- table %>%
flextable::separate_header("span-top", split = "[.]")
}
table <- table %>%
flextable::fontsize(part = "all", size = 12) %>%
flextable::font(part = "all", fontname = "Times New Roman")
table
}
format_columns <- function(dataframe, table, italics, separate.header,
highlight, sh.pattern, unique.pattern,
format_p_internal) {
## ....................................
## Special cases ####
# Fix header with italics
if (!missing(italics) & missing(separate.header)) {
table <- table %>%
flextable::italic(j = italics, part = "header")
} else if (!missing(italics) & !missing(separate.header)) {
level.number <- sum(charToRaw(names(
dataframe[2]
)) == charToRaw(".")) + 1
table <- table %>%
flextable::italic(j = italics, i = level.number, part = "header")
}
# Degrees of freedom
cols.df <- "df"
if (!missing(separate.header)) {
cols.df.sh <- paste0(sh.pattern, rep(
cols.df,
each = unique.pattern
))
cols.df <- c(cols.df, cols.df.sh)
}
for (i in cols.df) {
if (i %in% names(dataframe)) {
df.digits <- ifelse(any(dataframe[i] %% 1 == 0), 0, 2)
table <- table %>%
format_flex(j = i, digits = df.digits)
}
}
## .....................................
## 2-digit columns ####
# Italicize all these column names
cols.2digits <- c("t", "SE", "SD", "F", "b", "M", "W", "d", "g", "Mu", "S", "z", "Z")
if (!missing(separate.header)) {
cols.2digits.sh <- paste0(sh.pattern, rep(
cols.2digits,
each = unique.pattern
))
cols.2digits <- c(cols.2digits, cols.2digits.sh)
}
for (i in cols.2digits) {
if (i %in% names(dataframe)) {
table <- table %>%
format_flex(j = i)
}
}
## .....................................
## 0-digit columns ####
cols.0digits <- c("N", "n")
if (!missing(separate.header)) {
cols.0digits.sh <- paste0(sh.pattern, rep(
cols.0digits,
each = unique.pattern
))
cols.0digits <- c(cols.0digits, cols.0digits.sh)
}
for (i in cols.0digits) {
if (i %in% names(dataframe)) {
table <- table %>%
format_flex(j = i, digits = 0)
}
}
## .....................................
## Formatting functions ####
compose.table0 <- data.frame(
col = c("p", "r"),
fun = c(format_p_internal, "format_r")
)
if (!missing(separate.header)) {
cols.sh <- paste0(sh.pattern, rep(
compose.table0$col,
each = unique.pattern
))
table0.sh <- data.frame(
col = cols.sh,
fun = rep(compose.table0$fun,
each =
length(cols.sh) / length(compose.table0$fun)
)
)
compose.table0 <- rbind(compose.table0, table0.sh)
}
for (i in seq(nrow(compose.table0))) {
if (compose.table0[i, "col"] %in% names(dataframe)) {
table <- table %>%
format_flex(
j = compose.table0[i, "col"],
fun = compose.table0[i, "fun"]
)
}
}
## .....................................
## Special symbols ####
compose.table1 <- data.frame(
col = c(
"95% CI (b)", "95% CI (B)", "95% CI (t)", "95% CI (d)",
"95% CI (np2)", "95% CI (n2)", "95% CI (rrb)",
"95% CI (sigma)", "95% CI (sigma2)", "95% CI (r)", "np2",
"n2", "ges", "dR", "Predictor (+/-1 SD)", "M1 - M2", "tau",
"rho", "rrb", "chi2", "chi2.df", "B", "sigma", "sigma2"
),
value = c(
'"95% CI (", flextable::as_i("b"), ")"', # small b
'"95% CI (", flextable::as_i("b"), "*", ")"', # beta
'"95% CI (", flextable::as_i("t"), ")"', # t
'"95% CI (", flextable::as_i("d"), ")"', # d
'"95% CI (", "\u03b7", flextable::as_sub("p"), flextable::as_sup("2"), ")"', # peta
'"95% CI (", "\u03b7", flextable::as_sup("2"), ")"', # eta
'"95% CI (", flextable::as_i("r"), flextable::as_i(flextable::as_sub("rb")), ")"', # rrb
'"95% CI (", "\u03C3", ")"', # sigma
'"95% CI (", "\u03C3", flextable::as_sup("2"), ")"', # sigma2
'"95% CI (", flextable::as_i("r"), ")"', # r
'"\u03b7", flextable::as_sub("p"), flextable::as_sup("2")', # eta
'"\u03b7", flextable::as_sup("2")', # eta
'"\u03b7", flextable::as_sub("G"), flextable::as_sup("2")', # eta
'flextable::as_i("d"), flextable::as_sub("R")',
'"Predictor (+/-1 ", flextable::as_i("SD"), ")"',
'flextable::as_i("M"), flextable::as_sub("1"), " - ", flextable::as_i("M"), flextable::as_sub("2")',
'"\u03C4"', # tau
'"\u03C1"', # rho
'flextable::as_i("r"), flextable::as_i(flextable::as_sub("rb"))',
'"\u03C7", flextable::as_sup("2")', # Chi square
'"\u03C7", flextable::as_sup("2"), "\u2215", flextable::as_i("df")', # Chi square
'flextable::as_i("b"), "*"', # beta
'"\u03C3"', # sigma
'"\u03C3", flextable::as_sup("2")' # sigma
)
)
for (i in seq(nrow(compose.table1))) {
if (compose.table1[i, "col"] %in% names(dataframe)) {
table <- table %>%
format_flex(
j = compose.table1[i, "col"],
value = compose.table1[i, "value"]
)
}
}
# Values that can't be greater than 1, but not just italic formatting like r
compose.table2 <- data.frame(
col = c("R2", "sr2", "CFI", "TLI", "RMSEA", "SRMR"),
value = c(
'flextable::as_i("R"), flextable::as_sup("2")',
'flextable::as_i("sr"), flextable::as_sup("2")',
"CFI", "TLI", "RMSEA", "SRMR"
)
)
if (!missing(separate.header)) {
cols.sh <- paste0(sh.pattern, rep(
compose.table2$col,
each = unique.pattern
))
table2.sh <- data.frame(
col = cols.sh,
value = rep(compose.table2$value,
each =
length(cols.sh) / length(compose.table2$value)
)
)
compose.table2 <- rbind(compose.table2, table2.sh)
}
for (i in seq(nrow(compose.table2))) {
if (compose.table2[i, "col"] %in% names(dataframe)) {
table <- table %>%
format_flex(
j = compose.table2[i, "col"],
value = compose.table2[i, "value"],
fun = "format_r"
)
}
}
if ("p" %in% names(dataframe) && isTRUE(highlight) ||
is.numeric(highlight)) {
table <- table %>%
flextable::bold(
i = ~ signif == TRUE,
j = table$col_keys
) %>%
flextable::bg(
i = ~ signif == TRUE,
j = table$col_keys,
bg = "#D9D9D9"
)
}
# Set attributes for variables which digits should not be changed for later step
attr(table, "dont_change") <- c(
compose.table0$col, cols.df, cols.2digits, cols.0digits, compose.table2$col
)
table
}
beautify_flextable <- function(
dataframe, table, separate.header, col.format.p, col.format.r,
format.custom, col.format.custom, sh.pattern, unique.pattern,
format_p_internal) {
dont.change0 <- attr(table, "dont_change")
dont.change <- paste0("^", dont.change0, "$", collapse = "|")
if (!missing(separate.header)) {
dont.change.sh <- paste0(sh.pattern, rep(
dont.change0,
each = unique.pattern
))
dont.change.sh <- paste0("^", dont.change.sh, "$", collapse = "|")
dont.change <- paste0(dont.change, "|", dont.change.sh)
}
table <- table %>%
flextable::colformat_double(
j = (select(dataframe, where(is.numeric)) %>%
select(-matches(dont.change,
ignore.case = FALSE
)) %>% names()),
big.mark = ",", digits = 2
)
if (!missing(col.format.p)) {
table <- table %>%
parse_formatter(
column = table$col_keys[col.format.p],
fun = format_p_internal
)
}
if (!missing(col.format.r)) {
table <- table %>%
parse_formatter(
column = table$col_keys[col.format.r],
fun = "format_r"
)
}
if (!missing(format.custom) & !missing(col.format.custom)) {
# table %>%
# parse_formatter(column = table$col_keys[col.format.custom],
# fun = "format.custom") -> table
# Error in set_formatter: object 'format.custom' not found
rExpression <- paste0(
"table <- table %>% flextable::set_formatter(`",
table$col_keys[col.format.custom], "` = ",
format.custom, ")"
)
eval(parse(text = rExpression))
}
table
}
finalize_table <- function(table, title, nice.borders) {
if (!missing(title)) {
invisible.borders <- flextable::fp_border_default("width" = 0)
italic.lvl <- ifelse(length(title) == 1, 1, 2)
bold.decision <- ifelse(length(title) == 1, FALSE, TRUE)
table <- table %>%
flextable::add_header_lines(values = title) %>%
flextable::align(
part = "header", i = seq(length(title)),
align = "left"
) %>%
flextable::hline(
part = "header", i = seq_len(length(title) - 1),
border = invisible.borders
) %>%
flextable::hline(
part = "header", i = length(title),
border = nice.borders
) %>%
flextable::hline_top(border = invisible.borders, part = "header") %>%
# flextable::border(part = "header", i = 1:length(title),
# border = invisible.borders) %>%
flextable::italic(part = "header", i = italic.lvl) %>%
flextable::bold(., part = "header", i = 1, bold = bold.decision)
}
table
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.