################################################################################
###
### Project: R package risyphus
###
### PI/Contact: Fridtjof Thomas
###
### Purpose: Function to compile table (one group)
###
### Code: Fridtjof Thomas, 07/10/2018
### Last modified: Under GIT version control.
###
################################################################################
### History: >Short description of major changes to code, if applicable<
###
###
################################################################################
#
# You can learn more about package authoring with RStudio at:
#
# http://r-pkgs.had.co.nz/
#
# Some useful keyboard shortcuts for package authoring:
#
# Build and Reload Package: 'Ctrl + Shift + B'
# Check Package: 'Ctrl + Shift + E'
# Test Package: 'Ctrl + Shift + T'
#
# To build documentation, run
# > devtools::document()
#' Function to gather information about the ONE-table before computing begins
#'
#' \code{ONEtable.layout} is called by \code{\link{ONEtable}} to gather information about the table before computing begins
#'
#' @inheritParams ONEtable
#' @return Data frame containing layout information of the table to be computed
#' having these columns:
#' \describe{
#' \item{Variable, Table.label, Type, etc.}{Inherited from data set submitted as 'info'}
#' \item{n_rows}{Number of rows in the table for the corresponding variable}
#' \item{first_row}{First table row used for the corresponding variable}
#' \item{last_row}{Last table row used for the corresponding variable}
#' }
#' @examples
#' \dontrun{ Called by other function - not intended to be called by user directly. }
#' @export
ONEtable.layout <- function(data, info){
### NOTE: Uses factor levels to determine table
### - all dichotomous or factor-variables need to be R-factors!
table.info <- data.frame(info, n_rows=NA, first_row=NA, last_row=NA)
first_j <- 0 # Header information will be in colnames().
last_j <- 0
for (k in (1:length(table.info$Variable))){
if (table.info[k,"Type"] == "Continuous"){
table.info[k, "n_rows"] <- 1
first_j <- last_j + 1 # Last row from variable just before the current one.
last_j <- last_j + 1
table.info[k, "first_row"] <- first_j
table.info[k, "last_row"] <- last_j
} else if (table.info[k,"Type"] %in% c("Dichotomous", "Factor")){
this.variable <- table.info[k,"Variable"]
needed.rows <- length(levels(data[,this.variable])) + 1 # 1 for name and p-value.
table.info[k, "n_rows"] <- needed.rows
first_j <- last_j + 1 # Last row from variable just before the current one.
last_j <- last_j + needed.rows
table.info[k, "first_row"] <- first_j
table.info[k, "last_row"] <- last_j
}
}
return(table.info)
}
#' Function to compute and render text entries for ONE-table
#'
#' @param data Data used for the computations
#' @param this.var Variable in the table (must exist in \code{data})
#' @param this.type Variable type for table
#' @inheritParams ONEtable
#' @return Output used by \code{ONEtable()}
#' @examples
#' \dontrun{ Called by other function - not intended to be called by user directly. }
#' @export
ONEtable.text <- function(data, this.var, this.type,
sign.digits, sign.digits.prop){
this.data.All <- data[ , this.var] # Results in vector for a dataframe but in dataframe for a dplyr tibble.
if (this.type == "Continuous") {
list(
Col1 = paste(formatC(mean(this.data.All, na.rm=TRUE), digits=sign.digits, format="f"), " (",
formatC(stats::sd(this.data.All, na.rm=TRUE), digits=sign.digits, format="f"), ")", sep = "")
)
} else if ( (this.type %in% c("Dichotomous", "Factor")) ) {
this.table <- table(data[, this.var])
this.table.prop <- (prop.table(this.table)) * 100 # Relative counts in %.
table.combined <- matrix(
paste(as.numeric(this.table), " (", formatC(as.numeric(this.table.prop), digits=sign.digits.prop, format="f"), ")", sep=""),
nrow = dim(this.table)[1],
dimnames = dimnames(this.table))
list(
Col1 = table.combined
)
} else { # Serves to catch errors:
list(
Col1 = "??"
)
}
}
#' Function to compile ONE-table.
#'
#' @param data A data frame that contains the data (one row per participant).
#' @param info A data frame with specifications for the ONE-table. (See Examples below.)
#' @param sign.digits Digits used for mean, standard deviation, etc.
#' @param sign.digits.prop Digits used for proportion of cases for factors (and missing entries, if requested).
#' @param test.input Whether the input is tested for logical consistency. Warning: testing is not
#' comprehensive. Setting \code{test.input = FALSE} will bypass logical tests.
#' @param show.missing.info If \code{show.missing.info = TRUE} information about
#' missing information in a variable is added to the table. \code{show.missing.info = FALSE}
#' suppresses that information.
#' @param factor.level.bullet The character to be prepended to factor levels (listing). The default
#' is \code{factor.level.bullet = "- "} (hyphen/minus) but it can be reset to any character string. If the result is
#' passed on to html-rendering an en-dash can be produced by \code{factor.level.bullet = "– "} (HTML-code for the en-dash).
#' Other useful options for (subsequent) HTML-rendering: \code{factor.level.bullet = " "} forces non-breaking space and effectively
#' leads to indentation of factor levels; \code{factor.level.bullet = "» "} prints right double angle quotes in HTML.
#' @param linebreak.tag Can be used to insert a linebreak-command in the table headers to bring the counts to a new line.
#' The default is \code{linebreak.tag = ""} effectively not inserting any such command. For subsequent HTML-rendering
#' \code{linebreak.tag = "<br>"} (HTML tag to insert a single line break) is useful.
#' @return An R character-matrix containing the compiled table information.
#' @examples
#' ONEtable(data = example_data, info = example_variables, show.missing.info = TRUE)
#' @export
ONEtable <- function(data, info, sign.digits=2, sign.digits.prop=1,
show.missing.info = TRUE,
test.input = FALSE,
factor.level.bullet = "- ", linebreak.tag = ""){
# Convert tibbles to regular data frames without need to load package "tibbles":
# Note: a tibble tests TRUE for as.data.frame(tbl).
if ("tbl" %in% class(data)) { data <- as.data.frame(data) }
# Test input:
if (test.input) { }
### ADD TEST FOR FACTORS for all factors!
# Compute layout information:
this.layout <- ONEtable.layout(data, info)
# Set up the matrix that will hold the table information:
mytable <- matrix(data = " ", nrow = max(this.layout$last_row), ncol = 1) # Filled with space that will not 'show' in table.
colnames(mytable) <- c(
paste("All ", linebreak.tag,"(N = ", dim(data)[1], ")", sep="")
)
rownames(mytable) <- 1:dim(mytable)[1]
# Fill table with values variable by variable:
for (l in 1:length(this.layout$Variable)){
this.var <- this.layout$Variable[l]
if (show.missing.info){ # Add info about missing entries.
# Compile information about missing values.
n_recorded <- sum(!is.na(data[, this.var]))
n_missing <- sum(is.na(data[, this.var]))
percent_missing <- formatC(100*(n_missing/(n_missing + n_recorded)),
digits=sign.digits.prop, format = "f")
this.label <- paste(this.layout$Table.label[l],
" (", n_missing, " of ", n_missing + n_recorded, " missing; ",
formatC(percent_missing, digits=sign.digits.prop, format="f"), "%)", sep = "")
} else {
this.label <- this.layout$Table.label[l] # No info added.
}
this.type <- this.layout$Type[l]
this.n_rows <- this.layout$n_rows[l]
this.first_row <- this.layout$first_row[l]
this.last_row <- this.layout$last_row[l]
this.text <- ONEtable.text(data, this.var, this.type,
sign.digits, sign.digits.prop)
rownames(mytable)[this.first_row] <- this.label
if (this.n_rows == 1){ # All values go in a single row:
mytable[this.first_row, 1] <- this.text$Col1
} else { # More than one row (applies to factors only)
rownames(mytable)[(this.first_row+1):this.last_row] <-
paste(factor.level.bullet, rownames(this.text$Col1), sep = "") # rownames from table become names in extracted columns.
mytable[(this.first_row+1):this.last_row, 1] <- this.text$Col1
}
}
return(mytable)
}
#######################
##### End of file #####
#######################
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.