diff --git a/DESCRIPTION b/DESCRIPTION
index 4292f6c..475358d 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: desctable
Title: Produce Descriptive and Comparative Tables Easily
-Version: 0.1.7
+Version: 0.1.8
Authors@R: c(person("Maxime", "Wack", email = "maximewack@free.fr", role = c("aut", "cre")),
person("Adrien", "Boukobza", email = "hadrien_b@hotmail.fr", role = c("aut")))
Description: Easily create descriptive and comparative tables.
@@ -16,13 +16,13 @@ URL: https://github.com/maximewack/desctable
BugReports: https://github.com/maximewack/desctable/issues
Imports:
dplyr,
- purrr,
DT,
htmltools,
pander
Suggests:
knitr,
rmarkdown,
+ purrr,
survival
RoxygenNote: 7.0.2
VignetteBuilder: knitr
diff --git a/NEWS b/NEWS
index 74b6ee2..78426f7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Version 0.1.8
+
+- Code cleanup:
+ - use RStudio style guidelines for all code and docs
+ - use fewer tidyverse functions internally, drop `purrr` dependancy
+
Version 0.1.7
- Vignette and README with RStudio style guidelines
diff --git a/R/build.R b/R/build.R
index a482d9d..4334cbf 100644
--- a/R/build.R
+++ b/R/build.R
@@ -1,10 +1,18 @@
#' Generate one statistic for all variables
#'
+#' Use one stat function (made safe using statify) on all the data
+#' to produce a single statistics column.
+#'
+#' The result is either a numeric vector, or a character vector if
+#' the content of the column is not made entirely of numbers.
+#'
#' @param stat The statistic to use
#' @param data The dataframe to apply the statistic to
#' @return A vector for one statistic column
-statColumn <- function(stat, data)
-{
+statColumn <- function(stat, data) {
+ # Apply one statified stat function to every variable in the data
+ # Return a simple vector for the column
+ # Statify checks types and output for the stat function. Returns a numeric vector or a character vector if needed.
data %>%
lapply(statify, stat) %>%
unlist()
@@ -13,18 +21,25 @@ statColumn <- function(stat, data)
#' Generate the table of all statistics for all variables
#'
+#' If stats is a list of functions, use them.
+#' If it is a single function, use it with the entire data as
+#' its argument to produce a list of statistical functions to use.
+#'
#' @param data The dataframe to apply the statistic to
#' @param stats A list of named statistics to use
#' @return A dataframe of all statistics for all variables
-statTable <- function(data, stats)
-{
- # Call the stats arg_function passed, or use the provided list as-is
- if (is.function(stats))
- stats = stats(data)
+statTable <- function(data, stats) {
+ # If stats is a function, apply it to the data to obtain a list of stat functions
+ # Else use the function list as-is
+ if (is.function(stats)) stats = stats(data)
+ # Compute a statColumn for every stat function in stats
+ # Assemble the result in a dataframe
stats %>%
lapply(statColumn, data) %>%
- data.frame(check.names = F, row.names = NULL, stringsAsFactors = F)
+ data.frame(check.names = F,
+ row.names = NULL,
+ stringsAsFactors = F)
}
@@ -40,38 +55,38 @@ statTable <- function(data, stats)
#' @param data The dataframe to get the names from
#' @param labels The optional named character vector containing the keypairs var = "Label"
#' @return A dataframe with one variable named "Variables", a character vector of variable names/labels and levels
-varColumn <- function(data, labels = NULL)
-{
-# Replace variable names by their labels, if they exist
- names(data) -> base_names
+varColumn <- function(data, labels = NULL) {
+ # Every variable name that exists in the labels is to be replaced with its corresponding label
+ # Labels for non-existing variables are ignored
+ # Variables with no label are not replaced and used as-is
+ base_names <- names(data)
base_names[base_names %in% names(labels)] <- labels[base_names[base_names %in% names(labels)]]
+ # Check if there are factors
+ data %>%
+ lapply(is.factor) %>%
+ unlist() -> factors
+
# Insert levels for factors after the variable name
- if (any(data %>% lapply(is.factor) %>% unlist()))
- {
- data %>%
- lapply(is.factor) %>%
- unlist() %>%
- which() -> factors_idx
+ if (any(factors)) {
+ factors_idx <- which(factors)
+ # Factor names in **bold**
base_names[factors_idx] <- paste0("**", base_names[factors_idx], "**")
- factor_levels <-
- factors_idx %>%
- lapply(function(x)
- {
- paste0(base_names[x],
- ": ",
- "*",
- levels(data[[x]]),
- "*")
- })
-
- insert(x = base_names,
- y = factor_levels,
- position = factors_idx) -> base_names
+
+ # Factor levels in *italic*
+ factor_levels <- lapply(factors_idx, function(x) paste0(base_names[x], ": ", "*", levels(data[[x]]), "*"))
+
+ # Insert the factor levels after each factor name
+ base_names <- insert(x = base_names,
+ y = factor_levels,
+ position = factors_idx)
}
- data.frame(Variables = base_names, check.names = F, row.names = NULL, stringsAsFactors = F)
+ data.frame(Variables = base_names,
+ check.names = F,
+ row.names = NULL,
+ stringsAsFactors = F)
}
@@ -151,35 +166,32 @@ varColumn <- function(data, labels = NULL)
#' iris %>%
#' group_by(Petal.Length > 5) %>%
#' desctable(tests = list(.auto = tests_auto, Species = ~chisq.test))
-desctable <- function(data, stats, tests, labels)
-{
+desctable <- function(data, stats, tests, labels) {
UseMethod("desctable", data)
}
#' @rdname desctable
#' @export
-desctable.default <- function(data, stats = stats_auto, tests, labels = NULL)
-{
- # Build the complete table
+desctable.default <- function(data, stats = stats_auto, tests, labels = NULL) {
+ # Assemble the Variables and the statTable in a single desctable object
list(Variables = varColumn(data, labels),
- stats = statTable(data, stats)) %>%
- `class<-`("desctable")
+ stats = statTable(data, stats)) %>%
+ set_desctable_class()
}
#' @rdname desctable
#' @export
-desctable.grouped_df <- function(data, stats = stats_auto, tests = tests_auto, labels = NULL)
-{
+desctable.grouped_df <- function(data, stats = stats_auto, tests = tests_auto, labels = NULL) {
# Get groups then ungroup dataframe
- grps <- data %>% dplyr::groups()
+ grps <- dplyr::groups(data)
data <- dplyr::ungroup(data)
- # Build the complete table recursively, assign "desctable" class
+ # Assemble the Variables (excluding the grouping ones) and the subTables recursively in a single desctable object
c(Variables = list(varColumn(data[!names(data) %in% (grps %>% lapply(as.character) %>% unlist())], labels)),
subTable(data, stats, tests, grps)) %>%
- `class<-`("desctable")
+ set_desctable_class()
}
@@ -191,8 +203,7 @@ desctable.grouped_df <- function(data, stats = stats_auto, tests = tests_auto, l
#' @param grp Grouping factor
#' @param df Dataframe containing the grouping factor
#' @return A character vector with the names for the subtables
-subNames <- function(grp, df)
-{
+subNames <- function(grp, df) {
paste0(as.character(grp),
": ",
eval(grp, df) %>% factor() %>% levels(),
@@ -208,38 +219,35 @@ subNames <- function(grp, df)
#' @param tests Test function or list of functions
#' @param grp Grouping factor
#' @return A numeric vector of pvalues
-testColumn <- function(df, tests, grp)
-{
+testColumn <- function(df, tests, grp) {
group <- eval(grp, df)
- df <- df %>%
- dplyr::select(-!!(grp))
+ df <- df[!names(df) %in% as.character(grp)]
- if (is.function(tests))
- {
- ftests <- df %>%
- lapply(tests, group %>% factor())
+ # If tests is a function, apply it to the data and the grouping factor to produce a list of tests
+ # If there is an .auto element in the list of tests, apply the function as previously to select the relevant test
+ # If there is a .default element, use it as tests
+ # Else fall back on kruskal.test
+ if (is.function(tests)) {
+ ftests <- lapply(df, tests, factor(group))
tests <- ftests
- } else if (!is.null(tests$.auto))
- {
- ftests <- df %>%
- lapply(tests$.auto, group %>% factor)
- } else if (!is.null(tests$.default))
- {
- ftests <- df %>%
- lapply(function(x){tests$.default})
- } else
- {
- ftests <- df %>%
- lapply(function(x){stats::kruskal.test})
- }
+ } else if (!is.null(tests$.auto)) ftests <- lapply(df, tests$.auto, factor(group))
+ else if (!is.null(tests$.default)) ftests <- lapply(df, function(x){tests$.default})
+ else ftests <- lapply(df, function(x){stats::kruskal.test})
- names(tests) %>% setdiff(".auto") %>% intersect(names(df)) -> forced_tests
+ # Select the forced (named) tests
+ tests %>%
+ names() %>%
+ setdiff(".auto") %>%
+ intersect(names(df)) -> forced_tests
+
+ # Assemble the complete list of tests to compute
ftests[names(ftests) %in% forced_tests][forced_tests] <- tests[forced_tests]
+ # Compute the tests (made safe with testify) on the variable, using the grouping variable
df %>%
purrr::map2(ftests, testify, group) %>%
- dplyr::bind_rows()
+ Reduce(f = rbind)
}
@@ -250,32 +258,26 @@ testColumn <- function(df, tests, grp)
#' @param tests Tests list/function to use
#' @param grps List of symbols for grouping factors
#' @return A nested list of statTables and testColumns
-subTable <- function(df, stats, tests, grps)
-{
- # Final group, make tests
- if (length(grps) == 1)
- {
- group <- eval(grps[[1]], df) %>% factor()
+subTable <- function(df, stats, tests, grps) {
+ # Final group, compute tests
+ if (length(grps) == 1) {
+ group <- factor(eval(grps[[1]], df))
# Create the subtable stats
- df %>%
- dplyr::select(-!!(grps[[1]])) %>%
+ df[!names(df) %in% as.character(grps[[1]])] %>%
by(group, statTable, stats) %>%
# Name the subtables with info about group and group size
stats::setNames(subNames(grps[[1]], df)) -> stats
# Create the subtable tests
- testColumn(df, tests, grps[[1]]) -> pvalues
+ pvalues <- testColumn(df, tests, grps[[1]])
c(stats, tests = list(pvalues))
- }
- else
- {
+ } else {
group <- eval(grps[[1]], df)
# Go through the next grouping levels and build the subtables
- df %>%
- dplyr::select(-!!(grps[[1]])) %>%
+ df[!names(df) %in% as.character(grps[[1]])] %>%
by(group, subTable, stats, tests, grps[-1]) %>%
# Name the subtables with info about group and group size
stats::setNames(subNames(grps[[1]], df))
diff --git a/R/convenience_functions.R b/R/convenience_functions.R
index e11887c..ee950c1 100644
--- a/R/convenience_functions.R
+++ b/R/convenience_functions.R
@@ -2,15 +2,13 @@
#'
#' Return a compatible vector of length nlevels(x) + 1
#' to print the percentages of each level of a factor
+#'
#' @param x A factor
#' @export
#' @return A nlevels(x) + 1 length vector of percentages
-percent <- function(x)
-{
- if (x %>% is.factor())
- c(NA, summary(x, maxsum = Inf) / length(x)) * 100
- else
- NA
+percent <- function(x) {
+ if (is.factor(x)) c(NA, summary(x, maxsum = Inf) / length(x)) * 100
+ else NA
}
@@ -20,8 +18,7 @@ percent <- function(x)
#' @param x A vector
#' @return The IQR
#' @export
-IQR <- function(x)
-{
+IQR <- function(x) {
base::diff(stats::quantile(x, c(0.25, 0.75), na.rm = T))
}
@@ -34,15 +31,10 @@ IQR <- function(x)
#' @param x A numerical vector
#' @export
#' @return A boolean
-is.normal <- function(x)
-{
- if (! x %>% is.numeric())
- F
- else if (length(x %>% stats::na.omit()) >= 30)
- tryCatch(stats::shapiro.test(x)$p.value > .1,
- error = function(e) F)
- else
- F
+is.normal <- function(x) {
+ if (!is.numeric(x)) F
+ else if (length(stats::na.omit(x)) >= 30) tryCatch(stats::shapiro.test(x)$p.value > .1, error = function(e) F)
+ else F
}
@@ -191,15 +183,15 @@ is.normal <- function(x)
#' ###
#' }
#' @export
-fisher.test <- function(x, y, workspace, hybrid, control, or, alternative, conf.int, conf.level, simulate.p.value, B)
-{
+fisher.test <- function(x, y, workspace, hybrid, control, or, alternative, conf.int, conf.level, simulate.p.value, B) {
UseMethod("fisher.test")
}
#' @rdname fisher.test
-fisher.test.default <- function(x, ...) stats::fisher.test(x, ...)
-
+fisher.test.default <- function(x, ...) {
+ stats::fisher.test(x, ...)
+}
#' @rdname fisher.test
fisher.test.formula <- function(x,
@@ -212,19 +204,18 @@ fisher.test.formula <- function(x,
conf.int = T,
conf.level = .95,
simulate.p.value = F,
- B = 2000)
-{
+ B = 2000) {
stats::fisher.test(x = eval(x[[2]], envir = parent.frame()),
- y = eval(x[[3]], envir = parent.frame()),
- workspace = workspace,
- hybrid = hybrid,
- control = control,
- or = or,
- alternative = alternative,
- conf.int = conf.int,
- conf.level = conf.level,
- simulate.p.value = simulate.p.value,
- B = B)
+ y = eval(x[[3]], envir = parent.frame()),
+ workspace = workspace,
+ hybrid = hybrid,
+ control = control,
+ or = or,
+ alternative = alternative,
+ conf.int = conf.int,
+ conf.level = conf.level,
+ simulate.p.value = simulate.p.value,
+ B = B)
}
@@ -352,8 +343,7 @@ fisher.test.formula <- function(x,
#' ###
#' }
#' @export
-chisq.test <- function(x, y, correct, p, rescale.p, simulate.p.value, B)
-{
+chisq.test <- function(x, y, correct, p, rescale.p, simulate.p.value, B) {
UseMethod("chisq.test")
}
@@ -369,15 +359,14 @@ chisq.test.formula <- function(x,
p = rep(1/length(x), length(x)),
rescale.p = F,
simulate.p.value = F,
- B = 2000)
-{
+ B = 2000) {
stats::chisq.test(x = eval(x[[2]], envir = parent.frame()),
- y = eval(x[[3]], envir = parent.frame()),
- correct = correct,
- p = p,
- rescale.p = rescale.p,
- simulate.p.value = simulate.p.value,
- B = B)
+ y = eval(x[[3]], envir = parent.frame()),
+ correct = correct,
+ p = p,
+ rescale.p = rescale.p,
+ simulate.p.value = simulate.p.value,
+ B = B)
}
@@ -386,8 +375,7 @@ chisq.test.formula <- function(x,
#' @param formula An anova formula (\code{variable ~ grouping variable})
#' @seealso \code{\link{oneway.test}}
#' @export
-ANOVA <- function(formula)
-{
+ANOVA <- function(formula) {
stats::oneway.test(formula, var.equal = T)
}
@@ -396,7 +384,6 @@ ANOVA <- function(formula)
#'
#' An empty test
#' @param formula A formula
-no.test <- function(formula)
-{
+no.test <- function(formula) {
data.frame(p.value = NA)
}
diff --git a/R/output.R b/R/output.R
index 00eddbd..3ab5992 100644
--- a/R/output.R
+++ b/R/output.R
@@ -4,9 +4,8 @@
#' @param ... Additional print parameters
#' @return A flat dataframe
#' @export
-print.desctable <- function(x, ...)
-{
- print(x %>% as.data.frame())
+print.desctable <- function(x, ...) {
+ print(as.data.frame(x))
}
@@ -16,13 +15,15 @@ print.desctable <- function(x, ...)
#' @param ... Additional as.data.frame parameters
#' @return A flat dataframe
#' @export
-as.data.frame.desctable <- function(x, ...)
-{
+as.data.frame.desctable <- function(x, ...) {
+ # Discard "markdown" formatting of variable names
x$Variables$Variables <- gsub("\\*\\*(.*?)\\*\\*", "\\1", x$Variables$Variables)
x$Variables$Variables <- gsub("\\*(.*?)\\*", "\\1", x$Variables$Variables)
- header <- x %>% header("dataframe")
+ # Create a dataframe header
+ header <- header(x, "dataframe")
+ # Make a standard dataframe
x %>%
flatten_desctable() %>%
data.frame(check.names = F, ...) %>%
@@ -47,15 +48,16 @@ pander.desctable <- function(x = NULL,
keep.line.breaks = T,
split.tables = Inf,
emphasize.rownames = F,
- ...)
-{
- if (is.null(digits))
- digits <- pander::panderOptions("digits")
+ ...) {
+ if (is.null(digits)) digits <- pander::panderOptions("digits")
+ # Discard "markdown" and insert 4 NbSp before factor levels
x$Variables$Variables <- gsub("\\*\\*(.*?)\\*\\*: \\*(.*?)\\*", " \\2", x$Variables$Variables)
- header <- x %>% header("pander")
+ # Create a pander header
+ header <- header(x, "pander")
+ # Make a dataframe and push it to pandoc
x %>%
flatten_desctable %>%
data.frame(check.names = F, stringsAsFactors = F) %>%
@@ -107,8 +109,7 @@ pander.desctable <- function(x = NULL,
#' ###
#' @inheritParams DT::datatable
#' @export
-datatable <- function(data, ...)
-{
+datatable <- function(data, ...) {
UseMethod("datatable", data)
}
@@ -130,8 +131,7 @@ datatable.default <- function(data,
autoHideNavigation = getOption("DT.autoHideNavigation", NULL),
selection = c("multiple", "single", "none"),
extensions = list(),
- plugins = NULL, ...)
-{
+ plugins = NULL, ...) {
DT::datatable(data, options = options, class = class, callback = callback, caption = caption, filter = filter, escape = escape, style = style, width = width, height = height, elementId = elementId, fillContainer = fillContainer, autoHideNavigation = autoHideNavigation, selection = selection, extensions = extensions, plugins = plugins, ...)
}
@@ -162,19 +162,26 @@ datatable.desctable <- function(data,
extensions = c("FixedHeader", "FixedColumns", "Buttons"),
plugins = NULL,
rownames = F,
- digits = 2, ...)
-{
+ digits = 2, ...) {
+ # Discard "markdown" and insert 4 NbSp before factor levels
data$Variables$Variables <- gsub("\\*\\*(.*?)\\*\\*: \\*(.*?)\\*", " \\2", data$Variables$Variables)
data$Variables$Variables <- gsub("\\*\\*(.*?)\\*\\*", "\\1", data$Variables$Variables)
- header <- data %>% header("datatable")
+ # Create a datatable header
+ header <- header(data, "datatable")
- data %>%
- flatten_desctable() -> flat
+ # Flatten desctable
+ flat <- flatten_desctable(data)
+ # Replace NAs and apply digits arg
if (!is.null(digits))
- flat <- flat %>% lapply(prettyNum, digits = digits) %>% lapply(gsub, pattern = "^NA$", replacement = "")
+ {
+ flat %>%
+ lapply(prettyNum, digits = digits) %>%
+ lapply(gsub, pattern = "^NA$", replacement = "") -> flat
+ }
+ # Make a dataframe and push it to datatable, with its custom header
flat %>%
data.frame(check.names = F, stringsAsFactors = F) %>%
DT::datatable(container = header,
diff --git a/R/stats.R b/R/stats.R
index 9ec6383..3cace49 100644
--- a/R/stats.R
+++ b/R/stats.R
@@ -13,61 +13,51 @@
#' @param x A vector
#' @export
#' @return The results for the function applied on the vector, compatible with the format of the result table
-statify <- function(x, f)
-{
+statify <- function(x, f) {
UseMethod("statify", f)
}
#' @rdname statify
#' @export
-statify.default <- function(x, f)
-{
- x <- x %>% stats::na.omit()
+statify.default <- function(x, f) {
+ # Discard NA values
+ x <- stats::na.omit(x)
# Try f(x), silent warnings and fail with NA
- res <- tryCatch(x %>% f,
- warning = function(e) suppressWarnings(x %>% f),
+ res <- tryCatch(f(x),
+ warning = function(e) suppressWarnings(f(x)),
error = function(e) NA)
# If x is a factor and f(x) behaves as expected (nlevel + total value), return f(x), or apply f(x) on each level, or fail with n+1 NA
- # If it is a numeric, return f(x) if it behaves as expected (ONE value), or fail with NA
- if (x %>% is.factor)
- {
- if (length(res) == nlevels(x) + 1)
- res
- else if (length(res) == 1)
- c(res, lapply(levels(x), function(lvl)
- {
+ if (is.factor(x)) {
+ if (length(res) == nlevels(x) + 1) res
+ else if (length(res) == 1) {
+ c(res, lapply(levels(x), function(lvl) {
tryCatch(f(x[x == lvl]),
warning = function(e) suppressWarnings(f(x[x == lvl])),
error = function(e) NA)
}) %>% unlist)
- else
- rep(NA, nlevels(x) + 1)
- } else
- {
- if (length(res) == 1)
- if (res %>% is.numeric | res %>% is.na)
- res
- else
- res %>% as.character
- else
- NA
+ }
+ else rep(NA, nlevels(x) + 1)
+ # If it is a numeric, return f(x) if it behaves as expected (ONE value), or fail with NA
+ } else {
+ if (length(res) == 1) {
+ if (is.numeric(res) | is.na(res)) res
+ else as.character(res)
+ }
+ else NA
}
}
#' @rdname statify
#' @export
-statify.formula <- function(x, f)
-{
+statify.formula <- function(x, f) {
# if expression quoted with ~, evaluate the expression
- if (length(f) == 2)
- eval(f[[2]])
+ if (length(f) == 2) eval(f[[2]])
# else parse the formula (cond ~ T | F)
- else
- statify.default(x, parse_formula(x, f))
+ else statify.default(x, parse_formula(x, f))
}
@@ -88,8 +78,7 @@ statify.formula <- function(x, f)
#' @param data The dataframe to apply the statistic to
#' @return A list of statistics to use, potentially assessed from the dataframe
#' @export
-stats_default <- function(data)
-{
+stats_default <- function(data) {
list("N" = length,
"%" = percent,
"Mean" = is.normal ~ mean,
@@ -101,8 +90,7 @@ stats_default <- function(data)
#' @rdname stats_default
#' @export
-stats_normal <- function(data)
-{
+stats_normal <- function(data) {
list("N" = length,
"%" = percent,
"Mean" = mean,
@@ -112,8 +100,7 @@ stats_normal <- function(data)
#' @rdname stats_default
#' @export
-stats_nonnormal <- function(data)
-{
+stats_nonnormal <- function(data) {
list("N" = length,
"%" = percent,
"Median" = stats::median,
@@ -123,47 +110,39 @@ stats_nonnormal <- function(data)
#' @rdname stats_default
#' @export
-stats_auto <- function(data)
-{
+stats_auto <- function(data) {
data %>%
Filter(f = is.numeric) %>%
lapply(is.normal) %>%
unlist() -> shapiro
- if (length(shapiro) == 0)
- {
+ if (length(shapiro) == 0) {
normal <- F
nonnormal <- F
- }
- else
- {
- any(shapiro) -> normal
- any(!shapiro) -> nonnormal
+ } else {
+ normal <- any(shapiro)
+ nonnormal <- any(!shapiro)
}
- any(data %>% lapply(is.factor) %>% unlist()) -> fact
-
- if (fact & normal & !nonnormal)
- stats_normal(data)
- else if (fact & !normal & nonnormal)
- stats_nonnormal(data)
- else if (fact & !normal & !nonnormal)
- list("N" = length,
- "%" = percent)
- else if (!fact & normal & nonnormal)
- list("N" = length,
- "Mean" = is.normal ~ mean,
- "sd" = is.normal ~ sd,
- "Med" = stats::median,
- "IQR" = is.factor ~ NA | IQR)
- else if (!fact & normal & !nonnormal)
- list("N" = length,
- "Mean" = mean,
- "sd" = stats::sd)
- else if (!fact & !normal & nonnormal)
- list("N" = length,
- "Med" = stats::median,
- "IQR" = IQR)
- else
- stats_default(data)
+ data %>%
+ lapply(is.factor) %>%
+ unlist() %>%
+ any() -> fact
+
+ if (fact & normal & !nonnormal) stats_normal(data)
+ else if (fact & !normal & nonnormal) stats_nonnormal(data)
+ else if (fact & !normal & !nonnormal) list("N" = length,
+ "%" = percent)
+ else if (!fact & normal & nonnormal) list("N" = length,
+ "Mean" = is.normal ~ mean,
+ "sd" = is.normal ~ sd,
+ "Med" = stats::median,
+ "IQR" = is.factor ~ NA | IQR)
+ else if (!fact & normal & !nonnormal) list("N" = length,
+ "Mean" = mean,
+ "sd" = stats::sd)
+ else if (!fact & !normal & nonnormal) list("N" = length,
+ "Med" = stats::median,
+ "IQR" = IQR)
+ else stats_default(data)
}
diff --git a/R/tests.R b/R/tests.R
index dc3533a..76857f0 100644
--- a/R/tests.R
+++ b/R/tests.R
@@ -7,20 +7,25 @@
#' @param f The function to try to apply, or a formula combining two functions
#' @param group Grouping factor
#' @return The results for the function applied on the vector, compatible with the format of the result table
-testify <- function(x, f, group)
-{
- fun <- f %>% deparse() %>% Reduce(f = paste0) %>% substring(2)
+testify <- function(x, f, group) {
+ # Extract the name of the function
+ f %>%
+ deparse() %>%
+ Reduce(f = paste0) %>%
+ substring(2) -> fun
+
+ # Try the function
f <- eval(f[[2]])
p <- tryCatch(f(x ~ group)$p.value[1],
error = function(e) {message(e);NaN})
- if (is.factor(x))
- data.frame(p = c(p, NA %>% rep(nlevels(x))),
- test = c(fun, NA %>% rep(nlevels(x))),
- row.names = NULL, check.names = F, stringsAsFactors = F)
- else
- data.frame(p = p,
- test = fun,
- row.names = NULL, check.names = F, stringsAsFactors = F)
+
+ # Return the correct number of rows depending on the variable type
+ if (is.factor(x)) data.frame(p = c(p, rep(NA, nlevels(x))),
+ test = c(fun, rep(NA, nlevels(x))),
+ row.names = NULL, check.names = F, stringsAsFactors = F)
+ else data.frame(p = p,
+ test = fun,
+ row.names = NULL, check.names = F, stringsAsFactors = F)
}
@@ -34,34 +39,28 @@ testify <- function(x, f, group)
#' @param grp The variable for the groups
#' @return A statistical test function
#' @export
-tests_auto <- function(var, grp)
-{
- grp <- grp %>% factor()
- if (nlevels(grp) < 2)
- ~no.test
- else if (var %>% is.factor())
- if (tryCatch(fisher.test(var ~ grp)$p.value %>% is.numeric(), error = function(e) F))
- ~fisher.test
- else
- ~chisq.test
- else
- {
- all_normal <- all(var %>% tapply(grp, is.normal))
- if (nlevels(grp) == 2)
- if (all_normal)
- if (tryCatch(stats::var.test(var ~ grp)$p.value > .1, warning = function(e) F, error = function(e) F))
- ~. %>% t.test(var.equal = T)
- else
- ~. %>% t.test(var.equal = F)
- else
- ~wilcox.test
- else
- if (all_normal)
- if (tryCatch(stats::bartlett.test(var ~ grp)$p.value > .1, warning = function(e) F, error = function(e) F))
- ~. %>% oneway.test(var.equal = T)
- else
- ~. %>% oneway.test(var.equal = F)
- else
- ~kruskal.test
+tests_auto <- function(var, grp) {
+ grp <- factor(grp)
+
+ if (nlevels(grp) < 2) ~no.test
+ else if (is.factor(var)) {
+ if (tryCatch(is.numeric(fisher.test(var ~ grp)$p.value), error = function(e) F)) ~fisher.test
+ else ~chisq.test
+ } else {
+ all_normal <- all(tapply(var, grp, is.normal))
+
+ if (nlevels(grp) == 2) {
+ if (all_normal) {
+ if (tryCatch(stats::var.test(var ~ grp)$p.value > .1, warning = function(e) F, error = function(e) F)) ~. %>% t.test(var.equal = T)
+ else ~. %>% t.test(var.equal = F)
+ }
+ else ~wilcox.test
+ } else {
+ if (all_normal) {
+ if (tryCatch(stats::bartlett.test(var ~ grp)$p.value > .1, warning = function(e) F, error = function(e) F)) ~. %>% oneway.test(var.equal = T)
+ else ~. %>% oneway.test(var.equal = F)
+ }
+ else ~kruskal.test
+ }
}
}
diff --git a/R/utils.R b/R/utils.R
index ac954c6..d15360f 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -1,25 +1,47 @@
#' Insert a vector y inside another vector x at position
#'
-#' @param x A vector
-#' @param y A vector or list of vectors
+#' The vectors in the y list will be inserted
+#' at positions respectively *after* the x[position] element of x
+#'
+#' @param x A vector to be inserted into
+#' @param y A vector or list of vectors to insert into x
#' @param position The position / vector of positions to insert vector(s) y in vector x
#' @return The combined vector
-insert <- function(x, y, position)
-{
- if (! y %>% is.list())
- y <- list(y)
+insert <- function(x, y, position) {
+ # y is supposed to be a list of vectors. If it is a single vector, make it a simple list containing that vector
+ if (!is.list(y)) y <- list(y)
+ # Stop if there is not as many positions as vectors to insert
stopifnot(length(y) == length(position))
+ # Create an empty return vector that will contain the partition of x and the inserts
result <- vector("list", 2 * length(position) + 1)
+
+ # Split x in groups between the insert positions
old <- split(x, cumsum(seq_along(x) %in% (position + 1)))
+
+ # Insert the x splits at odd positions in result
result[seq(from = 1, by = 2, length.out = length(old))] <- old
+
+ # Insert the y inserts at even positions in results
result[c(F, T)] <- y
+ # Return a simple vector
unlist(result)
}
+#' Set the "desctable" class to the passed object
+#'
+#' @param x Object to set the "desctable" class to
+#' @return The object with the class "desctable"
+set_desctable_class <- function(x) {
+ class(x) <- "desctable"
+
+ x
+}
+
+
#' Parse a formula
#'
#' Parse a formula defining the conditions to pick a stat/test
@@ -35,33 +57,27 @@ insert <- function(x, y, position)
#' @param x The variable to test it on
#' @param f A formula to parse
#' @return A function to use as a stat/test
-parse_formula <- function(x, f)
-{
- parse_f <- function(x)
- {
- if (length(x) == 1)
- x %>% as.character()
- else
- {
- if (x[[1]] %>% as.character() == "~")
- {
- paste0("if (", x[[2]] %>% parse_f(), "(x)) ",
+parse_formula <- function(x, f) {
+ parse_f <- function(x) {
+ if (length(x) == 1) as.character(x)
+ else {
+ if (as.character(x[[1]]) == "~") {
+ paste0("if (", parse_f(x[[2]]), "(x)) ",
"{",
- x[[3]] %>% parse_f(),
+ parse_f(x[[3]]),
"}")
- } else if (x[[1]] %>% as.character() == "|")
- {
- paste0(x[[2]] %>% parse_f(),
+ } else if (as.character(x[[1]]) == "|") {
+ paste0(parse_f(x[[2]]),
"} else ",
"{",
- x[[3]] %>% parse_f())
- } else if (x[[1]] %>% as.character() == "(")
- {
- x[[2]] %>% parse_f()
+ parse_f(x[[3]]))
+ } else if (as.character(x[[1]]) == "(") {
+ parse_f(x[[2]])
}
}
}
- parse(text = parse_f(f)) %>% eval()
+
+ eval(parse(text = parse_f(f)))
}
@@ -69,15 +85,20 @@ parse_formula <- function(x, f)
#'
#' @param head A headerList object
#' @return A names vector
-head_pander <- function(head)
-{
- if (head[[1]] %>% is.integer())
- {
- head %>% names %>% lapply(function(x){c(x, rep("", head[[x]] - 1))}) %>% unlist
- } else
- {
- paste(head %>% names() %>% lapply(function(x){c(x, rep("", attr(head[[x]], "colspan") - 1))}) %>% unlist(),
- head %>% lapply(head_pander) %>% unlist(),
+head_pander <- function(head) {
+ if (is.integer(head[[1]])) {
+ head %>%
+ names %>%
+ lapply(function(x){c(x, rep("", head[[x]] - 1))}) %>%
+ unlist()
+ } else {
+ paste(head %>%
+ names() %>%
+ lapply(function(x){c(x, rep("", attr(head[[x]], "colspan") - 1))}) %>%
+ unlist(),
+ head %>%
+ lapply(head_pander) %>%
+ unlist(),
sep = "
")
}
}
@@ -87,17 +108,17 @@ head_pander <- function(head)
#'
#' @param head A headerList object
#' @return An htmltools$tags object containing the header
-head_datatable <- function(head)
-{
+head_datatable <- function(head) {
TRs <- list()
- while (head[[1]] %>% is.list())
- {
- TR <- purrr::map2(head %>% names(), head %>% lapply(attr, "colspan"), ~htmltools::tags$th(.x, colspan = .y))
+
+ while (is.list(head[[1]])) {
+ TR <- purrr::map2(names(head), lapply(head, attr, "colspan"), ~htmltools::tags$th(.x, colspan = .y))
TRs <- c(TRs, list(TR))
head <- purrr::flatten(head)
}
- c(TRs, list(purrr::map2(head %>% names, head, ~htmltools::tags$th(.x, colspan = .y))))
+
+ c(TRs, list(purrr::map2(names(head), head, ~htmltools::tags$th(.x, colspan = .y))))
}
@@ -105,15 +126,20 @@ head_datatable <- function(head)
#'
#' @param head A headerList object
#' @return A names vector
-head_dataframe <- function(head)
-{
- if (head[[1]] %>% is.integer())
- {
- head %>% names() %>% lapply(function(x){rep(x, head[[x]])}) %>% unlist()
- } else
- {
- paste(head %>% names() %>% lapply(function(x){rep(x, attr(head[[x]], "colspan"))}) %>% unlist(),
- head %>% lapply(head_pander) %>% unlist(),
+head_dataframe <- function(head) {
+ if (is.integer(head[[1]])) {
+ head %>%
+ names() %>%
+ lapply(function(x){rep(x, head[[x]])}) %>%
+ unlist()
+ } else {
+ paste(head %>%
+ names() %>%
+ lapply(function(x){rep(x, attr(head[[x]], "colspan"))}) %>%
+ unlist(),
+ head %>%
+ lapply(head_pander) %>%
+ unlist(),
sep = " / ")
}
}
@@ -127,40 +153,37 @@ head_dataframe <- function(head)
#' @param desctable A desctable object
#' @param output An output format for the header
#' @return A header object in the output format
-header <- function(desctable, output = c("pander", "datatable", "dataframe"))
-{
- nm <- desctable %>%
- `[`(-1) %>%
+header <- function(desctable, output = c("pander", "datatable", "dataframe")) {
+ desctable[-1] %>%
flatten_desctable() %>%
data.frame(check.names = F) %>%
- names
+ names() -> nm
desctable <- desctable[-1]
- if (length(desctable) == 1)
- {
- if (output == "datatable")
- c("\u00A0", nm) %>% lapply(htmltools::tags$th) %>% htmltools::tags$tr() %>% htmltools::tags$thead() %>% htmltools::tags$table(class = "display")
- else
- c("\u00A0", nm)
- }
- else
- {
+ if (length(desctable) == 1) {
+ if (output == "datatable") {
+ c("\u00A0", nm) %>%
+ lapply(htmltools::tags$th) %>%
+ htmltools::tags$tr() %>%
+ htmltools::tags$thead() %>%
+ htmltools::tags$table(class = "display")
+ } else c("\u00A0", nm)
+ } else {
head <- headerList(desctable)
- if (output == "pander")
- {
- c("\u00A0", head_pander(head) %>% paste(nm, sep = "
"))
- } else if (output == "datatable")
- {
- c(head_datatable(head), list(nm %>% lapply(htmltools::tags$th))) -> head
+ if (output == "pander") {
+ c("\u00A0", head_pander(head) %>%
+ paste(nm, sep = "
"))
+ } else if (output == "datatable") {
+ head <- c(head_datatable(head), list(nm %>% lapply(htmltools::tags$th)))
head[[1]] <- c(list(htmltools::tags$th(rowspan = length(head))), head[[1]])
+
head %>%
lapply(htmltools::tags$tr) %>%
htmltools::tags$thead() %>%
htmltools::tags$table(class = "display")
- } else if (output == "dataframe")
- {
+ } else if (output == "dataframe") {
c("\u00A0", head_dataframe(head) %>% paste(nm, sep = " / "))
}
}
@@ -171,19 +194,13 @@ header <- function(desctable, output = c("pander", "datatable", "dataframe"))
#'
#' @param desctable A desctable
#' @return A nested list of headers with colspans
-headerList <- function(desctable)
-{
- if (desctable %>% is.data.frame())
- {
- length(desctable)
- }
- else
- {
- lapply(desctable, headerList) -> rec
- if (is.integer(rec[[1]]))
- attr(rec, "colspan") <- rec %>% unlist() %>% sum()
- else
- attr(rec, "colspan") <- rec %>% lapply(attr, "colspan") %>% unlist %>% sum
+headerList <- function(desctable) {
+ if (is.data.frame(desctable)) length(desctable)
+ else {
+ rec <- lapply(desctable, headerList)
+
+ if (is.integer(rec[[1]])) attr(rec, "colspan") <- rec %>% unlist() %>% sum()
+ else attr(rec, "colspan") <- rec %>% lapply(attr, "colspan") %>% unlist() %>% sum()
rec
}
@@ -194,10 +211,11 @@ headerList <- function(desctable)
#'
#' @param desctable A desctable object
#' @return A flat dataframe
-flatten_desctable <- function(desctable)
-{
- if (desctable %>% is.data.frame())
- desctable
- else
- desctable %>% lapply(flatten_desctable) %>% dplyr::bind_cols()
+flatten_desctable <- function(desctable) {
+ if (is.data.frame(desctable)) desctable
+ else {
+ desctable %>%
+ lapply(flatten_desctable) %>%
+ Reduce(f = cbind)
+ }
}