diff --git a/DESCRIPTION b/DESCRIPTION index 9797ef6..46a4e78 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,12 +1,12 @@ Package: tableone Type: Package Title: Create "Table 1" to describe baseline characteristics -Version: 0.2.1 -Date: 2014-02-15 +Version: 0.3.0 +Date: 2014-02-17 Author: Kazuki Yoshida, Justin Bohn Maintainer: Kazuki Yoshida Description: This package creates "Table 1", i.e., description of baseline - patient characteristics, which is essential every medical research. This + patient characteristics, which is essential in every medical research. This package provides functions to create such summaries for continuous and categorical variables, optionally with subgroups comparisons. The package was insipired by and based on descriptive statistics functions in Deducer, diff --git a/NEWS b/NEWS index 284c9ff..309f756 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,20 @@ +tableone 0.3.0 (2014-02-16) +---------------------------------------------------------------- +NEW FEATURES + +* CreateTableOne has a new factorVars argument, a character + vector specifying numerically coded variables that should be + treated as factors. + +* The print method for the TableOne/CatTable class object has a + new minMax argument, a logical value specifying whether to show + median [min, max] instead of median [IQR] for nonnormal variables + +BUG FIXES + +* Documentations were fixed to represent the current version. + + tableone 0.2.1 (2014-02-15) ---------------------------------------------------------------- BUG FIXES diff --git a/R/CreateCatTable.R b/R/CreateCatTable.R index d22fd05..1f30163 100644 --- a/R/CreateCatTable.R +++ b/R/CreateCatTable.R @@ -1,89 +1,81 @@ ##' Create an object summarizing categorical variables -##' +##' ##' Create an object summarizing categorical variables optionally stratifying ##' by one or more startifying variables and performing statistical tests. The ##' object gives a table that is easy to use in medical research papers. See ##' also \code{\link{print.CatTable}} and \code{\link{summary.CatTable}}. -##' +##' ##' @param vars Variable(s) to be summarized given as a character vector. -##' @param strata Stratifying (grouping) variable name(s) given as a character -##' vector. If omitted, the overall results are returned. -##' @param data A data frame in which these variables exist. All variables -##' (both vars and strata) must be in this data frame. -##' @param test If TRUE, as in the default and there are more than two groups, -##' groupwise comparisons are performed. Both tests that require the large -##' sample approximation and exact tests are performed. Either one of the -##' result can be obtained from the print method. -##' @param testApprox A function used to perform the large sample approximation -##' based tests. The default is \code{\link{chisq.test}}. This is not recommended when some -##' of the cell have small counts like fewer than 5. +##' @param strata Stratifying (grouping) variable name(s) given as a character vector. If omitted, the overall results are returned. +##' @param data A data frame in which these variables exist. All variables (both vars and strata) must be in this data frame. +##' @param test If TRUE, as in the default and there are more than two groups, groupwise comparisons are performed. Both tests that require the large sample approximation and exact tests are performed. Either one of the result can be obtained from the print method. +##' @param testApprox A function used to perform the large sample approximation based tests. The default is \code{\link{chisq.test}}. This is not recommended when some of the cell have small counts like fewer than 5. ##' @param argsApprox A named list of arguments passed to the function specified in testApprox. The default is \code{list(correct = TRUE)}, which turns on the continuity correction for \code{\link{chisq.test}}. -##' @param testExact A function used to perform the exact tests. The default is -##' fisher.test. If the cells have large numbers, it will fail because of -##' memory limitation. In this situation, the large sample approximation based -##' should suffice. +##' @param testExact A function used to perform the exact tests. The default is fisher.test. If the cells have large numbers, it will fail because of memory limitation. In this situation, the large sample approximation based should suffice. ##' @param argsExact A named list of arguments passed to the function specified in testExact. The default is \code{list(workspace = 2*10^5)}, which specifies the memory space allocated for \code{\link{fisher.test}}. -##' @return An object of class \code{CatTable}, which really is a \code{\link{by}} object with -##' additional attributes. Each element of the \code{\link{by}} part is a matrix with rows -##' representing variables, and columns representing summary statistics. +##' @return An object of class \code{CatTable}, which really is a \code{\link{by}} object with additional attributes. Each element of the \code{\link{by}} part is a matrix with rows representing variables, and columns representing summary statistics. ##' @author Kazuki Yoshida (based on \code{Deducer::frequencies()}) ##' @seealso ##' \code{\link{CreateCatTable}}, \code{\link{print.CatTable}}, \code{\link{summary.CatTable}}, ##' \code{\link{CreateContTable}}, \code{\link{print.ContTable}}, \code{\link{summary.ContTable}}, ##' \code{\link{CreateTableOne}}, \code{\link{print.TableOne}}, \code{\link{summary.TableOne}} ##' @examples -##' +##' ##' ## Load ##' library(tableone) -##' +##' ##' ## Load Mayo Clinic Primary Biliary Cirrhosis Data ##' library(survival) ##' data(pbc) ##' ## Check variables ##' head(pbc) -##' +##' ##' ## Create an overall table for categorical variables ##' catVars <- c("status","ascites","hepato","spiders","edema","stage") ##' catTableOverall <- CreateCatTable(vars = catVars, data = pbc) -##' +##' ##' ## Simply typing the object name will invoke the print.CatTable method, ##' ## which will show the sample size, frequencies and percentages. -##' ## For 2-level variables, only the higher level is shown for simplicity. +##' ## For 2-level variables, only the higher level is shown for simplicity +##' ## unless the variables are specified in the cramVars argument. ##' catTableOverall +##' +##' ## If you need to show both levels for some 2-level factors, use cramVars +##' print(catTableOverall, cramVars = "hepato") ##' ##' ## Use the showAllLevels argument to see all levels for all variables. ##' print(catTableOverall, showAllLevels = TRUE) -##' +##' ##' ## You can choose form frequencies ("f") and/or percentages ("p") or both. ##' ## "fp" frequency (percentage) is the default. Row names change accordingly. ##' print(catTableOverall, format = "f") ##' print(catTableOverall, format = "p") -##' +##' ##' ## To further examine the variables, use the summary.CatTable method, ##' ## which will show more details. ##' summary(catTableOverall) -##' +##' ##' ## The table can be stratified by one or more variables ##' catTableBySexTrt <- CreateCatTable(vars = catVars, ##' strata = c("sex","trt"), data = pbc) -##' +##' ##' ## print now includes p-values which are by default calculated by chisq.test. ##' ## It is formatted at the decimal place specified by the pDigits argument ##' ## (3 by default). It does <0.001 for you. ##' catTableBySexTrt -##' -##' ## The exact argument will toggle the p-values to the example test result from +##' +##' ## The exact argument toggles the p-values to the exact test result from ##' ## fisher.test. It will show which ones are from exact tests. ##' print(catTableBySexTrt, exact = "ascites") -##' +##' ##' ## summary now includes both types of p-values ##' summary(catTableBySexTrt) -##' +##' ##' ## If your work flow includes copying to Excel and Word when writing manuscripts, ##' ## you may benefit from the quote argument. This will quote everything so that ##' ## Excel does not mess up the cells. ##' print(catTableBySexTrt, exact = "ascites", quote = TRUE) -##' +##' ##' @export CreateCatTable <- function(vars, # character vector of variable names @@ -99,33 +91,33 @@ CreateCatTable <- ### Data check ## Check if the data given is a dataframe ModuleStopIfNotDataFrame(data) - + ## Check if variables exist. Drop them if not. vars <- ModuleReturnVarsExist(vars, data) ## Abort if no variables exist at this point ModuleStopIfNoVarsLeft(vars) - + ## Extract necessary variables (unused variables are not included in dat) dat <- data[c(vars)] + ## Toggle test FALSE if no strata + test <- ModuleReturnFalseIfNoStrata(strata, test) + ## Convert to a factor if it is not a factor already. (categorical version only) ## Not done on factors, to avoid dropping zero levels. datNotFactor <- sapply(dat, class) != "factor" dat[datNotFactor] <- lapply(dat[datNotFactor], factor) - ## Toggle test FALSE if no strata - test <- ModuleReturnFalseIfNoStrata(strata, test) - ## Create strata data frame (data frame with only strata variables) strata <- ModuleReturnStrata(strata, data, dat) -### Perform descriptive analysis +### Actual descriptive statistics are calculated here. ## strata--variable-CreateTableForOneVar structure ## Devide by strata - result <- by(data = dat, INDICES = strata, + result <- by(data = dat, INDICES = strata, # INDICES can be a multi-column data frame ## Work on each stratum FUN = function(dfStrataDat) { # dfStrataDat should be a data frame @@ -137,8 +129,8 @@ CreateCatTable <- }, simplify = FALSE) - - ## Add stratification information to the column header + + ## Add stratification variable information as an attribute if (length(result) > 1 ) { ## strataVarName from dimension headers strataVarName <- ModuleCreateStrataVarName(result) @@ -146,36 +138,27 @@ CreateCatTable <- attributes(result) <- c(attributes(result), list(strataVarName = strataVarName)) } - + ### Perform tests when necessary ## Initialize - pValues <- NULL + pValues <- NULL listXtabs <- list() - ## Only when test is asked for # Should always do this? + ## Only when test is asked for if (test == TRUE) { - ## Create all combinations of strata levels and collapse as a vector for level combinations. - dfStrataLevels <- expand.grid(attr(result, "dimnames")) # 1st var cycles fastest, consistent with by() - ## Create a single variable representing all strata - strataLevels <- apply(X = dfStrataLevels, - MARGIN = 1, - FUN = paste0, collapse = ":") - ## Create the actual variable from the observed levels - strataVar <- as.character(interaction(strata, sep = ":")) - - - ## Make it a factor (kruskal.test requires it). Use levels not to drop defined nonexisting levels. - strataVar <- factor(strataVar, levels = strataLevels) - + ## Create a single variable representation of multivariable stratification + strataVar <- ModuleCreateStrataVarAsFactor(result, strata) + ## Loop over variables in dat, and create a list of xtabs + ## Empty strata are kept in the corss tables. Different behavior than the cont counterpart! listXtabs <- sapply(X = names(dat), FUN = function(var) { ## Create a formula formula <- paste0("~ ", var, " + ", "strataVar") formula <- as.formula(formula) - + ## Create a 2-dimensional crosstable xtabs(formula = formula, data = dat) }, @@ -183,10 +166,10 @@ CreateCatTable <- ## Rename the second dimension of the xtabs with the newly create name. for (i in seq_along(listXtabs)) { - + names(dimnames(listXtabs[[i]]))[2] <- strataVarName - } - + } + ## Loop over xtabs, and create p-values pValues <- sapply(X = listXtabs, FUN = function(xtabs) { @@ -196,7 +179,7 @@ CreateCatTable <- pExact = ModuleTestSafe(xtabs, testExact, argsExact) ) }, - simplify = FALSE) + simplify = FALSE) ## Create a single data frame (n x 2 (normal,nonormal)) pValues <- do.call(rbind, pValues) diff --git a/R/CreateContTable.R b/R/CreateContTable.R index 9564427..8bb32f1 100644 --- a/R/CreateContTable.R +++ b/R/CreateContTable.R @@ -6,33 +6,16 @@ ##' also \code{\link{print.ContTable}} and \code{\link{summary.ContTable}}. ##' ##' @param vars Variable(s) to be summarized given as a character vector. -##' @param strata Stratifying (grouping) variable name(s) given as a character -##' vector. If omitted, the overall results are returned. -##' @param data A data frame in which these variables exist. All variables -##' (both vars and strata) must be in this data frame. -##' @param func.names The functions to give the group size, number with missing -##' values, mean, standard deviations, median, 25th percentile, 75th -##' percentile, minimum, maximum, skewness (same definition as in SAS), -##' kurtosis (same definition as in SAS). All of them can be seen in the -##' summary method output. The print method uses subset of these. You can -##' choose subset of them or reorder them. They are all configure to omit NA -##' values (\code{na.rm = TRUE}). -##' @param func.additional Additional functions can be given as a named list. For example, \code{list(sum = sum)}. -##' @param test If TRUE, as in the default and there are more than two groups, -##' groupwise comparisons are performed. Both tests that assume normality and -##' tests that do not are performed. Either one of the result can be obtained -##' from the print method. -##' @param testNormal A function used to perform the normal assumption based -##' tests. The default is \code{\link{oneway.test}}. This is equivalent of the t-test when there are only two groups. +##' @param strata Stratifying (grouping) variable name(s) given as a character vector. If omitted, the overall results are returned. +##' @param data A data frame in which these variables exist. All variables (both vars and strata) must be in this data frame. +##' @param funcNames The functions to give the group size, number with missing values, proportion with missing values, mean, standard deviations, median, 25th percentile, 75th percentile, minimum, maximum, skewness (same definition as in SAS), kurtosis (same definition as in SAS). All of them can be seen in the summary method output. The print method uses subset of these. You can choose subset of them or reorder them. They are all configure to omit NA values (\code{na.rm = TRUE}). +##' @param funcAdditional Additional functions can be given as a named list. For example, \code{list(sum = sum)}. +##' @param test If TRUE, as in the default and there are more than two groups, groupwise comparisons are performed. Both tests that assume normality and tests that do not are performed. Either one of the result can be obtained from the print method. +##' @param testNormal A function used to perform the normal assumption based tests. The default is \code{\link{oneway.test}}. This is equivalent of the t-test when there are only two groups. ##' @param argsNormal A named list of arguments passed to the function specified in \code{testNormal}. The default is \code{list(var.equal = TRUE)}, which makes it the ordinary ANOVA that assumes equal variance across groups. -##' @param testNonNormal A function used to perform the nonparametric tests. -##' The default is \code{kruskal.test} (Kruskal-Wallis rank sum test). This is -##' equivalent of the wilcox.test (Man-Whitney U test) when there are only two -##' groups. -##' @param argsNonNormal A named list of arguments passed to the function specified in \code{testNonNormal}. The default is \code{list(NULL)}, which is just a placeholder. -##' @return An object of class \code{ContTable}, which really is a \code{\link{by}} object with -##' additional attributes. Each element of the \code{\link{by}} part is a matrix with rows -##' representing variables, and columns representing summary statistics. +##' @param testNonNormal A function used to perform the nonparametric tests. The default is \code{kruskal.test} (Kruskal-Wallis rank sum test). This is equivalent of the wilcox.test (Man-Whitney U test) when there are only two groups. +##' @param argsNonNormal A named list of arguments passed to the function specified in \code{testNonNormal}. The default is \code{list(NULL)}, which is just a placeholder. +##' @return An object of class \code{ContTable}, which really is a \code{\link{by}} object with additional attributes. Each element of the \code{\link{by}} part is a matrix with rows representing variables, and columns representing summary statistics. ##' @author Kazuki Yoshida (based on \code{Deducer::descriptive.table()}) ##' @seealso ##' \code{\link{CreateContTable}}, \code{\link{print.ContTable}}, \code{\link{summary.ContTable}}, @@ -68,6 +51,9 @@ ##' nonNormalVars <- c("age","chol","copper","alk.phos","trig","protime") ##' print(contTableOverall, nonnormal = nonNormalVars) ##' +##' ## To show median [min,max] for nonnormal variables, use minMax = TRUE +##' print(contTableOverall, nonnormal = nonNormalVars, minMax = TRUE) +##' ##' ## The table can be stratified by one or more variables ##' contTableBySexTrt <- CreateContTable(vars = contVars, ##' strata = c("sex","trt"), data = pbc) @@ -77,7 +63,7 @@ ##' ## by the pDigits argument (3 by default). It does <0.001 for you. ##' contTableBySexTrt ##' -##' ## The nonnormal argument will toggle the p-values to the nonparametric result from +##' ## The nonnormal argument toggles the p-values to the nonparametric result from ##' ## kruskal.test (wilcox.test equivalent for the two group case). ##' print(contTableBySexTrt, nonnormal = nonNormalVars) ##' @@ -91,21 +77,21 @@ ##' ##' @export CreateContTable <- - function(vars, # character vector of variable names - strata, # character vector of variable names - data, # data frame - func.names = c( # can pick a subset of them - "n","miss", + function(vars, # character vector of variable names + strata, # character vector of variable names + data, # data frame + funcNames = c( # can pick a subset of them + "n","miss","p.miss", "mean","sd", "median","p25","p75","min","max", "skew","kurt" ), - func.additional, # named list of additional functions - test = TRUE, # Whether to put p-values - testNormal = oneway.test, # test for normally distributed variables - argsNormal = list(var.equal = TRUE), # arguments passed to testNormal - testNonNormal = kruskal.test, # test for nonnormally distributed variables - argsNonNormal = list(NULL) # arguments passed to testNonNormal + funcAdditional, # named list of additional functions + test = TRUE, # Whether to put p-values + testNormal = oneway.test, # test for normally distributed variables + argsNormal = list(var.equal = TRUE), # arguments passed to testNormal + testNonNormal = kruskal.test, # test for nonnormally distributed variables + argsNonNormal = list(NULL) # arguments passed to testNonNormal ) { ## Require dependencies (DELETE before CRAN release. Use Depends in DESCRIPTION) @@ -142,49 +128,53 @@ CreateContTable <- if(!all(sapply(dat, is.numeric))) {stop("Can only be run on numeric variables")} - ## Create indexes for default functions by partial string matching with the func.names argument - func.indexes <- pmatch(func.names, c("n","miss", - "mean","sd", - "median","p25","p75","min","max", - "skew","kurt")) + ## Create indexes for default functions by partial string matching with the funcNames argument + funcIndexes <- pmatch(funcNames, c("n","miss","p.miss", + "mean","sd", + "median","p25","p75","min","max", + "skew","kurt")) ## Remove NA - func.indexes <- func.indexes[!is.na(func.indexes)] + funcIndexes <- funcIndexes[!is.na(funcIndexes)] ## Create a list of default functions - functions <- c("n" = function(x) length(x), - "miss" = function(x) sum(is.na(x)), - "mean" = function(x) mean(x, na.rm = TRUE), - "sd" = function(x) sd(x, na.rm = TRUE), - "median" = function(x) median(x, na.rm = TRUE), - "p25" = function(x) quantile(x, probs = 0.25, na.rm = TRUE), - "p75" = function(x) quantile(x, probs = 0.75, na.rm = TRUE), - "min" = function(x) min(x, na.rm = TRUE), - "max" = function(x) max(x, na.rm = TRUE), - "skew" = function(x) sasSkewness(x), - "kurt" = function(x) sasKurtosis(x) + functions <- c("n" = function(x) {length(x)}, + "miss" = function(x) {sum(is.na(x))}, + "p.miss" = function(x) {(sum(is.na(x)) / length(x)) * 100}, + "mean" = function(x) {mean(x, na.rm = TRUE)}, + "sd" = function(x) {sd(x, na.rm = TRUE)}, + "median" = function(x) {median(x, na.rm = TRUE)}, + "p25" = function(x) {quantile(x, probs = 0.25, na.rm = TRUE)}, + "p75" = function(x) {quantile(x, probs = 0.75, na.rm = TRUE)}, + "min" = function(x) {min(x, na.rm = TRUE)}, + "max" = function(x) {max(x, na.rm = TRUE)}, + "skew" = function(x) {ModuleSasSkewness(x)}, + "kurt" = function(x) {ModuleSasKurtosis(x)} ) ## Keep only functions in use - functions <- functions[func.indexes] + functions <- functions[funcIndexes] ## Check for additional functions - if(!missing(func.additional)){ + if(!missing(funcAdditional)) { + ## When additional functions are given - if(!is.list(func.additional) || is.null(names(func.additional))) { + if(!is.list(funcAdditional) || is.null(names(funcAdditional))) { ## Stop if not a named list - stop("func.additional must be a named list of functions") + stop("funcAdditional must be a named list of functions") } ## If a named list is given, add to the vector of functions and their names - functions <- c(functions, unlist(func.additional)) - func.names <- c(func.names, names(func.additional)) + functions <- c(functions, unlist(funcAdditional)) + funcNames <- c(funcNames, names(funcAdditional)) } ### Actual descriptive statistics are calculated here. ## strata-functions-variable structure alternative 2014-01-22 ## Devide by strata - result <- by(data = dat, INDICES = strata, # INDICES can be a multi-column data frame + result <- by(data = dat, INDICES = strata, # INDICES can be a multi-column data frame + + ## Work on each stratum FUN = function(strataDat) { # Work on each stratum through by() ## Loop for functions @@ -201,7 +191,7 @@ CreateContTable <- do.call(cbind, out) }) - ## Add stratification information to the column header + ## Add stratification variable information as an attribute if (length(result) > 1 ) { ## strataVarName from dimension headers strataVarName <- ModuleCreateStrataVarName(result) @@ -215,34 +205,29 @@ CreateContTable <- ## Initialize to avoid error when it does not exist at the attribute assignment pValues <- NULL -### This part performs between group tests + ## Only when test is asked for if (test == TRUE) { - ## Create a single variable representing all strata - strataVec <- apply(X = strata, MARGIN = 1, FUN = paste0, collapse = ":") - ## Give NA if any of the variables are missing - strataVecAnyMiss <- apply(X = is.na(strata), MARGIN = 1, FUN = sum) > 0 - strataVec[strataVecAnyMiss] <- NA - ## Make it a factor (kruskal.test requires it) - strataVec <- factor(strataVec) - + ## Create a single variable representation of multivariable stratification + strataVar <- ModuleCreateStrataVarAsFactor(result, strata) ## Loop over variables in dat, and obtain p values for two tests + ## DF = 6 when there are 8 levels (one empty), i.e., empty strata dropped by oneway.test/kruskal.test pValues <- sapply(X = dat, FUN = function(var) { ## Perform tests and return the result as 1x2 DF data.frame( - pNormal = ModuleTestSafe(var ~ strataVec, testNormal, argsNormal), - pNonNormal = ModuleTestSafe(var ~ strataVec, testNonNormal, argsNonNormal) + pNormal = ModuleTestSafe(var ~ strataVar, testNormal, argsNormal), + pNonNormal = ModuleTestSafe(var ~ strataVar, testNonNormal, argsNonNormal) ) }, simplify = FALSE) ## Create a single data frame (n x 2 (normal,nonormal)) pValues <- do.call(rbind, pValues) - } + } # Conditional for test == TRUE ends here. ## Return object diff --git a/R/CreateTableOne.R b/R/CreateTableOne.R index 771ee11..fa35c98 100644 --- a/R/CreateTableOne.R +++ b/R/CreateTableOne.R @@ -2,30 +2,18 @@ ##' ##' Create an object summarizing all baseline variables optionally stratifying by one or more startifying variables and performing statistical tests. The object gives a table that is easy to use in medical research papers. See also \code{\link{print.TableOne}} and \code{\link{summary.TableOne}}. ##' -##' @param vars Variables to be summarized given as a character vector. Factors are -##' handled as categorical variables, whereas numeric variables are handled as continuous variables. -##' @param strata Stratifying (grouping) variable name(s) given as a character -##' vector. If omitted, the overall results are returned. -##' @param data A data frame in which these variables exist. All variables -##' (both vars and strata) must be in this data frame. -##' @param test If TRUE, as in the default and there are more than two groups, -##' groupwise comparisons are performed. -##' @param testNormal A function used to perform the normal assumption based -##' tests. The default is \code{\link{oneway.test}}. This is equivalent of the t-test when there are only two groups. +##' @param vars Variables to be summarized given as a character vector. Factors are handled as categorical variables, whereas numeric variables are handled as continuous variables. +##' @param strata Stratifying (grouping) variable name(s) given as a character vector. If omitted, the overall results are returned. +##' @param data A data frame in which these variables exist. All variables (both vars and strata) must be in this data frame. +##' @param factorVars Numerically coded variables that should be handled as categorical variables given as a character vector. If omitted, only factors are considered categorical variables. If all categorical variables in the dataset are already factors, this option is not necessary. +##' @param test If TRUE, as in the default and there are more than two groups, groupwise comparisons are performed. +##' @param testNormal A function used to perform the normal assumption based tests. The default is \code{\link{oneway.test}}. This is equivalent of the t-test when there are only two groups. ##' @param argsNormal A named list of arguments passed to the function specified in \code{testNormal}. The default is \code{list(var.equal = TRUE)}, which makes it the ordinary ANOVA that assumes equal variance across groups. -##' @param testNonNormal A function used to perform the nonparametric tests. -##' The default is \code{kruskal.test} (Kruskal-Wallis Rank Sum Test). This is -##' equivalent of the wilcox.test (Man-Whitney U test) when there are only two -##' groups. +##' @param testNonNormal A function used to perform the nonparametric tests. The default is \code{kruskal.test} (Kruskal-Wallis Rank Sum Test). This is equivalent of the wilcox.test (Man-Whitney U test) when there are only two groups. ##' @param argsNonNormal A named list of arguments passed to the function specified in \code{testNonNormal}. The default is \code{list(NULL)}, which is just a placeholder. -##' @param testApprox A function used to perform the large sample approximation -##' based tests. The default is \code{\link{chisq.test}}. This is not recommended when some -##' of the cell have small counts like fewer than 5. +##' @param testApprox A function used to perform the large sample approximation based tests. The default is \code{\link{chisq.test}}. This is not recommended when some of the cell have small counts like fewer than 5. ##' @param argsApprox A named list of arguments passed to the function specified in testApprox. The default is \code{list(correct = TRUE)}, which turns on the continuity correction for \code{\link{chisq.test}}. -##' @param testExact A function used to perform the exact tests. The default is -##' fisher.test. If the cells have large numbers, it will fail because of -##' memory limitation. In this situation, the large sample approximation based -##' should suffice. +##' @param testExact A function used to perform the exact tests. The default is fisher.test. If the cells have large numbers, it will fail because of memory limitation. In this situation, the large sample approximation based should suffice. ##' @param argsExact A named list of arguments passed to the function specified in testExact. The default is \code{list(workspace = 2*10^5)}, which specifies the memory space allocated for \code{\link{fisher.test}}. ##' @return An object of class \code{TableOne}, which really is a list of three objects. ##' @return \item{TableOne}{a categorical-continuous mixture data formatted and printed by the \code{\link{print.TableOne}} method} @@ -94,6 +82,7 @@ CreateTableOne <- function(vars, # character vector of variable names strata, # character vector of variable names data, # data frame + factorVars, # variables to be transformed to factors test = TRUE, # whether to put p-values ## Test configuration for categorical data testApprox = chisq.test, # function for approximation test @@ -117,6 +106,14 @@ CreateTableOne <- ## Abort if no variables exist at this point ModuleStopIfNoVarsLeft(vars) + ## Factor conversions if the factorVars argument exist + if (!missing(factorVars)) { + ## Check if variables exist. Drop them if not. + factorVars <- ModuleReturnVarsExist(factorVars, data) + ## Convert to factor + data[factorVars] <- lapply(data[factorVars], factor) + } + ## Toggle test FALSE if no strata is given test <- ModuleReturnFalseIfNoStrata(strata, test) @@ -136,13 +133,13 @@ CreateTableOne <- testNonNormal = testNonNormal, argsNonNormal = argsNonNormal ) - argsCreateCatTable <- list(data = data, - test = test, - testApprox = testApprox, - argsApprox = argsApprox, - testExact = testExact, - argsExact = argsExact - ) + argsCreateCatTable <- list(data = data, + test = test, + testApprox = testApprox, + argsApprox = argsApprox, + testExact = testExact, + argsExact = argsExact + ) ## Add strata = strata for argument only if strata is given if(!missing(strata)) { @@ -175,12 +172,12 @@ CreateTableOne <- ## CreateCatTable for categorical. CreateContTable for continuous. listOfConstructors <- listOfConstructors[logiFactors + 1] ## Create a list of arguments - listOfArgs <- list(argsCreateContTable = argsCreateContTable, - argsCreateCatTable = argsCreateCatTable) + listOfArgs <- list(argsCreateContTable = argsCreateContTable, + argsCreateCatTable = argsCreateCatTable) ## argsCreateCatTable for categorical. argsCreateContTable for continuous. - listOfArgs <- listOfArgs[logiFactors + 1] + listOfArgs <- listOfArgs[logiFactors + 1] - ## Create a list of tables + ## Create a list of tables by looping over variables/constructors/arguments TableOne <- sapply(seq_along(listOfConstructors), FUN = function(i) { @@ -192,11 +189,11 @@ CreateTableOne <- }, simplify = FALSE) - ## Give variable names + ## Give variable names to the result object names(TableOne) <- vars - ## Create ContTable and CatTable objects (this is redundant) + ## Create ContTable and CatTable objects (this is redundant, but easy) ## Aggregated ContTable ContTable <- do.call(CreateContTable, args = c(list(vars = varNumerics), argsCreateContTable)) @@ -204,16 +201,16 @@ CreateTableOne <- CatTable <- do.call(CreateCatTable, args = c(list(vars = varFactors), argsCreateCatTable)) - ## Create a list - listOfTables <- list(TableOne = TableOne, - ContTable = ContTable, - CatTable = CatTable - ) + ## Create a list for output + TableOneObject <- list(TableOne = TableOne, + ContTable = ContTable, + CatTable = CatTable + ) ## Give a class - class(listOfTables) <- "TableOne" + class(TableOneObject) <- "TableOne" ## Return the object - return(listOfTables) + return(TableOneObject) } } diff --git a/R/modules.R b/R/modules.R index 906d9b1..79afb72 100644 --- a/R/modules.R +++ b/R/modules.R @@ -7,18 +7,20 @@ ### Modules intended for the constructors ################################################################################ +## Check if the data given is a data frame ModuleStopIfNotDataFrame <- function(data) { if (is.data.frame(data) == FALSE) { stop("The data argument needs to be a data frame (no quote).") } } + ## Extract variables that exist in the data frame ModuleReturnVarsExist <- function(vars, data) { - + ## Check if variables exist. Drop them if not. varsNotInData <- setdiff(vars, names(data)) - + if (length(varsNotInData) > 0) { warning("The data frame does not have ", paste0(varsNotInData, sep = " "), " Dropped") @@ -28,11 +30,13 @@ ModuleReturnVarsExist <- function(vars, data) { ## Return existing variables return(vars) } + ## Stop if not vars are left ModuleStopIfNoVarsLeft <- function(vars) { if (length(vars) < 1) {stop("No valid variables.")} } -## + +## Toggle test FALSE if no strata are given ModuleReturnFalseIfNoStrata <- function(strata, test) { # Give strata variable names if(missing(strata)) { @@ -41,9 +45,10 @@ ModuleReturnFalseIfNoStrata <- function(strata, test) { # Give strata variable n } return(test) } -## Check statra variables and conditionally create + +## Check statra variables and conditionally create strata data frame ModuleReturnStrata <- function(strata, data, dat) { # Give strata variable names - + if(missing(strata)) { ## If there is no strata, give "Overall" to every subject strata <- rep("Overall", dim(dat)[1]) # Check if dim(dat)[[1]] is correct. @@ -65,6 +70,7 @@ ModuleReturnStrata <- function(strata, data, dat) { # Give strata variable n ## return DF with strata variable(s) return(strata) } + ## Module to create a table for one categorical variable ## Taken from Deducer::frequencies() ModuleCreateTableForOneVar <- function(x) { # Give a vector @@ -81,6 +87,9 @@ ModuleCreateTableForOneVar <- function(x) { # Give a vector ## Total missing n (duplicated as many times as there are levels) freq$miss <- sum(is.na(x)) + ## Total missing percentage + freq$p.miss <- (freq$miss / freq$n) * 100 + ## Category frequency freq$freq <- freqRaw @@ -91,13 +100,13 @@ ModuleCreateTableForOneVar <- function(x) { # Give a vector freq$cum.percent <- cumsum(freqRaw) / sum(freqRaw) * 100 ## Reorder variables - freq <- freq[c("n","miss","level","freq","percent","cum.percent")] + freq <- freq[c("n","miss","p.miss","level","freq","percent","cum.percent")] ## Return result as a data frame return(freq) } -## Create StrataVarName from multiple dimension headers +## Create StrataVarName from multiple dimension headers, for example sex:trt ModuleCreateStrataVarName <- function(obj) { ## Combine variable names with : in between paste0(names(attr(obj, "dimnames")), collapse = ":") @@ -105,7 +114,7 @@ ModuleCreateStrataVarName <- function(obj) { ## Try catch function # Taken from demo(error.catching) ## Used to define non-failing functions, that return NA when there is an error -tryCatch.W.E <- function(expr) { +ModuleTryCatchWE <- function(expr) { W <- NULL w.handler <- function(w) { # warning handler W <<- w @@ -117,32 +126,197 @@ tryCatch.W.E <- function(expr) { } ## Function to perform non-failing tests (obj should be xtabs or formula) -## Function has to have $p.value element -## Consider additional options by do.call() ModuleTestSafe <- function(obj, testFunction, testArgs = NULL) { - - out <- tryCatch.W.E(do.call(testFunction, args = c(list(obj), testArgs))$p.value) + ## Result from a function has to have $p.value element + out <- ModuleTryCatchWE(do.call(testFunction, args = c(list(obj), testArgs))$p.value) ## If it returns a numeric value, return it. Otherwise, return NA. ifelse(is.numeric(out$value), out$value, NA) } ## Define special skewness and kurtosis functions that do not fail (SAS definitions) -sasSkewness <- function(x) { - out <- tryCatch.W.E(e1071::skewness(x, na.rm = TRUE, type = 2)) +ModuleSasSkewness <- function(x) { + out <- ModuleTryCatchWE(e1071::skewness(x, na.rm = TRUE, type = 2)) ## If it returns a numeric value, return it. Otherwise, return NaN. ifelse(is.numeric(out$value), out$value, NaN) } -sasKurtosis <- function(x) { - out <- tryCatch.W.E(e1071::kurtosis(x, na.rm = TRUE, type = 2)) +ModuleSasKurtosis <- function(x) { + out <- ModuleTryCatchWE(e1071::kurtosis(x, na.rm = TRUE, type = 2)) ## If it returns a numeric value, return it. Otherwise, return NaN. ifelse(is.numeric(out$value), out$value, NaN) } +## Create a single variable representation of multivariable stratification for individuals +## result: by object; strata: data frame of stratifying variable(s) +ModuleCreateStrataVarAsFactor <- function(result, strata) { + + ## Create all possible combinations of strata levels and collapse as a vector. + dfStrataLevels <- expand.grid(attr(result, "dimnames")) # 1st var cycles fastest, consistent with by() + ## Create a single variable representing all strata + strataLevels <- apply(X = dfStrataLevels, MARGIN = 1, FUN = paste0, collapse = ":") + ## The length is the number of potential combinations. Used for the levels argument in the next part. + + ## Create the actual variable from the observed levels. NA if any one of the variables is NA. + strataVar <- as.character(interaction(strata, sep = ":")) + ## Make it a factor (kruskal.test requires it). Use levels not to drop defined nonexisting levels. + strataVar <- factor(strataVar, levels = strataLevels) + + ## Return stratifying variable. The length is the number of observations in the dataset. + ## NA for subjects with NA for any of the stratifying variables. + return(strataVar) +} + + + ### Modules intented for the print methods ################################################################################ -### ModuleQuoteAndPrintMat() +## Define a function to format a normal variable +ModuleConvertNormal <- function(rowMat, digits) { + + ## Format for SD + fmt <- paste0(" (%.", digits,"f",")") + + ## Create a DF with numeric mean column and character (SD) column + data.frame(col1 = rowMat[,"mean"], + col2 = sprintf(fmt = fmt, rowMat[,"sd"]), + stringsAsFactors = FALSE) +} + +## Define a function to format a nonnormal variable +ModuleConvertNonNormal <- function(rowMat, digits, minMax = FALSE) { + + ## Format for [p25, p75] + fmt <- paste0(" [%.", digits,"f, %.",digits,"f]") + + if (minMax == FALSE) { + ## Create a DF with numeric median column and character [p25, p75] column + out <- data.frame(col1 = rowMat[,"median"], + col2 = sprintf(fmt = fmt, rowMat[,"p25"], rowMat[,"p75"]), + stringsAsFactors = FALSE) + } else if (minMax == TRUE) { + ## Create a DF with numeric median column and character [p25, p75] column + out <- data.frame(col1 = rowMat[,"median"], + col2 = sprintf(fmt = fmt, rowMat[,"min"], rowMat[,"max"]), + stringsAsFactors = FALSE) + } else { + stop("minMax must be a logical vector of one: FALSE or TRUE") + } + + return(out) +} + + +## Module to handle TRUE/FALSE or character vector of variable names +## Returns a numeric vector: 1 for default action variable; 2 for alternative action variable +ModuleHandleDefaultOrAlternative <- function(switchVec, nameOfSwitchVec, varNames) { + + ## Check the number of variables + nVars <- length(varNames) + + ## If null, do default print/test for all variables + if (is.null(switchVec)) { + ## Give one as many as there are variables + switchVec <- rep(1, nVars) + + } else { + ## If not null, it needs checking. + + ## Check the switchVec argument + if (!is.logical(switchVec) & !is.character(switchVec)) { + stop(paste0(nameOfSwitchVec, " argument has to be FALSE/TRUE or a character vector of variable names.")) + } + ## Extend if it is a logitcal vector with one element. + if (is.logical(switchVec)) { + + if (length(switchVec) != 1) { + stop(paste0(nameOfSwitchVec, " has to be a logical vector of length 1")) + } + + switchVec <- rep(switchVec, nVars) + } + ## Convert to a logical vector if it is a character vector + if (is.character(switchVec)) { + switchVec <- varNames %in% switchVec + } + ## Convert to numeric (1 for default action, 2 for alternative actions) + switchVec <- as.numeric(switchVec) + 1 + } + + return(switchVec) +} + + +## Column name formatter +ModuleCreateStrataNames <- function(TableObject) { + + ## Create all combinations and collapse as strings + strataNames <- apply(expand.grid(attr(TableObject, "dimnames")), + MARGIN = 1, + paste0, collapse = ":") + + ## Return the names as a vector + return(strataNames) +} + + +## p-value picker/formatter +ModulePickAndFormatPValues <- function(TableObject, switchVec, pDigits) { + + ## nVarsiables x 2 (pNormal,pNonNormal) data frame + pValues <- attr(TableObject, "pValues") + + ## Pick ones specified in exact (a vector with 1s(approx) and 2s(exact)) + pValues <- sapply(seq_along(switchVec), # loop over exact + FUN = function(i) { + ## Pick from a matrix i-th row, exact[i]-th column + ## Logical NA must be converted to a numeric + as.numeric(pValues[i, switchVec[i]]) + }, + simplify = TRUE) + + ## Format p value + fmt <- paste0("%.", pDigits, "f") + pVec <- sprintf(fmt = fmt, pValues) + + ## Create a string like <0.001 + smallPString <- paste0("<0.", paste0(rep("0", pDigits - 1), collapse = ""), "1") + ## Check positions where it is all zero like 0.000 + posAllZeros <- grepl("^0\\.0*$", pVec) + ## Put the string where it is all zero like 0.000 + pVec[posAllZeros] <- smallPString + ## Put a preceding space where it is not like 0.000 + pVec[!posAllZeros] <- paste0(" ", pVec[!posAllZeros]) + + ## Return formatted p-values (as many as there are variables) + return(pVec) +} + + +## Module to return the dimention headers added to the out 2d matrix +ModuleReturnDimHeaders <- function(TableObject) { + + ## Add stratification information to the column header + if (length(TableObject) > 1 ) { + ## Create strata string + strataString <- paste0("Stratified by ", + paste0(names(attr(TableObject, "dimnames")), collapse = ":")) + + ## Name the row dimension with it. 1st dimension name should be empty. + dimHeaders <- c("", strataString) + + } else { + ## If no stratification, no name for the second dimension + dimHeaders <- c("", "") + } + + ## Return the dim header a vector of length 2 + return(dimHeaders) +} + + +### Modules by both print and summary methods +## ModuleQuoteAndPrintMat() ## Takes an matrix object format, print, and (invisibly) return it ## Requires quote and printToggle argument in the printToggle method ModuleQuoteAndPrintMat <- function(matObj, quote = FALSE, printToggle = TRUE) { @@ -158,13 +332,12 @@ ModuleQuoteAndPrintMat <- function(matObj, quote = FALSE, printToggle = TRUE) { names(dimnames(matObj))[1] <- paste0(" ", names(dimnames(matObj))[1]) } - ## print if required and return if (printToggle) { - + print(matObj, quote = quote) return(matObj) - + } else if (!printToggle) { return(matObj) diff --git a/R/print.CatTable.R b/R/print.CatTable.R index 22de2ef..fd59144 100644 --- a/R/print.CatTable.R +++ b/R/print.CatTable.R @@ -3,34 +3,20 @@ ##' This is the \code{print} method for the \code{CatTable} class objects created by \code{\link{CreateCatTable}} function. ##' ##' @param x The result of a call to the \code{\link{CreateCatTable}} function. -##' @param missing Whether to show missing data information (not implemented -##' yet, placeholder) -##' @param format The default is "fp" frequency (percentage). You can also -##' choose from "f" frequency only, "p" percentage only, and "pf" percentage -##' (frequency). ##' @param digits Number of digits to print in the table. -##' @param exact A character vector to specify the variables for which the -##' p-values should be those of exact tests. By default all p-values are from -##' large sample approximation tests (chisq.test). -##' @param quote Whether to show everything in quotes. The default is FALSE. If -##' TRUE, everything including the row and column names are quoted so that you -##' can copy it to Excel easily. -##' @param test Whether to show the p-values. TRUE by default. If FALSE, only -##' the numerical summaries are shown. ##' @param pDigits Number of digits to print for p-values. -##' @param showAllLevels Whether to show all levels. FALSE by default, i.e., -##' for 2-level categorical variables, only the higher level is shown to avoid -##' @param explain Whether to add explanation to the variable names, i.e., (\%) -##' is added to the variable names when percentage is shown. -##' @param CrossTable Whether to show the cross table objects held internally -##' using gmodels::CrossTable function. This will give an output similar to the -##' PROC FREQ in SAS. -##' @param printToggle Whether to print the output. If FLASE, no output is -##' created, and a matrix is invisibly returned. +##' @param quote Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily. +##' @param missing Whether to show missing data information (not implemented yet, placeholder) +##' @param explain Whether to add explanation to the variable names, i.e., (\%) is added to the variable names when percentage is shown. +##' @param printToggle Whether to print the output. If FLASE, no output is created, and a matrix is invisibly returned. +##' @param format The default is "fp" frequency (percentage). You can also choose from "f" frequency only, "p" percentage only, and "pf" percentage (frequency). +##' @param showAllLevels Whether to show all levels. FALSE by default, i.e., for 2-level categorical variables, only the higher level is shown to avoid +##' @param cramVars A character vector to specify the two-level categorical variables, for which both levels should be shown in one row. +##' @param test Whether to show the p-values. TRUE by default. If FALSE, only the numerical summaries are shown. +##' @param exact A character vector to specify the variables for which the p-values should be those of exact tests. By default all p-values are from large sample approximation tests (chisq.test). +##' @param CrossTable Whether to show the cross table objects held internally using gmodels::CrossTable function. This will give an output similar to the PROC FREQ in SAS. ##' @param ... For compatibility with generic. Ignored. -##' @return It is mainly for printing the result. But this function does return -##' a matrix containing what you see in the output invisibly. You can assign it -##' to an object to save it. +##' @return It is mainly for printing the result. But this function does return a matrix containing what you see in the output invisibly. You can assign it to an object to save it. ##' @author Kazuki Yoshida ##' @seealso ##' \code{\link{CreateCatTable}}, \code{\link{print.CatTable}}, \code{\link{summary.CatTable}}, @@ -56,6 +42,9 @@ ##' ## For 2-level variables, only the higher level is shown for simplicity. ##' catTableOverall ##' +##' ## If you need to show both levels for some 2-level factors, use cramVars +##' print(catTableOverall, cramVars = "hepato") +##' ##' ## Use the showAllLevels argument to see all levels for all variables. ##' print(catTableOverall, showAllLevels = TRUE) ##' @@ -77,7 +66,7 @@ ##' ## (3 by default). It does <0.001 for you. ##' catTableBySexTrt ##' -##' ## The exact argument will toggle the p-values to the example test result from +##' ## The exact argument toggles the p-values to the exact test result from ##' ## fisher.test. It will show which ones are from exact tests. ##' print(catTableBySexTrt, exact = "ascites") ##' @@ -90,14 +79,23 @@ ##' print(catTableBySexTrt, exact = "ascites", quote = TRUE) ##' ##' @export -print.CatTable <- function(x, missing = FALSE, - format = c("fp","f","p","pf")[1], # Format f_requency and/or p_ercent - digits = 1, exact = NULL, quote = FALSE, - test = TRUE, pDigits = 3, +print.CatTable <- function(x, # CatTable object + digits = 1, pDigits = 3, # Number of digits to show + quote = FALSE, # Whether to show quotes + + missing = FALSE, # Show missing values (not implemented yet) + explain = TRUE, # Whether to show explanation in variable names + printToggle = TRUE, # Whether to print the result visibly + + format = c("fp","f","p","pf")[1], # Format f_requency and/or p_ercent showAllLevels = FALSE, - explain = TRUE, - CrossTable = FALSE, - printToggle = TRUE, + cramVars = NULL, # variables to be crammed into one row + + test = TRUE, # Whether to add p-values + exact = NULL, # Which variables should be tested with exact tests + + CrossTable = FALSE, # Whether to show gmodels::CrossTable + ...) { ## x and ... required to be consistent with generic print(x, ...) @@ -115,36 +113,14 @@ print.CatTable <- function(x, missing = FALSE, ## Save variable names using the first non-null element varNames <- names(CatTable[[posFirstNonNullElement]]) ## Check the number of variables (list length) - nVar <- length(varNames) - - ## If null, do approx - if (is.null(exact)) { - - exact <- rep(1, nVar) - - } else { - ## If not null, it needs checking. - - ## Check the exact argument - if (!is.logical(exact) & !is.character(exact)) { - stop("exact argument has to be FALSE/TRUE or character.") - } - ## Extend if it is a logitcal vector with one element. - if (is.logical(exact)) { - - if (length(exact) != 1) { - stop("exact has to be a logical vector of length 1") - } - - exact <- rep(exact, nVar) - } - ## Convert to a logical vector if it is a character vector - if (is.character(exact)) { - exact <- varNames %in% exact - } - ## Convert to numeric (1 for approx, 2 for exact) - exact <- as.numeric(exact) + 1 - } + nVars <- length(varNames) + + + ## Returns a numeric vector: 1 for approx test variable; 2 for exact test variable + exact <- ModuleHandleDefaultOrAlternative(switchVec = exact, + nameOfSwitchVec = "exact", + varNames = varNames) + ## Check format argument. If it is broken, choose "fp" for frequency (percent) if (!length(format) == 1 | !format %in% c("fp","f","p","pf")) { @@ -156,15 +132,15 @@ print.CatTable <- function(x, missing = FALSE, ## Added as the top row later strataN <- sapply(CatTable, FUN = function(stratum) { # loop over strata - ## Just the first available element may be enough. - ## Obtain n from all variables and all levels, and get the mean + ## each stratum is a list of one data frame for each variable + ## Obtain n from all variables and all levels (list of data frames) n <- unlist(sapply(stratum, getElement, "n")) ## Pick the first non-null element n[!is.null(n)][1] - ## Convert NULL to N + ## Convert NULL to 0 ifelse(is.null(n), "0", as.character(n)) }, - simplify = TRUE) + simplify = TRUE) # vector with as many elements as strata ## Provide indicators to show what columns were added. wasLevelColumnAdded <- FALSE @@ -186,77 +162,91 @@ print.CatTable <- function(x, missing = FALSE, if (!is.null(LIST)) { ## Returns an empty list if the stratum is null (empty). - LIST <- sapply(X = seq_along(LIST), # Loop over variables (list element is DF) - FUN = function(i) { - - ## Extract the data frame (list element) - DF <- LIST[[i]] - - ## Extract the variable name - varName <- names(LIST)[i] - - ## Check number of rows (levels) - nRow <- nrow(DF) + LIST <- + sapply(X = seq_along(LIST), # Loop over variables (list element is DF) + FUN = function(i) { + + ## Extract the data frame (list element) + DF <- LIST[[i]] + + ## Extract the variable name + varName <- names(LIST)[i] + + ## Check number of rows (levels) + nRow <- nrow(DF) + + ## Add a variable name to the left as a character vector + DF <- cbind(var = rep(varName, nRow), + DF) + + ## Format percent and cum.percent as strings + DF[c("p.miss","percent","cum.percent")] <- + lapply(X = DF[c("p.miss","percent","cum.percent")], + FUN = sprintf, + fmt = fmt1) + + + ## Make all variables strings (freq is an integer, so direct convert ok) + DF[] <- lapply(X = DF, FUN = as.character) + + ## Add first row indicator column + DF$firstRowInd <- "" + ## Add crammed row indicator column + DF$crammedRowInd <- "" + + ## Format based on the number of levels + if (!showAllLevels & nRow == 1) { + ## If showAllLevels is FALSE AND there are only ONE levels, + ## change variable name to "var = level" + DF$var <- with(DF, paste0(var, " = ", level)) + + } else if (!showAllLevels & nRow == 2) { + ## If showAllLevels is FALSE AND there are only TWO levels, + ## cram two levels in one row if requested + if (unique(DF$var) %in% cramVars) { + ## If cramVars includes var, cram into one line + ## Cram two freq and count with / in between + DF$freq <- paste0(DF$freq, collapse = "/") + DF$percent <- paste0(DF$percent, collapse = "/") + ## change variable name, and delete the first level. + DF$var <- paste0(DF$var, " = ", + paste0(DF$level, collapse = "/")) + ## Delete the first row + DF <- DF[-1, , drop = FALSE] + ## Add crammed row indicator (used for formatting) + DF[1,"crammedRowInd"] <- "crammed" + } else { + ## Otherwise, keep the second level only + ## change variable name, and delete the first level. + DF$var <- with(DF, paste0(var, " = ", level)) + DF <- DF[-1, , drop = FALSE] + } - ## Add a variable name to the left as a character vector - DF <- cbind(var = rep(varName, nRow), + } else if (!showAllLevels & nRow > 2) { + ## If showAllLevels is FALSE AND there are MORE THAN two levels, + ## add an empty row and put the var name, then levels below. + DF <- rbind(rep("", ncol(DF)), DF) + ## Add variable name in the first row + DF[1,"var"] <- DF[2,"var"] - ## Format percent and cum.percent as strings - DF[c("percent","cum.percent")] <- - lapply(X = DF[c("percent","cum.percent")], - FUN = sprintf, - fmt = fmt1) + ## 2nd to last have level names. (nrow has changed by +1) + secondToLastRows <- seq(from = 2,to = nrow(DF), by = 1) + DF[secondToLastRows, "var"] <- + paste0(" ", DF[secondToLastRows, "level"]) # preceding spaces + } else if (showAllLevels) { + ## If showAllLevels is TRUE, clear these except in 1st row + DF[-1, c("var","n","miss","p.miss")] <- "" + } - ## Make all variables strings (freq is an integer, so direct convert ok) - DF[] <- lapply(X = DF, FUN = as.character) + ## Add first row indicator (used to add (%)) + DF[1,"firstRowInd"] <- "first" - ## Add first row indicator column - DF$firstRowInd <- "" - - ## Format based on the number of levels - if (!showAllLevels & nRow == 1) { - ## If showAllLevels is FALSE AND there are only ONE levels, - ## change variable name to "var = level" - DF$var <- with(DF, paste0(var, " = ", level)) - ## Add first row indicator (used to add (%)) - DF[1,"firstRowInd"] <- "first" - - } else if (!showAllLevels & nRow == 2) { - ## If showAllLevels is FALSE AND there are only TWO levels, - ## change variable name, and delete the first level. - DF$var <- with(DF, paste0(var, " = ", level)) - DF <- DF[-1, , drop = FALSE] - ## Add first row indicator (used to add (%)) - DF[1,"firstRowInd"] <- "first" - - } else if (!showAllLevels & nRow > 2) { - ## If showAllLevels is FALSE AND there are MORE THAN two levels, - ## add an empty row and put the var name, then levels below. - DF <- rbind(rep("", ncol(DF)), - DF) - ## Add variable name in the first row - DF[1,"var"] <- DF[2,"var"] - - ## 2nd to last have level names. (nrow has changed by +1) - secondToLastRows <- seq(from = 2,to = nrow(DF), by = 1) - DF[secondToLastRows, "var"] <- - paste0(" ", DF[secondToLastRows, "level"]) # preceding spaces - ## Add first row indicator (used to add (%)) - DF[1,"firstRowInd"] <- "first" - - } else if (showAllLevels) { - ## If showAllLevels is TRUE clear names - DF[-1, c("var","n","miss")] <- "" - ## Add first row indicator (used to add (%)) - DF[1,"firstRowInd"] <- "first" - } - - ## Return a data frame - DF - }, - simplify = FALSE) + ## Return a data frame + DF + }, + simplify = FALSE) # Looped over variables (list element is DF) ## Collapse DFs within each stratum @@ -266,27 +256,40 @@ print.CatTable <- function(x, missing = FALSE, ## Check non-empty rows posNonEmptyRows <- DF$freq != "" - ## Right justify frequency + + ## Create freq to be added on to the right side within () + DF$freqAddOn <- DF$freq + ## Right justify frequency (crammed and non-crammed at once) DF$freq <- format(DF$freq, justify = "right") - ## Obtain the width of characters - nCharFreq <- nchar(DF$freq[1]) + ## Right justify frequency (non-crammed only) + DF[DF$crammedRowInd == "","freqAddOn"] <- + format(DF[DF$crammedRowInd == "","freqAddOn"], justify = "right") + ## Obtain the max width of characters + nCharFreq <- max(nchar(DF$freq)) + - ## Right justify percent + ## Create percent to be added on to the right side within () + DF$percentAddOn <- DF$percent + ## Right justify percent (crammed and non-crammed at once) DF$percent <- format(DF$percent, justify = "right") - ## Obtain the width of characters - nCharPercent <- nchar(DF$percent[1]) + ## Right justify percent (non-crammed only) + DF[DF$crammedRowInd == "","percentAddOn"] <- + format(DF[DF$crammedRowInd == "","percentAddOn"], justify = "right") + ## Obtain the max width of characters + nCharPercent <- max(nchar(DF$percent)) + ## Add freq (percent) column (only in non-empty rows) DF$freqPer <- "" DF[posNonEmptyRows,]$freqPer <- sprintf(fmt = "%s (%s) ", DF[posNonEmptyRows,]$freq, - DF[posNonEmptyRows,]$percent) + DF[posNonEmptyRows,]$percentAddOn) ## Add percent (freq) column (only in non-empty rows) DF$perFreq <- "" DF[posNonEmptyRows,]$perFreq <- sprintf(fmt = "%s (%s) ", DF[posNonEmptyRows,]$percent, - DF[posNonEmptyRows,]$freq) + DF[posNonEmptyRows,]$freqAddOn) ## Add aditional attributes attributes(DF) <- c(attributes(DF), @@ -300,7 +303,6 @@ print.CatTable <- function(x, missing = FALSE, }, simplify = FALSE) - ## browser() ### Obtain the original column width in characters for alignment in print.TableOne ## Name of the column to keep @@ -343,13 +345,10 @@ print.CatTable <- function(x, missing = FALSE, ## Add column names if multivariable stratification is used. (No column names added automatically) if (length(attr(CatTable, "dimnames")) > 1) { - colnames(out) <- - ## Create all combinations and collapse as strings. 1st variable cycles fastest. - apply(expand.grid(attr(CatTable, "dimnames")), - MARGIN = 1, - paste0, collapse = ":") + colnames(out) <- ModuleCreateStrataNames(CatTable) } + ## Set the variables names rownames(out) <- CatTableCollapsed[[posFirstNonNullElement]][,"var"] ## Get positions of rows with variable names @@ -369,57 +368,32 @@ print.CatTable <- function(x, missing = FALSE, ## Add p-values when requested and available if (test == TRUE & !is.null(attr(CatTable, "pValues"))) { - ## nVariables x 2 (pNormal,pNonNormal) data frame - pValues <- attr(CatTable, "pValues") - - ## Pick ones specified in exact (a vector with 1s(approx) and 2s(exact)) - pValues <- sapply(seq_along(exact), # loop over exact - FUN = function(i) { - ## Pick from a matrix i-th row, exact[i]-th column - ## Logical NA must be converted to a numeric - as.numeric(pValues[i, exact[i]]) - }, - simplify = TRUE) - - ## Pick test types used + ## Pick test types used (used for annonation) testTypes <- c("","exact")[exact] - ## Format p value - fmt <- paste0("%.", pDigits, "f") - p <- sprintf(fmt = fmt, pValues) - - ## Create a string like <0.001 - smallPString <- paste0("<0.", paste0(rep("0", pDigits - 1), collapse = ""), "1") - ## Check positions where it is all zero like 0.000 - posAllZeros <- grepl("^0\\.0*$", p) - ## Put the string where it is all zero like 0.000 - p[posAllZeros] <- smallPString - ## Put a preceding space where it is not like 0.000 - p[!posAllZeros] <- paste0(" ", p[!posAllZeros]) + ## Pick the p-values requested, and format like <0.001 + pVec <- ModulePickAndFormatPValues(TableObject = CatTable, + switchVec = exact, + pDigits = pDigits) ## Create an empty p-value column and test column out <- cbind(out, p = rep("", nrow(out))) # Column for p-values ## Put the values at the non-empty positions - out[logiNonEmptyRowNames,"p"] <- p + out[logiNonEmptyRowNames,"p"] <- pVec ## Change the indicator wasPValueColumnAdded <- TRUE - ## If exact test is used at least onece, add a test type indicator. - ## if (any(exact == 2)) { - if (TRUE) { - ## Create an empty test type column - out <- cbind(out, - test = rep("", nrow(out))) # Column for test types - - ## Put the test types at the non-empty positions - out[logiNonEmptyRowNames,"test"] <- testTypes + ## Create an empty test type column, and add test types + out <- cbind(out, + test = rep("", nrow(out))) # Column for test types + ## Put the test types at the non-empty positions (all rows in continuous!) + out[logiNonEmptyRowNames,"test"] <- testTypes - ## Change the indicator - wasExactColumnAdded <- TRUE - } + ## Change the indicator + wasExactColumnAdded <- TRUE } @@ -444,21 +418,8 @@ print.CatTable <- function(x, missing = FALSE, ## Put back the column names (overkill for non-multivariable cases) colnames(out) <- outColNames - ## Add stratification information to the column header (This is also in the constructor) - if (length(CatTable) > 1 ) { - ## Combine variable names with : in between - strataVarName <- attributes(CatTable)$strataVarName - - ## Create strata string - strataString <- paste0("Stratified by ", strataVarName) - - ## Name the row dimension with it. 1st dimension name should be empty. - names(dimnames(out)) <- c("", strataString) - } else { - - names(dimnames(out)) <- c("", "") - } - + ## Add stratification information to the column header depending on the dimension + names(dimnames(out)) <- ModuleReturnDimHeaders(CatTable) ## Modular version of quote/print toggle. out <- ModuleQuoteAndPrintMat(matObj = out, diff --git a/R/print.ContTable.R b/R/print.ContTable.R index 3fc39ad..495415c 100644 --- a/R/print.ContTable.R +++ b/R/print.ContTable.R @@ -3,26 +3,17 @@ ##' This is the \code{print} method for the \code{ContTable} class objects created by \code{\link{CreateContTable}} function. ##' ##' @param x The result of a call to the \code{\link{CreateContTable}} function. -##' @param missing Whether to show missing data information (not implemented -##' yet, placeholder) ##' @param digits Number of digits to print in the table. -##' @param nonnormal A character vector to specify the variables for which the -##' p-values should be those of nonparametric tests. By default all p-values -##' are from normal assumption-based tests (oneway.test). -##' @param quote Whether to show everything in quotes. The default is FALSE. If -##' TRUE, everything including the row and column names are quoted so that you -##' can copy it to Excel easily. -##' @param test Whether to show the p-values. TRUE by default. If FALSE, only -##' the numerical summaries are shown. ##' @param pDigits Number of digits to print for p-values. -##' @param explain Whether to add explanation to the variable names, i.e., -##' (mean (sd) or median [IQR]) is added to the variable names. -##' @param printToggle Whether to print the output. If FLASE, no output is -##' created, and a matrix is invisibly returned. +##' @param quote Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily. +##' @param missing Whether to show missing data information (not implemented yet, placeholder) +##' @param explain Whether to add explanation to the variable names, i.e., (mean (sd) or median [IQR]) is added to the variable names. +##' @param printToggle Whether to print the output. If FLASE, no output is created, and a matrix is invisibly returned. +##' @param nonnormal A character vector to specify the variables for which the p-values should be those of nonparametric tests. By default all p-values are from normal assumption-based tests (oneway.test). +##' @param minMax Whether to use [min,max] instead of [p25,p75] for nonnormal variables. The default is FALSE. +##' @param test Whether to show the p-values. TRUE by default. If FALSE, only the numerical summaries are shown. ##' @param ... For compatibility with generic. Ignored. -##' @return It is mainly for printing the result. But this function does return -##' a matrix containing what you see in the output invisibly. You can assign it -##' to an object to save it. +##' @return It is mainly for printing the result. But this function does return a matrix containing what you see in the output invisibly. You can assign it to an object to save it. ##' @author Kazuki Yoshida ##' @seealso ##' \code{\link{CreateCatTable}}, \code{\link{print.CatTable}}, \code{\link{summary.CatTable}}, @@ -58,6 +49,9 @@ ##' nonNormalVars <- c("age","chol","copper","alk.phos","trig","protime") ##' print(contTableOverall, nonnormal = nonNormalVars) ##' +##' ## To show median [min,max] for nonnormal variables, use minMax = TRUE +##' print(contTableOverall, nonnormal = nonNormalVars, minMax = TRUE) +##' ##' ## The table can be stratified by one or more variables ##' contTableBySexTrt <- CreateContTable(vars = contVars, ##' strata = c("sex","trt"), data = pbc) @@ -67,10 +61,13 @@ ##' ## by the pDigits argument (3 by default). It does <0.001 for you. ##' contTableBySexTrt ##' -##' ## The nonnormal argument will toggle the p-values to the nonparametric result from +##' ## The nonnormal argument toggles the p-values to the nonparametric result from ##' ## kruskal.test (wilcox.test equivalent for the two group case). ##' print(contTableBySexTrt, nonnormal = nonNormalVars) ##' +##' ## The minMax argument toggles whether to show median [range] +##' print(contTableBySexTrt, nonnormal = nonNormalVars, minMax = TRUE) +##' ##' ## summary now includes both types of p-values ##' summary(contTableBySexTrt) ##' @@ -80,11 +77,19 @@ ##' print(contTableBySexTrt, nonnormal = nonNormalVars, quote = TRUE) ##' ##' @export -print.ContTable <- function(x, missing = FALSE, - digits = 2, nonnormal = NULL, quote = FALSE, - test = TRUE, pDigits = 3, - explain = TRUE, - printToggle = TRUE, +print.ContTable <- function(x, # ContTable object + digits = 2, pDigits = 3, # Number of digits to show + quote = FALSE, # Whether to show quotes + + missing = FALSE, # show missing values (not implemented yet) + explain = TRUE, # Whether to show explanation in variable names + printToggle = TRUE, # Whether to print the result visibly + + nonnormal = NULL, # Which variables should be treated as nonnormal + minMax = FALSE, # median [range] instead of median [IQR] + + test = TRUE, # Whether to add p-values + ...) { ## x and ... required to be consistent with generic print(x, ...) @@ -97,37 +102,15 @@ print.ContTable <- function(x, missing = FALSE, posFirstNonNullElement <- which(!sapply(ContTable, is.null))[1] ## Save variable names using the first non-null element varNames <- rownames(ContTable[[posFirstNonNullElement]]) - ## Check the number of rows - nRows <- length(varNames) - - ## If null, do normal print/test - if (is.null(nonnormal)) { - ## Give one as many as there are rows - nonnormal <- rep(1, nRows) + ## Check the number of variables + nVars <- length(varNames) - } else { - ## If not null, it needs checking. - ## Check the nonnormal argument - if (!is.logical(nonnormal) & !is.character(nonnormal)) { - stop("nonnormal argument has to be FALSE/TRUE or character.") - } - ## Extend if it is a logitcal vector with one element. - if (is.logical(nonnormal)) { + ## Returns a numeric vector: 1 for normal variable; 2 for nonnormal variable + nonnormal <- ModuleHandleDefaultOrAlternative(switchVec = nonnormal, + nameOfSwitchVec = "nonnormal", + varNames = varNames) - if (length(nonnormal) != 1) { - stop("nonormal has to be a logical vector of length 1") - } - - nonnormal <- rep(nonnormal, nRows) - } - ## Convert to a logical vector if it is a character vector - if (is.character(nonnormal)) { - nonnormal <- varNames %in% nonnormal - } - ## Convert to numeric (1 for normal, 2 for nonnormal) - nonnormal <- as.numeric(nonnormal) + 1 - } ## Check the statistics. If necessary statistics are lacking abort statNames <- colnames(ContTable[[posFirstNonNullElement]]) @@ -143,15 +126,15 @@ print.ContTable <- function(x, missing = FALSE, ## Added as the top row later strataN <- sapply(ContTable, FUN = function(stratum) { # loop over strata - ## Just the first available element may be enough. - ## Obtain n from all variables and all levels, and get the mean + ## each strutum is a data frame with one row for each variable + ## Obtain n from all variables (matrix) n <- stratum[,"n"] ## Pick the first non-null element n[!is.null(n)][1] - ## Convert NULL to N + ## Convert NULL to 0 ifelse(is.null(n), "0", as.character(n)) }, - simplify = TRUE) + simplify = TRUE) # vector with as many elements as strata ## Provide indicators to show what columns were added. wasPValueColumnAdded <- FALSE @@ -160,34 +143,21 @@ print.ContTable <- function(x, missing = FALSE, ### Conversion of data for printing - ## These may want to be moved to separate files later. - ## Define a function to print a normal variable + ## Define the nonnormal formatter depending on the minMax status ConvertNormal <- function(rowMat) { - - ## Format for SD - fmt <- paste0(" (%.", digits,"f",")") - - ## Create a DF with numeric mean column and character (SD) column - data.frame(col1 = rowMat[,"mean"], - col2 = sprintf(fmt = fmt, rowMat[,"sd"]), - stringsAsFactors = FALSE) + ## Take minMax value from outside (NOT A STANDALONE FUNCTION!!) + ModuleConvertNormal(rowMat, digits) } - ## Define a function to print a nonnormal variable + ## Define the nonnormal formatter depending on the minMax status ConvertNonNormal <- function(rowMat) { - ## Format for [p25, p75] - fmt <- paste0(" [%.", digits,"f, %.",digits,"f]") - - ## Create a DF with numeric median column and character [p25, p75] column - data.frame(col1 = rowMat[,"median"], - col2 = sprintf(fmt = fmt, rowMat[,"p25"], rowMat[,"p75"]), - stringsAsFactors = FALSE) + ## Take minMax value from outside (NOT A STANDALONE FUNCTION!!) + ModuleConvertNonNormal(rowMat, digits, minMax = minMax) } - ## Create a list of these two functions listOfFunctions <- list(normal = ConvertNormal, nonnormal = ConvertNonNormal) - ## Take functions from the 2-element list, and convert to an nRows-length list + ## Take functions from the 2-element list, and convert to an nVars-length list listOfFunctions <- listOfFunctions[nonnormal] ## Loop over strata (There may be just one) @@ -196,14 +166,14 @@ print.ContTable <- function(x, missing = FALSE, ## In an empty stratum, return empty if (is.null(stratum)) { - out <- rep("-", nRows) + out <- rep("-", nVars) ## Give NA to the width of the mean/median column in characters nCharMeanOrMedian <- NA } else { ## Apply row by row within each non-empty stratum ## This row-by-row operation is necessary to handle mean (sd) and median [IQR] - out <- sapply(seq_len(nRows), + out <- sapply(seq_len(nVars), FUN = function(i) { ## Choose between normal or nonnormal function @@ -260,71 +230,48 @@ print.ContTable <- function(x, missing = FALSE, ## Add column names if multivariable stratification is used. if (length(attr(ContTable, "dimnames")) > 1) { - colnames(out) <- - ## Create all combinations and collapse as strings - apply(expand.grid(attr(ContTable, "dimnames")), - MARGIN = 1, - paste0, collapse = ":") + colnames(out) <- ModuleCreateStrataNames(ContTable) } ## Add p-values when requested and available if (test == TRUE & !is.null(attr(ContTable, "pValues"))) { - ## nVariables x 2 (pNormal,pNonNormal) data frame - pValues <- attr(ContTable, "pValues") - - ## Pick ones specified in nonnormal (a vector with 1s(normal) and 2s(nonnormal)) - pValues <- sapply(seq_along(nonnormal), # loop over nonnormal - FUN = function(i) { - ## Pick from a matrix i-th row, nonnormal[i]-th column - ## Logical NA must be converted to a numeric - as.numeric(pValues[i, nonnormal[i]]) - }, - simplify = TRUE) - - ## Pick test types used + ## Pick test types used (used for annonation) testTypes <- c("","nonnorm")[nonnormal] - ## Format - fmt <- paste0("%.", pDigits, "f") - p <- sprintf(fmt = fmt, pValues) - - ## Create a string like <0.001 - smallPString <- paste0("<0.", paste0(rep("0", pDigits - 1), collapse = ""), "1") - ## Check positions where it is all zero like 0.000 - posAllZeros <- grepl("^0\\.0*$", p) - ## Put the string where it is all zero like 0.000 - p[posAllZeros] <- smallPString - ## Put a preceding space where it is not like 0.000 - p[!posAllZeros] <- paste0(" ", p[!posAllZeros]) + ## Pick the p-values requested, and format like <0.001 + pVec <- ModulePickAndFormatPValues(TableObject = ContTable, + switchVec = nonnormal, + pDigits = pDigits) ## Column combine with the output - out <- cbind(out, p = p) + out <- cbind(out, p = pVec) ## Change the indicator wasPValueColumnAdded <- TRUE - ## If nonormal test is used at least onece, add a test type indicator. - ## if (any(nonormal == 2)) { - if (TRUE) { - ## Create an empty test type column - out <- cbind(out, - test = rep("", nrow(out))) # Column for test types - - ## Put the test types at the non-empty positions (all rows in continuous!) - out[ ,"test"] <- testTypes + ## Create an empty test type column, and add test types + out <- cbind(out, + test = rep("", nrow(out))) # Column for test types + ## Put the test types at the non-empty positions (all rows in continuous!) + out[ ,"test"] <- testTypes - ## Change the indicator - wasNonNormalColumnAdded <- TRUE - } + ## Change the indicator + wasNonNormalColumnAdded <- TRUE } - ## Add mean (sd) or median [IQR] explanation if requested + ## Add mean (sd) or median [IQR]/median [range] explanation if requested if (explain) { - what <- c(" (mean (sd))"," (median [IQR])")[nonnormal] + ## Create a vector of explanations to be pasted + if (minMax == FALSE) { + what <- c(" (mean (sd))"," (median [IQR])")[nonnormal] + } else if (minMax == TRUE) { + what <- c(" (mean (sd))"," (median [range])")[nonnormal] + } + ## Paste to the rownames rownames(out) <- paste0(rownames(out), what) } @@ -339,19 +286,8 @@ print.ContTable <- function(x, missing = FALSE, ## Put back the column names (overkill for non-multivariable cases) colnames(out) <- outColNames - ## Add stratification information to the column header - if (length(ContTable) > 1 ) { - ## Create strata string - strataString <- paste0("Stratified by ", - paste0(names(attr(ContTable, "dimnames")), collapse = ":")) - - ## Name the row dimension with it. 1st dimension name should be empty. - names(dimnames(out)) <- c("", strataString) - } else { - - names(dimnames(out)) <- c("", "") - } - + ## Add stratification information to the column header depending on the dimension + names(dimnames(out)) <- ModuleReturnDimHeaders(ContTable) ## (module) Takes an matrix object format, print if requested out <- ModuleQuoteAndPrintMat(matObj = out, diff --git a/R/print.TableOne.R b/R/print.TableOne.R index 111183c..7b7a3d1 100644 --- a/R/print.TableOne.R +++ b/R/print.TableOne.R @@ -3,33 +3,21 @@ ##' This is the \code{print} method for the \code{TableOne} class objects created by \code{\link{CreateTableOne}} function. ##' ##' @param x The result of a call to the \code{\link{CreateTableOne}} function. -##' @param missing Whether to show missing data information (not implemented -##' yet, placeholder) -##' @param quote Whether to show everything in quotes. The default is FALSE. If -##' TRUE, everything including the row and column names are quoted so that you -##' can copy it to Excel easily. -##' @param test Whether to show the p-values. TRUE by default. If FALSE, only -##' the numerical summaries are shown. ##' @param catDigits Number of digits to print for proportions. Default 1. ##' @param contDigits Number of digits to print for continuous variables. Default 2. ##' @param pDigits Number of digits to print for p-values. Default 3. -##' @param format The default is "fp" frequency (percentage). You can also -##' choose from "f" frequency only, "p" percentage only, and "pf" percentage -##' (frequency). -##' @param exact A character vector to specify the variables for which the -##' p-values should be those of exact tests. By default all p-values are from -##' large sample approximation tests (chisq.test). -##' @param nonnormal A character vector to specify the variables for which the -##' p-values should be those of nonparametric tests. By default all p-values -##' are from normal assumption-based tests (oneway.test). -##' @param explain Whether to add explanation to the variable names, i.e., (\%) -##' is added to the variable names when percentage is shown. -##' @param printToggle Whether to print the output. If FLASE, no output is -##' created, and a matrix is invisibly returned. +##' @param quote Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily. +##' @param missing Whether to show missing data information (not implemented yet, placeholder) +##' @param explain Whether to add explanation to the variable names, i.e., (\%) is added to the variable names when percentage is shown. +##' @param printToggle Whether to print the output. If FLASE, no output is created, and a matrix is invisibly returned. +##' @param test Whether to show the p-values. TRUE by default. If FALSE, only the numerical summaries are shown. +##' @param format The default is "fp" frequency (percentage). You can also choose from "f" frequency only, "p" percentage only, and "pf" percentage (frequency). +##' @param cramVars A character vector to specify the two-level categorical variables, for which both levels should be shown in one row. +##' @param exact A character vector to specify the variables for which the p-values should be those of exact tests. By default all p-values are from large sample approximation tests (chisq.test). +##' @param nonnormal A character vector to specify the variables for which the p-values should be those of nonparametric tests. By default all p-values are from normal assumption-based tests (oneway.test). +##' @param minMax Whether to use [min,max] instead of [p25,p75] for nonnormal variables. The default is FALSE. ##' @param ... For compatibility with generic. Ignored. -##' @return It is mainly for printing the result. But this function does return -##' a matrix containing what you see in the output invisibly. You can assign it -##' to an object to save it. +##' @return It is mainly for printing the result. But this function does return a matrix containing what you see in the output invisibly. You can assign it to an object to save it. ##' @author Kazuki Yoshida, Justin Bohn ##' @seealso ##' \code{\link{CreateTableOne}}, \code{\link{print.TableOne}}, \code{\link{summary.TableOne}}, @@ -62,13 +50,14 @@ ##' ##' ## Specifying nonnormal variables will show the variables appropriately, ##' ## and show nonparametric test p-values. Specify variables in the exact -##' ## argument to obtain the exact test p-values. +##' ## argument to obtain the exact test p-values. cramVars can be used to +##' ## show both levels for a 2-level categorical variables. ##' print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), -##' exact = c("status","stage")) +##' exact = c("status","stage"), cramVars = "hepato") ##' ##' ## Use the summary.TableOne method for detailed summary ##' summary(tableOne) -##' +##' ##' ## See the categorical part only using $ operator ##' tableOne$CatTable ##' summary(tableOne$CatTable) @@ -78,20 +67,25 @@ ##' summary(tableOne$ContTable) ##' ##' @export -print.TableOne <- function(x, missing = FALSE, - quote = FALSE, - test = TRUE, catDigits = 1, contDigits = 2, pDigits = 3, +print.TableOne <- function(x, # TableOne object + catDigits = 1, contDigits = 2, pDigits = 3, # Number of digits to show + quote = FALSE, # Whether to show quotes + + ## Common options + missing = FALSE, # Not implemented yet + explain = TRUE, # Whether to show explanation in variable names + printToggle = TRUE, # Whether to print the result visibly + test = TRUE, # Whether to add p-values ## Categorical options - format = c("fp","f","p","pf")[1], # Format f_requency and/or p_ercent - exact = NULL, + format = c("fp","f","p","pf")[1], # Format f_requency and/or p_ercent + cramVars = NULL, # Which 2-level variables to show both levels in one row + exact = NULL, # Which variables should be tested with exact tests ## Continuous options - nonnormal = NULL, - - ## Common options - explain = TRUE, - printToggle = TRUE, + nonnormal = NULL, # Which variables should be treated as nonnormal + minMax = FALSE, # Whether to show median + ...) { ## Get the mixed element only @@ -105,13 +99,15 @@ print.TableOne <- function(x, missing = FALSE, ## Get the formatted tables formattedTables <- sapply(seq_along(TableOne), FUN = function(i) { - + print(TableOne[[i]], printToggle = FALSE, test = test, explain = explain, digits = digits[i], ## print.CatTable arguments - format = format, exact = exact, showAllLevels = FALSE, + format = format, exact = exact, + showAllLevels = FALSE, # must be FALSE to get same column counts + cramVars = cramVars, ## print.ContTable argument - nonnormal = nonnormal + nonnormal = nonnormal, minMax = minMax ) # Method dispatch at work }, simplify = FALSE) @@ -119,7 +115,7 @@ print.TableOne <- function(x, missing = FALSE, ## Get the column width information (strata x vars format) columnWidthInfo <- sapply(formattedTables, FUN = function(matObj) { - + attributes(matObj)$vecColWidths }, simplify = FALSE) @@ -163,7 +159,7 @@ print.TableOne <- function(x, missing = FALSE, ## Remove 1st rows from each table (stratum sizes) spaceFormattedTables <- sapply(spaceFormattedTables, FUN = function(matObj) { - + matObj[-1, , drop = FALSE] }, simplify = FALSE) diff --git a/R/summary.CatTable.R b/R/summary.CatTable.R index 0c1e1d2..052b18f 100644 --- a/R/summary.CatTable.R +++ b/R/summary.CatTable.R @@ -66,8 +66,8 @@ summary.CatTable <- function(object, digits = 1, ...) { DF) ## Format percent and cum.percent - DF[c("percent","cum.percent")] <- - lapply(X = DF[c("percent","cum.percent")], + DF[c("p.miss","percent","cum.percent")] <- + lapply(X = DF[c("p.miss","percent","cum.percent")], FUN = sprintf, fmt = fmt) @@ -77,7 +77,7 @@ summary.CatTable <- function(object, digits = 1, ...) { FUN = as.character) ## Delete n and miss except in the first row - DF[-1, c("var","n","miss")] <- "" + DF[-1, c("var","n","miss","p.miss")] <- "" ## row bind an empty row DF <- rbind(DF, diff --git a/R/tableone-package.R b/R/tableone-package.R index 176ebb9..1961439 100644 --- a/R/tableone-package.R +++ b/R/tableone-package.R @@ -1,6 +1,6 @@ ##' Create "Table 1" to describe baseline characteristics ##' -##' This package creates "Table 1", i.e., description of baseline patient characteristics, which is essential every medical research. This package provides functions to create such summaries for continuous and categorical variables, optionally with subgroups and groupwise comparison. The package was insipired by and based on descriptive statistics functions in Deducer, a Java-based GUI package by Ian Fellows. This package does not require GUI or Java, and intended for CUI users. +##' This package creates "Table 1", i.e., description of baseline patient characteristics, which is essential in every medical research. This package provides functions to create such summaries for continuous and categorical variables, optionally with subgroups and groupwise comparison. The package was insipired by and based on descriptive statistics functions in Deducer, a Java-based GUI package by Ian Fellows. This package does not require GUI or Java, and intended for CUI users. ##' ##' @name tableone-package ##' @aliases tableone-package tableone @@ -10,8 +10,11 @@ ##' Ian Fellows for developing the Deducer package, which this package is based on. ##' ##' Hadley Wickham for packaging advice and for creating tools this package was made with (roxygen2, devtools, testthat). +##' +##' Yoshinobu Kanda for design advice. +## and for (future) integration into \code{RcmdrPlugin.EZR}. ##' -##' Members of Facebook Organization of R Users for Medical Statistics in Japan (FORUMS-J) for testing pre-release versions. +##' Members of the Facebook Organization of R Users for Medical Statistics in Japan (FORUMS-J) for testing pre-release versions. ##' ##' Developmental repository is on github. Your contributions are appreciated. ##' @@ -36,28 +39,32 @@ ##' ## Check variables ##' head(pbc) ##' -##' ## Make categorical variables factors -##' varsToFactor <- c("status","trt","ascites","hepato","spiders","edema","stage") -##' pbc[varsToFactor] <- lapply(pbc[varsToFactor], factor) +##' ## List numerically coded categorical variables for later conversion. +##' ## Factor variables are automatically handled as categorical variables. +##' factorVars <- c("status","trt","ascites","hepato","spiders","edema","stage") ##' ##' ## Create a variable list -##' dput(names(pbc)) +##' dput(names(pbc)) # This shows a character vector-creating syntax. ##' vars <- c("time","status","age","sex","ascites","hepato", ##' "spiders","edema","bili","chol","albumin", ##' "copper","alk.phos","ast","trig","platelet", ##' "protime","stage") ##' -##' ## Create Table 1 stratified by trt -##' tableOne <- CreateTableOne(vars = vars, strata = c("trt"), data = pbc) +##' ## Create Table 1 stratified by trt. Use factorVars to convert numerically +##' ## coded categorical variables as factors without changing the dataset. +##' tableOne <- CreateTableOne(vars = vars, strata = c("trt"), data = pbc, +##' factorVars = factorVars) ##' ##' ## Just typing the object name will invoke the print.TableOne method ##' tableOne ##' ##' ## Specifying nonnormal variables will show the variables appropriately, ##' ## and show nonparametric test p-values. Specify variables in the exact -##' ## argument to obtain the exact test p-values. +##' ## argument to obtain the exact test p-values. For two-level categorical +##' ## variables specified in cramVars, both levels are shown. Use minMax +##' ## argument to show median [min, max] for nonnormal variables. ##' print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), -##' exact = c("status","stage")) +##' exact = c("status","stage"), cramVars = "sex") ##' ##' ## Use the summary.TableOne method for detailed summary ##' summary(tableOne) @@ -74,6 +81,6 @@ ##' ## you may benefit from the quote argument. This will quote everything so that ##' ## Excel does not mess up the cells. ##' print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), -##' exact = c("status","stage"), quote = TRUE) +##' exact = c("status","stage"), cramVars = "sex", quote = TRUE) ##' NULL diff --git a/man/CreateCatTable.Rd b/man/CreateCatTable.Rd index 04b8791..ec231f5 100644 --- a/man/CreateCatTable.Rd +++ b/man/CreateCatTable.Rd @@ -76,9 +76,13 @@ catTableOverall <- CreateCatTable(vars = catVars, data = pbc) ## Simply typing the object name will invoke the print.CatTable method, ## which will show the sample size, frequencies and percentages. -## For 2-level variables, only the higher level is shown for simplicity. +## For 2-level variables, only the higher level is shown for simplicity +## unless the variables are specified in the cramVars argument. catTableOverall +## If you need to show both levels for some 2-level factors, use cramVars +print(catTableOverall, cramVars = "hepato") + ## Use the showAllLevels argument to see all levels for all variables. print(catTableOverall, showAllLevels = TRUE) @@ -100,7 +104,7 @@ catTableBySexTrt <- CreateCatTable(vars = catVars, ## (3 by default). It does <0.001 for you. catTableBySexTrt -## The exact argument will toggle the p-values to the example test result from +## The exact argument toggles the p-values to the exact test result from ## fisher.test. It will show which ones are from exact tests. print(catTableBySexTrt, exact = "ascites") diff --git a/man/CreateContTable.Rd b/man/CreateContTable.Rd index ef0fcdd..df99775 100644 --- a/man/CreateContTable.Rd +++ b/man/CreateContTable.Rd @@ -2,10 +2,11 @@ \alias{CreateContTable} \title{Create an object summarizing continous variables} \usage{ -CreateContTable(vars, strata, data, func.names = c("n", "miss", "mean", "sd", - "median", "p25", "p75", "min", "max", "skew", "kurt"), func.additional, - test = TRUE, testNormal = oneway.test, argsNormal = list(var.equal = - TRUE), testNonNormal = kruskal.test, argsNonNormal = list(NULL)) +CreateContTable(vars, strata, data, funcNames = c("n", "miss", "p.miss", + "mean", "sd", "median", "p25", "p75", "min", "max", "skew", "kurt"), + funcAdditional, test = TRUE, testNormal = oneway.test, + argsNormal = list(var.equal = TRUE), testNonNormal = kruskal.test, + argsNonNormal = list(NULL)) } \arguments{ \item{vars}{Variable(s) to be summarized given as a @@ -19,17 +20,17 @@ CreateContTable(vars, strata, data, func.names = c("n", "miss", "mean", "sd", All variables (both vars and strata) must be in this data frame.} - \item{func.names}{The functions to give the group size, - number with missing values, mean, standard deviations, - median, 25th percentile, 75th percentile, minimum, - maximum, skewness (same definition as in SAS), kurtosis - (same definition as in SAS). All of them can be seen in - the summary method output. The print method uses subset - of these. You can choose subset of them or reorder them. - They are all configure to omit NA values (\code{na.rm = - TRUE}).} - - \item{func.additional}{Additional functions can be given + \item{funcNames}{The functions to give the group size, + number with missing values, proportion with missing + values, mean, standard deviations, median, 25th + percentile, 75th percentile, minimum, maximum, skewness + (same definition as in SAS), kurtosis (same definition as + in SAS). All of them can be seen in the summary method + output. The print method uses subset of these. You can + choose subset of them or reorder them. They are all + configure to omit NA values (\code{na.rm = TRUE}).} + + \item{funcAdditional}{Additional functions can be given as a named list. For example, \code{list(sum = sum)}.} \item{test}{If TRUE, as in the default and there are more @@ -104,6 +105,9 @@ summary(contTableOverall) nonNormalVars <- c("age","chol","copper","alk.phos","trig","protime") print(contTableOverall, nonnormal = nonNormalVars) +## To show median [min,max] for nonnormal variables, use minMax = TRUE +print(contTableOverall, nonnormal = nonNormalVars, minMax = TRUE) + ## The table can be stratified by one or more variables contTableBySexTrt <- CreateContTable(vars = contVars, strata = c("sex","trt"), data = pbc) @@ -113,7 +117,7 @@ contTableBySexTrt <- CreateContTable(vars = contVars, ## by the pDigits argument (3 by default). It does <0.001 for you. contTableBySexTrt -## The nonnormal argument will toggle the p-values to the nonparametric result from +## The nonnormal argument toggles the p-values to the nonparametric result from ## kruskal.test (wilcox.test equivalent for the two group case). print(contTableBySexTrt, nonnormal = nonNormalVars) diff --git a/man/CreateTableOne.Rd b/man/CreateTableOne.Rd index bc8e095..790a14f 100644 --- a/man/CreateTableOne.Rd +++ b/man/CreateTableOne.Rd @@ -2,11 +2,11 @@ \alias{CreateTableOne} \title{Create an object summarizing both categorical and continuous variables} \usage{ -CreateTableOne(vars, strata, data, test = TRUE, testApprox = chisq.test, - argsApprox = list(correct = TRUE), testExact = fisher.test, - argsExact = list(workspace = 2 * 10^5), testNormal = oneway.test, - argsNormal = list(var.equal = TRUE), testNonNormal = kruskal.test, - argsNonNormal = list(NULL)) +CreateTableOne(vars, strata, data, factorVars, test = TRUE, + testApprox = chisq.test, argsApprox = list(correct = TRUE), + testExact = fisher.test, argsExact = list(workspace = 2 * 10^5), + testNormal = oneway.test, argsNormal = list(var.equal = TRUE), + testNonNormal = kruskal.test, argsNonNormal = list(NULL)) } \arguments{ \item{vars}{Variables to be summarized given as a @@ -22,6 +22,13 @@ CreateTableOne(vars, strata, data, test = TRUE, testApprox = chisq.test, All variables (both vars and strata) must be in this data frame.} + \item{factorVars}{Numerically coded variables that should + be handled as categorical variables given as a character + vector. If omitted, only factors are considered + categorical variables. If all categorical variables in + the dataset are already factors, this option is not + necessary.} + \item{test}{If TRUE, as in the default and there are more than two groups, groupwise comparisons are performed.} diff --git a/man/print.CatTable.Rd b/man/print.CatTable.Rd index 37da460..9224605 100644 --- a/man/print.CatTable.Rd +++ b/man/print.CatTable.Rd @@ -2,56 +2,60 @@ \alias{print.CatTable} \title{Format and print the \code{CatTable} class objects} \usage{ -\method{print}{CatTable}(x, missing = FALSE, format = c("fp", "f", "p", - "pf")[1], digits = 1, exact = NULL, quote = FALSE, test = TRUE, - pDigits = 3, showAllLevels = FALSE, explain = TRUE, - CrossTable = FALSE, printToggle = TRUE, ...) +\method{print}{CatTable}(x, digits = 1, pDigits = 3, quote = FALSE, + missing = FALSE, explain = TRUE, printToggle = TRUE, format = c("fp", + "f", "p", "pf")[1], showAllLevels = FALSE, cramVars = NULL, test = TRUE, + exact = NULL, CrossTable = FALSE, ...) } \arguments{ \item{x}{The result of a call to the \code{\link{CreateCatTable}} function.} - \item{missing}{Whether to show missing data information - (not implemented yet, placeholder)} - - \item{format}{The default is "fp" frequency (percentage). - You can also choose from "f" frequency only, "p" - percentage only, and "pf" percentage (frequency).} - \item{digits}{Number of digits to print in the table.} - \item{exact}{A character vector to specify the variables - for which the p-values should be those of exact tests. By - default all p-values are from large sample approximation - tests (chisq.test).} + \item{pDigits}{Number of digits to print for p-values.} \item{quote}{Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily.} - \item{test}{Whether to show the p-values. TRUE by - default. If FALSE, only the numerical summaries are - shown.} + \item{missing}{Whether to show missing data information + (not implemented yet, placeholder)} - \item{pDigits}{Number of digits to print for p-values.} + \item{explain}{Whether to add explanation to the variable + names, i.e., (\%) is added to the variable names when + percentage is shown.} + + \item{printToggle}{Whether to print the output. If FLASE, + no output is created, and a matrix is invisibly + returned.} + + \item{format}{The default is "fp" frequency (percentage). + You can also choose from "f" frequency only, "p" + percentage only, and "pf" percentage (frequency).} \item{showAllLevels}{Whether to show all levels. FALSE by default, i.e., for 2-level categorical variables, only the higher level is shown to avoid} - \item{explain}{Whether to add explanation to the variable - names, i.e., (\%) is added to the variable names when - percentage is shown.} + \item{cramVars}{A character vector to specify the + two-level categorical variables, for which both levels + should be shown in one row.} + + \item{test}{Whether to show the p-values. TRUE by + default. If FALSE, only the numerical summaries are + shown.} + + \item{exact}{A character vector to specify the variables + for which the p-values should be those of exact tests. By + default all p-values are from large sample approximation + tests (chisq.test).} \item{CrossTable}{Whether to show the cross table objects held internally using gmodels::CrossTable function. This will give an output similar to the PROC FREQ in SAS.} - \item{printToggle}{Whether to print the output. If FLASE, - no output is created, and a matrix is invisibly - returned.} - \item{...}{For compatibility with generic. Ignored.} } \value{ @@ -83,6 +87,9 @@ catTableOverall <- CreateCatTable(vars = catVars, data = pbc) ## For 2-level variables, only the higher level is shown for simplicity. catTableOverall +## If you need to show both levels for some 2-level factors, use cramVars +print(catTableOverall, cramVars = "hepato") + ## Use the showAllLevels argument to see all levels for all variables. print(catTableOverall, showAllLevels = TRUE) @@ -104,7 +111,7 @@ catTableBySexTrt <- CreateCatTable(vars = catVars, ## (3 by default). It does <0.001 for you. catTableBySexTrt -## The exact argument will toggle the p-values to the example test result from +## The exact argument toggles the p-values to the exact test result from ## fisher.test. It will show which ones are from exact tests. print(catTableBySexTrt, exact = "ascites") diff --git a/man/print.ContTable.Rd b/man/print.ContTable.Rd index 070cab4..5feb81c 100644 --- a/man/print.ContTable.Rd +++ b/man/print.ContTable.Rd @@ -2,34 +2,25 @@ \alias{print.ContTable} \title{Format and print the \code{ContTable} class objects} \usage{ -\method{print}{ContTable}(x, missing = FALSE, digits = 2, - nonnormal = NULL, quote = FALSE, test = TRUE, pDigits = 3, - explain = TRUE, printToggle = TRUE, ...) +\method{print}{ContTable}(x, digits = 2, pDigits = 3, quote = FALSE, + missing = FALSE, explain = TRUE, printToggle = TRUE, nonnormal = NULL, + minMax = FALSE, test = TRUE, ...) } \arguments{ \item{x}{The result of a call to the \code{\link{CreateContTable}} function.} - \item{missing}{Whether to show missing data information - (not implemented yet, placeholder)} - \item{digits}{Number of digits to print in the table.} - \item{nonnormal}{A character vector to specify the - variables for which the p-values should be those of - nonparametric tests. By default all p-values are from - normal assumption-based tests (oneway.test).} + \item{pDigits}{Number of digits to print for p-values.} \item{quote}{Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily.} - \item{test}{Whether to show the p-values. TRUE by - default. If FALSE, only the numerical summaries are - shown.} - - \item{pDigits}{Number of digits to print for p-values.} + \item{missing}{Whether to show missing data information + (not implemented yet, placeholder)} \item{explain}{Whether to add explanation to the variable names, i.e., (mean (sd) or median [IQR]) is added to the @@ -39,6 +30,18 @@ no output is created, and a matrix is invisibly returned.} + \item{nonnormal}{A character vector to specify the + variables for which the p-values should be those of + nonparametric tests. By default all p-values are from + normal assumption-based tests (oneway.test).} + + \item{minMax}{Whether to use [min,max] instead of + [p25,p75] for nonnormal variables. The default is FALSE.} + + \item{test}{Whether to show the p-values. TRUE by + default. If FALSE, only the numerical summaries are + shown.} + \item{...}{For compatibility with generic. Ignored.} } \value{ @@ -80,6 +83,9 @@ summary(contTableOverall) nonNormalVars <- c("age","chol","copper","alk.phos","trig","protime") print(contTableOverall, nonnormal = nonNormalVars) +## To show median [min,max] for nonnormal variables, use minMax = TRUE +print(contTableOverall, nonnormal = nonNormalVars, minMax = TRUE) + ## The table can be stratified by one or more variables contTableBySexTrt <- CreateContTable(vars = contVars, strata = c("sex","trt"), data = pbc) @@ -89,10 +95,13 @@ contTableBySexTrt <- CreateContTable(vars = contVars, ## by the pDigits argument (3 by default). It does <0.001 for you. contTableBySexTrt -## The nonnormal argument will toggle the p-values to the nonparametric result from +## The nonnormal argument toggles the p-values to the nonparametric result from ## kruskal.test (wilcox.test equivalent for the two group case). print(contTableBySexTrt, nonnormal = nonNormalVars) +## The minMax argument toggles whether to show median [range] +print(contTableBySexTrt, nonnormal = nonNormalVars, minMax = TRUE) + ## summary now includes both types of p-values summary(contTableBySexTrt) diff --git a/man/print.TableOne.Rd b/man/print.TableOne.Rd index 93ad684..15a6761 100644 --- a/man/print.TableOne.Rd +++ b/man/print.TableOne.Rd @@ -2,40 +2,52 @@ \alias{print.TableOne} \title{Format and print the \code{TableOne} class objects} \usage{ -\method{print}{TableOne}(x, missing = FALSE, quote = FALSE, test = TRUE, - catDigits = 1, contDigits = 2, pDigits = 3, format = c("fp", "f", "p", - "pf")[1], exact = NULL, nonnormal = NULL, explain = TRUE, - printToggle = TRUE, ...) +\method{print}{TableOne}(x, catDigits = 1, contDigits = 2, pDigits = 3, + quote = FALSE, missing = FALSE, explain = TRUE, printToggle = TRUE, + test = TRUE, format = c("fp", "f", "p", "pf")[1], cramVars = NULL, + exact = NULL, nonnormal = NULL, minMax = FALSE, ...) } \arguments{ \item{x}{The result of a call to the \code{\link{CreateTableOne}} function.} - \item{missing}{Whether to show missing data information - (not implemented yet, placeholder)} + \item{catDigits}{Number of digits to print for + proportions. Default 1.} + + \item{contDigits}{Number of digits to print for + continuous variables. Default 2.} + + \item{pDigits}{Number of digits to print for p-values. + Default 3.} \item{quote}{Whether to show everything in quotes. The default is FALSE. If TRUE, everything including the row and column names are quoted so that you can copy it to Excel easily.} - \item{test}{Whether to show the p-values. TRUE by - default. If FALSE, only the numerical summaries are - shown.} + \item{missing}{Whether to show missing data information + (not implemented yet, placeholder)} - \item{catDigits}{Number of digits to print for - proportions. Default 1.} + \item{explain}{Whether to add explanation to the variable + names, i.e., (\%) is added to the variable names when + percentage is shown.} - \item{contDigits}{Number of digits to print for - continuous variables. Default 2.} + \item{printToggle}{Whether to print the output. If FLASE, + no output is created, and a matrix is invisibly + returned.} - \item{pDigits}{Number of digits to print for p-values. - Default 3.} + \item{test}{Whether to show the p-values. TRUE by + default. If FALSE, only the numerical summaries are + shown.} \item{format}{The default is "fp" frequency (percentage). You can also choose from "f" frequency only, "p" percentage only, and "pf" percentage (frequency).} + \item{cramVars}{A character vector to specify the + two-level categorical variables, for which both levels + should be shown in one row.} + \item{exact}{A character vector to specify the variables for which the p-values should be those of exact tests. By default all p-values are from large sample approximation @@ -46,13 +58,8 @@ nonparametric tests. By default all p-values are from normal assumption-based tests (oneway.test).} - \item{explain}{Whether to add explanation to the variable - names, i.e., (\%) is added to the variable names when - percentage is shown.} - - \item{printToggle}{Whether to print the output. If FLASE, - no output is created, and a matrix is invisibly - returned.} + \item{minMax}{Whether to use [min,max] instead of + [p25,p75] for nonnormal variables. The default is FALSE.} \item{...}{For compatibility with generic. Ignored.} } @@ -92,9 +99,10 @@ tableOne ## Specifying nonnormal variables will show the variables appropriately, ## and show nonparametric test p-values. Specify variables in the exact -## argument to obtain the exact test p-values. +## argument to obtain the exact test p-values. cramVars can be used to +## show both levels for a 2-level categorical variables. print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), - exact = c("status","stage")) + exact = c("status","stage"), cramVars = "hepato") ## Use the summary.TableOne method for detailed summary summary(tableOne) diff --git a/man/tableone-package.Rd b/man/tableone-package.Rd index eea76e5..11081de 100644 --- a/man/tableone-package.Rd +++ b/man/tableone-package.Rd @@ -5,14 +5,14 @@ \title{Create "Table 1" to describe baseline characteristics} \description{ This package creates "Table 1", i.e., description of -baseline patient characteristics, which is essential every -medical research. This package provides functions to create -such summaries for continuous and categorical variables, -optionally with subgroups and groupwise comparison. The -package was insipired by and based on descriptive -statistics functions in Deducer, a Java-based GUI package -by Ian Fellows. This package does not require GUI or Java, -and intended for CUI users. +baseline patient characteristics, which is essential in +every medical research. This package provides functions to +create such summaries for continuous and categorical +variables, optionally with subgroups and groupwise +comparison. The package was insipired by and based on +descriptive statistics functions in Deducer, a Java-based +GUI package by Ian Fellows. This package does not require +GUI or Java, and intended for CUI users. } \note{ Special Thanks: @@ -23,7 +23,9 @@ package is based on. Hadley Wickham for packaging advice and for creating tools this package was made with (roxygen2, devtools, testthat). -Members of Facebook Organization of R Users for Medical +Yoshinobu Kanda for design advice. + +Members of the Facebook Organization of R Users for Medical Statistics in Japan (FORUMS-J) for testing pre-release versions. @@ -42,28 +44,32 @@ data(pbc) ## Check variables head(pbc) -## Make categorical variables factors -varsToFactor <- c("status","trt","ascites","hepato","spiders","edema","stage") -pbc[varsToFactor] <- lapply(pbc[varsToFactor], factor) +## List numerically coded categorical variables for later conversion. +## Factor variables are automatically handled as categorical variables. +factorVars <- c("status","trt","ascites","hepato","spiders","edema","stage") ## Create a variable list -dput(names(pbc)) +dput(names(pbc)) # This shows a character vector-creating syntax. vars <- c("time","status","age","sex","ascites","hepato", "spiders","edema","bili","chol","albumin", "copper","alk.phos","ast","trig","platelet", "protime","stage") -## Create Table 1 stratified by trt -tableOne <- CreateTableOne(vars = vars, strata = c("trt"), data = pbc) +## Create Table 1 stratified by trt. Use factorVars to convert numerically +## coded categorical variables as factors without changing the dataset. +tableOne <- CreateTableOne(vars = vars, strata = c("trt"), data = pbc, + factorVars = factorVars) ## Just typing the object name will invoke the print.TableOne method tableOne ## Specifying nonnormal variables will show the variables appropriately, ## and show nonparametric test p-values. Specify variables in the exact -## argument to obtain the exact test p-values. +## argument to obtain the exact test p-values. For two-level categorical +## variables specified in cramVars, both levels are shown. Use minMax +## argument to show median [min, max] for nonnormal variables. print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), - exact = c("status","stage")) + exact = c("status","stage"), cramVars = "sex") ## Use the summary.TableOne method for detailed summary summary(tableOne) @@ -80,7 +86,7 @@ summary(tableOne$ContTable) ## you may benefit from the quote argument. This will quote everything so that ## Excel does not mess up the cells. print(tableOne, nonnormal = c("bili","chol","copper","alk.phos","trig"), - exact = c("status","stage"), quote = TRUE) + exact = c("status","stage"), cramVars = "sex", quote = TRUE) } \author{ Kazuki Yoshida, Justin Bohn