diff --git a/DESCRIPTION b/DESCRIPTION
index 6031ca8e6..35d7b86b4 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: rtables
Title: Reporting Tables
-Version: 0.1.0.6
+Version: 0.1.1
Authors@R: c(
person("Adrian", "Waddell", email = "adrian.waddell@roche.com", role = c("aut", "cre"))
)
@@ -18,7 +18,7 @@ Suggests:
License: Apache License 2.0 | file LICENSE
Encoding: UTF-8
LazyData: true
-RoxygenNote: 6.1.0
+RoxygenNote: 6.1.1
VignetteBuilder: knitr
URL: https://github.com/roche/rtables, https://roche.github.io/rtables/
BugReports: https://github.com/roche/rtables/issues
diff --git a/NAMESPACE b/NAMESPACE
index f5aeab82d..0d858e86a 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -10,6 +10,7 @@ S3method(as_html,rrow)
S3method(as_html,rtable)
S3method(dim,rheader)
S3method(dim,rtable)
+S3method(levels,no_by)
S3method(names,rtable)
S3method(print,rcell)
S3method(print,rrow)
@@ -23,6 +24,7 @@ S3method(rtabulate,factor)
S3method(rtabulate,logical)
S3method(rtabulate,numeric)
S3method(toString,rtable)
+S3method(unlist,rtable)
export("header<-")
export(Viewer)
export(as.rtable)
@@ -30,10 +32,14 @@ export(as_html)
export(compare_rtables)
export(format_rcell)
export(header)
+export(header_add_N)
export(indented_row.names)
export(is.no_by)
export(list_rcell_format_labels)
export(no_by)
+export(order_rrows)
+export(order_rtables)
+export(rbindl_rtables)
export(rcell)
export(rheader)
export(rrow)
@@ -41,6 +47,8 @@ export(rrowl)
export(rtable)
export(rtablel)
export(rtabulate)
+export(sort_rrows)
+export(sort_rtables)
export(sprintf_format)
importFrom(htmltools,tagList)
importFrom(htmltools,tags)
diff --git a/NEWS.md b/NEWS.md
index 743bf8e59..12c1b61e9 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,16 +1,24 @@
-## rtable 0.1.0.6
+
+## rtables 0.1.1
+
+* `rtablulate` family of functions do not support the `row_*_data_args` arguments anymore. Instead, the `col_wise_args` argument is introduced.
+* add functions `order_rrows`, `sort_rrows`, `order_rtables`, and `sort_rtables` are introduced.
+* prevent `rtables` from being unlisted with `unlist.rtables`
+
+
+## rtables 0.1.0.6
* `Viewer` now also accepts objects of class `shiny.tag` (defined in package `htmltools`)
* `as.html` accepts `class.table`, `class.tr`, `class.th`, and `class.td` as an argument
-## rtable 0.1.0.5
+## rtables 0.1.0.5
* added `sprintf_format` for formatting rcells (thanks to Doug Kelkhoff for the suggestion)
* added `"(N=xx)"` and `">999.9"` format labels
-* `rtabulate` has now an argument `col_total` and shows by default the numbers of elements associated with a column
+* `rtabulate` has now an argument `col_N` and the function `col_N()`
-## rtable 0.1.0
+## rtables 0.1.0
Version `0.1.0` is a major re-design with lots of internal refactoring and the
following API changes:
@@ -37,6 +45,6 @@ single `rrow` and by setting `row.name` to `NULL`.
* `indented_row.names` function added
-## rtable 0.0.1
+## rtables 0.0.1
* Initial public release
\ No newline at end of file
diff --git a/R/accessors_modifiers.R b/R/accessors_modifiers.R
index b4db76762..5efefb0e9 100644
--- a/R/accessors_modifiers.R
+++ b/R/accessors_modifiers.R
@@ -243,62 +243,3 @@ set_rrow_attrs <- function(rrow, row.name, indent) {
rrow
}
-#' stack rtable objects
-#'
-#' @param ... \code{\link{rtable}} objects
-#'
-#' @return an \code{\link{rtable}} object
-#'
-#' @export
-#'
-#' @examples
-#'
-#' mtbl <- rtable(
-#' header = rheader(
-#' rrow(row.name = NULL, rcell("Sepal.Length", colspan = 2), rcell("Petal.Length", colspan=2)),
-#' rrow(NULL, "mean", "median", "mean", "median")
-#' ),
-#' rrow(
-#' row.name = "All Species",
-#' mean(iris$Sepal.Length), median(iris$Sepal.Length),
-#' mean(iris$Petal.Length), median(iris$Petal.Length),
-#' format = "xx.xx"
-#' )
-#' )
-#'
-#' mtbl2 <- with(subset(iris, Species == 'setosa'), rtable(
-#' header = rheader(
-#' rrow(row.name = NULL, rcell("Sepal.Length", colspan = 2), rcell("Petal.Length", colspan=2)),
-#' rrow(NULL, "mean", "median", "mean", "median")
-#' ),
-#' rrow(
-#' row.name = "Setosa",
-#' mean(Sepal.Length), median(Sepal.Length),
-#' mean(Petal.Length), median(Petal.Length),
-#' format = "xx.xx"
-#' )
-#' ))
-#'
-#' tbl <- rbind(mtbl, mtbl2)
-#'
-#' tbl
-#'
-rbind.rtable <- function(...) {
-
- dots <- Filter(Negate(is.null), list(...))
-
- if (!are(dots, "rtable")) stop("not all elements are of type rtable")
-
- headers <- lapply(dots, header)
- ref_header <- headers[[1]]
-
- same_headers <- vapply(headers[-1], function(h) {
- identical(h, ref_header)
- }, logical(1))
-
- if (!all(same_headers)) stop("not all rtables have the same header")
-
- body <- unlist(dots, recursive = FALSE)
-
- rtablel(header = ref_header, body)
-}
diff --git a/R/as_html.R b/R/as_html.R
index 9b0cdb1ce..a3be40262 100644
--- a/R/as_html.R
+++ b/R/as_html.R
@@ -44,14 +44,13 @@ as_html.default <- function(x, ...) {
stop("no as_html method for class ", class(x))
}
-# Convert an rtable object to html
-#
-# @param x an object of class \code{\link{rtable}}
-# @param class.table class attributes for the table in html
-# @param ... arguments passed on to methods
-#
-# @return an object of class \code{shinyTag}
-
+#' Convert an rtable object to html
+#'
+#' @inheritParams as_html
+#' @param class.table class attributes for \code{
} html object
+#'
+#' @return an object of class \code{shinyTag}
+#'
#' @export
as_html.rtable <- function(x, class.table = "table table-condensed table-hover",
...) {
diff --git a/R/header_add_N.R b/R/header_add_N.R
new file mode 100644
index 000000000..049da7ad4
--- /dev/null
+++ b/R/header_add_N.R
@@ -0,0 +1,34 @@
+
+#' Add N=xx to header
+#'
+#' Helper function used to add the population total (N) in the
+#' column header of \code{\link{rtable}} object.
+#'
+#' @param x \code{rtable}
+#' @param N vector with counts to be displayed in the header. The
+#' length must match the number of columns in \code{x}
+#'
+#' @export
+#'
+#' @examples
+#'
+#' tbl <- rtable(
+#' header = letters[1:3],
+#' rrow("X", 1, 2, 3),
+#' rrow("Y", 4, 5, 6)
+#' )
+#'
+#' tbl
+#'
+#' header_add_N(tbl, 1:3)
+#'
+header_add_N <- function(x, N) {
+ is(x, "rtable") || stop("x is expected to be an rtable")
+ length(N) == ncol(x) || stop("dimension missmatch")
+
+ header(x) <- rheader(
+ header(x)[[1]],
+ rrowl("", N, format = "(N=xx)")
+ )
+ x
+}
diff --git a/R/rbind.R b/R/rbind.R
new file mode 100644
index 000000000..07a4672fd
--- /dev/null
+++ b/R/rbind.R
@@ -0,0 +1,104 @@
+#' Stack rtable objects
+#'
+#' Note that the columns order are not mached by the header: the first table
+#' header is taken as the reference.
+#'
+#' @param ... \code{\link{rtable}} objects
+#' @param gap number of empty rows to add between tables
+#'
+#' @return an \code{\link{rtable}} object
+#'
+#' @export
+#'
+#' @examples
+#'
+#' mtbl <- rtable(
+#' header = rheader(
+#' rrow(row.name = NULL, rcell("Sepal.Length", colspan = 2), rcell("Petal.Length", colspan=2)),
+#' rrow(NULL, "mean", "median", "mean", "median")
+#' ),
+#' rrow(
+#' row.name = "All Species",
+#' mean(iris$Sepal.Length), median(iris$Sepal.Length),
+#' mean(iris$Petal.Length), median(iris$Petal.Length),
+#' format = "xx.xx"
+#' )
+#' )
+#'
+#' mtbl2 <- with(subset(iris, Species == 'setosa'), rtable(
+#' header = rheader(
+#' rrow(row.name = NULL, rcell("Sepal.Length", colspan = 2), rcell("Petal.Length", colspan=2)),
+#' rrow(NULL, "mean", "median", "mean", "median")
+#' ),
+#' rrow(
+#' row.name = "Setosa",
+#' mean(Sepal.Length), median(Sepal.Length),
+#' mean(Petal.Length), median(Petal.Length),
+#' format = "xx.xx"
+#' )
+#' ))
+#'
+#' tbl <- rbind(mtbl, mtbl2)
+#' tbl
+#'
+#' tbl <- rbind(mtbl, mtbl2, gap = 1)
+#' tbl
+#'
+#' tbl <- rbind(mtbl, mtbl2, gap = 2)
+#' tbl
+#'
+rbind.rtable <- function(..., gap = 0) {
+ dots <- Filter(Negate(is.null), list(...))
+ rbindl_rtables(dots, gap = gap)
+}
+
+#' Stack a list of rtables
+#'
+#' See \code{\link{rbind.rtable}} for details
+#'
+#' @param x a list of rtable objects
+#' @inheritParams rbind.rtable
+#'
+#' @export
+#'
+rbindl_rtables <- function(x, gap = 0) {
+
+ stopifnot(is.list(x))
+ stopifnot(are(x, "rtable"))
+ stopifnot(length(x) > 0)
+ stopifnot(is.numeric(gap), gap >= 0)
+
+ if (!num_all_equal(vapply(x, ncol, numeric(1))))
+ stop("non-matching number of columns between tables")
+
+ tbl <- if (gap != 0) {
+ gap_rows <- replicate(gap, rrow(), simplify = FALSE)
+ Reduce(function(tbl1, tbl2) c(tbl1, gap_rows, tbl2), x)
+ } else {
+ unlist(x, recursive = FALSE)
+ }
+
+ ref_header <- header(x[[1]])
+ class(tbl) <- "rtable"
+ attr(tbl, "header") <- ref_header
+ attr(tbl, "nrow") <- length(tbl)
+ attr(tbl, "ncol") <- ncol(ref_header)
+
+ tbl
+}
+
+
+#' Unlist method for rtables
+#'
+#' rtable objects should not be unlisted. This allows us to create nested lists with rtables objects and then flatten
+#' them to a list of rtable objects.
+#'
+#' @inheritParams base::unlist
+#'
+#' @return rtable object
+#'
+#' @method unlist rtable
+#' @export
+unlist.rtable <- function(x, recursive = TRUE, use.names = TRUE) {
+ x
+}
diff --git a/R/rtabulate.R b/R/rtabulate.R
index 4e4b2c6c9..e320026d7 100644
--- a/R/rtabulate.R
+++ b/R/rtabulate.R
@@ -5,13 +5,13 @@
#' \code{\link{rtable}}s. Conceptually the \code{rtabulate} has it's origin in
#' \code{\link{tapply}}.
#'
-#' The data is split into cell-data and a function can be specified that return
+#' The data is split into cell-data and a function can be specified that returns
#' a data structre (or \code{\link{rcell}}).
#'
#' @param x either a vector or \code{data.frame}
#' @param ... arguments passed to methods
#'
-#' @return an \code{\link{rtable}} project
+#' @return an \code{\link{rtable}} object
#'
#' @author Adrian Waddell
#'
@@ -22,20 +22,26 @@ rtabulate <- function(x, ...) {
}
-#' Do not split data into columns or row in \code{rtabulate}
+#' Do not split data into columns in \code{rtabulate}
#'
-#' \code{\link{rtabulate}} has the arguments \code{col_by} and \code{row_by}
+#' \code{\link{rtabulate}} has the argument \code{col_by}
#' which can either take a vector or if no splitting is needed the return value
-#' of \code{no_by}.
+#' of \code{no_by}. Using \code{no_by} creates a table with a single
+#' column.
#'
-#' @param name row name or column name
+#' @param name character column name to display in the table header
#'
#' @export
#'
+#' @examples
+#'
+#' rtabulate(iris$Species, col_by = no_by("Total"))
+
no_by <- function(name) {
structure(name, class = "no_by")
}
+
#' Check if object inherits from the \code{no_by} Class
#'
#' Functions to test inheritance on \code{no_by}
@@ -49,93 +55,90 @@ is.no_by <- function(x) {
is(x, "no_by")
}
-rtabulate_header <- function(col_by, Ntot, N=NULL, format="(N=xx)") {
- if (!is.factor(col_by) && !is.no_by(col_by)) stop("col_by is required to be a factor or no_by object")
-
- lvls <- if (is.no_by(col_by)) as.vector(col_by) else levels(col_by)
-
- if (is.null(format)) {
- rheader(lvls)
- } else {
- if (is.null(N)) {
- N <- if (is.no_by(col_by)) Ntot else table(col_by)
- }
- rheader(
- rrowl("", lvls),
- rrowl("", unname(N), format = format)
- )
- }
+#' Access levels attribute for an object of \code{no_by} Class
+#'
+#' @param x \code{no_by} class object
+#'
+#' @export
+#'
+levels.no_by <- function(x) {
+ as.vector(x)
}
# rtabulate default for vectors
#
-# this method is used for vectors of type \code{logical} and \code{numeric}
+# This method is used for vectors of type \code{logical} and \code{numeric}
#
# see parameter descrition for rtabulate.numeric
#
-rtabulate_default <- function(x, col_by = no_by("col_1"), FUN = NULL, ..., row_data_arg=FALSE,
- format = NULL, row.name = "", indent = 0, col_total = "(N=xx)") {
-
- if (is.null(FUN)) stop("FUN is required")
-
- stop_if_has_na(col_by)
+rtabulate_default <- function(x, col_by = no_by("col_1"), FUN, ...,
+ format = NULL, row.name = "", indent = 0,
+ col_wise_args = NULL) {
- tbl_header <- rtabulate_header(col_by, length(x), format=col_total)
+ force(FUN)
+ check_stop_col_by(col_by, col_wise_args)
- xs <- if (is.no_by(col_by)) {
+ column_data <- if (is.no_by(col_by)) {
setNames(list(x), col_by)
} else {
if (length(x) != length(col_by)) stop("dimension missmatch x and col_by")
split(x, col_by, drop = FALSE)
}
-
- col_data <- if (row_data_arg) {
- lapply(xs, FUN, x, ...)
+
+ cells <- if (is.null(col_wise_args)) {
+
+ lapply(column_data, FUN, ...)
+
} else {
- lapply(xs, FUN, ...)
+
+ dots <- list(...)
+ args <- lapply(seq_len(nlevels(col_by)), function(i) c(dots, lapply(col_wise_args, `[[`, i)))
+
+ Map(function(xi, argsi) {
+ do.call(FUN, c(list(xi), argsi))
+ }, column_data, args)
}
- rr <- rrowl(row.name = row.name, col_data, format = format, indent = indent)
-
- rtable(header = tbl_header, rr)
+ rr <- rrowl(row.name = row.name, cells, format = format, indent = indent)
+ rtable(header = levels(col_by), rr)
}
-#' tabulate a numeric vector
-#'
-#' by default the \code{\link[stats]{fivenum}} function is applied to the
-#' vectors associated to the cells
-#'
-#'
+
+
+#' Tabulate a numeric vector
+#'
+#' By default each cell reports the mean based on the associated vector.
+#'
+#'
#' @inheritParams rrow
#' @param x a vecor
#' @param col_by a \code{\link{factor}} of length \code{nrow(x)} that defines
#' which levels in \code{col_by} define a column. If data should not be split
#' into columns use the \code{\link{no_by}} function.
-#' @param FUN a function that processes the cell data, if \code{row_data_arg} is
-#' set to \code{TRUE} the a second argument with the row data is passed to
-#' \code{FUN}
+#' @param FUN a function that processes the cell data
#' @param ... arguments passed to \code{FUN}
-#' @param row_data_arg call \code{FUN} with the row data as the second argument
#' @param format if \code{FUN} does not return a formatted \code{\link{rcell}}
#' then the \code{format} is applied
#' @param row.name if \code{NULL} then the \code{FUN} argument is deparsed and
#' used as \code{row.name} of the \code{\link{rrow}}
-#' @param col_total a format string for displaying the number of elements in the
-#' column header. If \code{NULL} then no header row for the column is
-#' displayed.
-#'
+#' @param col_wise_args a named list containing collections (e.g. vectors or
+#' lists) with data elements for each column of the resulting table. The data
+#' elements are then passed to the named argument \code{FUN} corresponding to
+#' the element name of the outer list. Hence, the length and order of each
+#' collection must match the levels in \code{col_by}. See examples.
+#'
#' @inherit rtabulate return
-#'
+#'
#' @export
-#'
-#' @examples
+#'
+#' @examples
#'
#' rtabulate(iris$Sepal.Length)
-#'
+#'
#' rtabulate(iris$Sepal.Length, col_by = no_by("Sepal.Length"))
-#'
-#' with(iris, rtabulate(x = Sepal.Length, col_by = Species, row.name = "fivenum"))
+#'
+#' with(iris, rtabulate(x = Sepal.Length, col_by = Species, row.name = "mean"))
#'
#' SL <- iris$Sepal.Length
#' Sp <- iris$Species
@@ -146,19 +149,31 @@ rtabulate_default <- function(x, col_by = no_by("col_1"), FUN = NULL, ..., row_d
#' rtabulate(SL, Sp, range, format = "xx.xx - xx.xx", row.name = "Min - Max")
#' )
#'
+#' x <- 1:100
+#' cb <- factor(rep(LETTERS[1:3], c(20, 30, 50)))
#'
+#' rtabulate(
+#' x = x, col_by = cb, FUN = function(x, N) list(mean(x), sd(x), N),
+#' format = sprintf_format("%.2f (%.2f) and %i"), row.name = "Mean (SD) and N",
+#' col_wise_args = list(N = table(cb))
+#' )
#'
rtabulate.numeric <- function(x, col_by = no_by("col_1"), FUN = mean, ...,
- row_data_arg = FALSE, format = NULL, row.name = NULL,
- indent = 0, col_total = "(N=xx)") {
- if (is.null(row.name)) row.name <- paste0(deparse(substitute(FUN)))
- rtabulate_default(x = x, col_by = col_by, FUN = FUN, ...,
- row_data_arg = row_data_arg, format = format,
- row.name = row.name, indent = indent,
- col_total = col_total)
+ format = NULL, row.name = NULL,
+ indent = 0, col_wise_args = NULL) {
+
+ if (is.null(row.name)) row.name <- paste(deparse(substitute(FUN)), collapse = ";")
+
+ rtabulate_default(
+ x = x, col_by = col_by, FUN = FUN, ...,
+ format = format, row.name = row.name, indent = indent,
+ col_wise_args = col_wise_args
+ )
}
-#' tabulate a logical vector
+#' Tabulate a logical vector
+#'
+#' By default each cell reports the number of \code{TRUE} observations from the associated vector.
#'
#' @inheritParams rtabulate.numeric
#'
@@ -169,43 +184,51 @@ rtabulate.numeric <- function(x, col_by = no_by("col_1"), FUN = mean, ...,
#' @examples
#' rtabulate(iris$Species == "setosa")
#'
-#' rtabulate(iris$Species == "setosa", no_by("Species"), row.name = "n (n/N)")
+#' rtabulate(iris$Species == "setosa", no_by("Species"),
+#' FUN = function(x, N) list(sum(x), sum(x)/N),
+#' row.name = "n (n/N)",
+#' col_wise_args = list(N = 150))
#'
-#' # default: percentages equal \code{TRUE}
+#' # default FUN is number of observations equal to TRUE
#' with(iris, rtabulate(Sepal.Length < 5, Species, row.name = "Sepal.Length < 5"))
#'
-#' # precentages with proportion of cell number of \code{TRUE}s to overvall
-#' # number of \code{TRUE}s
-#' with(iris, rtabulate(Sepal.Length < 5, Species, row.name = "Sepal.Length < 5",
-#' FUN = function(cell_data, row_data) sum(cell_data) * c(1, 1/sum(row_data)),
-#' row_data_arg = TRUE
+#' # Custom FUN: number of TRUE records in a cell and precentages based on number of records
+#' # in each column
+#' with(iris, rtabulate(Sepal.Length < 5, Species,
+#' FUN = function(xi, N) sum(xi) * c(1, 1/N),
+#' format = "xx.xx (xx.xx%)",
+#' row.name = "Sepal.Length < 5",
+#' col_wise_args = list(N = table(Species))
#' ))
#'
rtabulate.logical <- function(x, col_by = no_by("col_1"),
- FUN = function(x) sum(x) * c(1, 1/length(x)),
+ FUN = sum,
...,
- row_data_arg = FALSE,
- format = "xx.xx (xx.xx%)",
- row.name = "",
+ format = NULL,
+ row.name = NULL,
indent = 0,
- col_total = "(N=xx)"
+ col_wise_args = NULL
) {
- if (is.null(row.name)) row.name <- paste0(deparse(substitute(FUN)))
- rtabulate_default(x = x, col_by = col_by, FUN = FUN, ...,
- row_data_arg = row_data_arg, format = format,
- row.name = row.name, indent = indent,
- col_total = col_total)
+
+ if (is.null(row.name)) row.name <- paste(deparse(substitute(FUN)), collapse = ";")
+
+ rtabulate_default(
+ x = x, col_by = col_by, FUN = FUN, ...,
+ format = format, row.name = row.name, indent = indent,
+ col_wise_args = col_wise_args
+ )
}
#' Tabulate Factors
#'
+#' By default each cell reports the number of observations in
+#' each level of \code{x}.
+#'
#' @inheritParams rtabulate.numeric
-#' @param row_col_data_args boolean, if \code{TRUE} then \code{FUN} is called
-#' with the first three arguments being the cell, row, and column data,
-#' respectively
-#' @param useNA boolean, if \code{TRUE} then \code{NA} values in \code{x} get
-#' turned into a factor level \code{"NA"}, if \code{FALSE} then the \code{NA}
-#' values in \code{x} get dropped.
+#' @param useNA either one of ("no", "ifany", "always"). If \code{"no"} then \code{NA} values
+#' in \code{x} get dropped. When \code{"ifany"} is used a row for \code{NA} values is
+#' included in the summary if any \code{NA}s exist in \code{x}. For option \code{"always"}
+#' \code{NA} values are always included in the summary even if none exist in \code{x}.
#'
#' @inherit rtabulate return
#'
@@ -221,40 +244,51 @@ rtabulate.logical <- function(x, col_by = no_by("col_1"),
#' labels = c("S.L > 5", "S.L <= 5"))
#'
#' rtabulate(iris$Species, col_by=sl5)
+#' rtabulate(sl5, iris$Species)
#'
#' rtabulate(iris$Species, col_by=sl5,
-#' FUN = function(cell_data, row_data, col_data) {
+#' FUN = function(cell_data, N) {
#' if (length(cell_data) > 10) {
-#' length(cell_data) * c(1, 1/length(col_data))
+#' length(cell_data) * c(1, 1/N)
#' } else {
#' rcell("-", format = "xx")
#' }
#' },
-#' row_col_data_args = TRUE,
-#' format = "xx (xx.xx%)"
+#' format = "xx (xx.xx%)",
+#' col_wise_args = list(N = table(sl5))
+#' )
+#'
+#' rtabulate(x = factor(c("X", "Y"), c("X", "Y")),
+#' col_by = factor(c("a", "a"), c("a", "b")), FUN = length)
+#'
+#' rtabulate(factor(c("Y", "Y"), c("X", "Y")),
+#' factor(c("b", "b"), c("a", "b")), length)
+#'
+#' rtabulate(
+#' x = factor(c("Y", "Y"), c("X", "Y")),
+#' col_by = factor(c("b", "b"), c("a", "b")),
+#' FUN = function(x, N) list(length(x), N),
+#' col_wise_args = list(N = c(1,2))
#' )
#'
-#' rtabulate(sl5, iris$Species)
#'
rtabulate.factor <- function(x,
col_by = no_by("col_1"),
FUN = length,
...,
- row_col_data_args = FALSE,
useNA = c("no", "ifany", "always"),
- format = "xx",
+ format = NULL,
indent = 0,
- col_total = "(N=xx)") {
+ col_wise_args = NULL) {
- stop_if_has_na(col_by)
+ force(FUN)
+ check_stop_col_by(col_by, col_wise_args)
useNA <- match.arg(useNA)
- tbl_header <- rtabulate_header(col_by, length(x), format=col_total)
+ if (any("" %in% levels(x))) stop("factor with level '' is not valid in rtabulate.factor")
if (useNA %in% c("ifany", "always")) {
- if (any("" %in% levels(x))) stop("cannot use useNA='ifany' or 'always' if there any levels called ")
-
if (useNA == "always" || any(is.na(x))) {
levels(x) <- c(levels(x), "")
x[is.na(x)] <- ""
@@ -267,48 +301,43 @@ rtabulate.factor <- function(x,
levels(x) <- gsub("^$", "-", levels(x))
warning("'' levels were turned into level -")
}
-
- row_data_list <- split(x, x, drop = FALSE)
- cell_data <- if (is.no_by(col_by)) {
- lapply(row_data_list, function(row_i) setNames(list(row_i), col_by))
+
+ # cell_data = list(row1 = list(col1, col2, ...), row2 = list(col1, col2, ...), ...)
+ cell_data_by_row <- if (is.no_by(col_by)) {
+ lapply(split(x, x, drop = FALSE), function(row_i) setNames(list(row_i), col_by))
} else {
if (length(x) != length(col_by)) stop("dimension missmatch x and col_by")
-
df <- data.frame(
x = x,
- col_by = factor(col_by)
+ col_by = col_by
)
lapply(split(df, df$x, drop = FALSE), function(row_i) {
split(row_i$x, row_i$col_by, drop = FALSE)
})
}
- rrow_data <- if (!row_col_data_args) {
- lapply(cell_data, function(row_i) lapply(row_i, FUN, ...))
+
+ cells_by_row <- if (is.null(col_wise_args)) {
+
+ lapply(cell_data_by_row, function(row_i) lapply(row_i, FUN, ...))
+
} else {
- col_data_list <- if (is.no_by(col_by)) {
- setNames(list(x), col_by)
- } else {
- split(x, col_by, drop = FALSE)
- }
-
- rrow_data_tmp <- lapply(1:length(row_data_list), function(i) {
- rrow_data_i <- lapply(1:length(col_data_list), function(j) {
- FUN(cell_data[[i]][[j]], row_data_list[[i]], col_data_list[[j]], ...)
- })
- names(rrow_data_i) <- names(col_data_list)
- rrow_data_i
+ dots <- list(...)
+ args <- lapply(seq_len(nlevels(col_by)), function(i) c(dots, lapply(col_wise_args, `[[`, i)))
+
+ lapply(cell_data_by_row, function(row_i) {
+ Map(function(xi, argsi) {
+ do.call(FUN, c(list(xi), argsi))
+ }, row_i, args)
})
- names(rrow_data_tmp) <- names(row_data_list)
- rrow_data_tmp
}
rrows <- Map(function(row, rowname) rrowl(rowname, row, format = format, indent = indent),
- rrow_data, names(rrow_data))
+ cells_by_row, names(cells_by_row))
- rtablel(header = tbl_header, rrows)
+ rtablel(header = levels(col_by), rrows)
}
@@ -317,8 +346,9 @@ rtabulate.factor <- function(x,
#' Split data.frame and apply functions
#'
#' @inheritParams rtabulate.factor
-#' @param row_by_var name of factor variable in \code{x}
-#' @param col_by_var name of factor variable in \code{x}
+#' @param x data.frame
+#' @param row_by name of factor variable in \code{x}
+#' @param col_by name of factor variable in \code{x}
#'
#'
#' @inherit rtabulate return
@@ -333,8 +363,8 @@ rtabulate.factor <- function(x,
#'
#' rtabulate(
#' x = df,
-#' row_by_var = "aaa",
-#' col_by_var = "bbb",
+#' row_by = df$aaa,
+#' col_by = df$bbb,
#' FUN = function(x) {
#' sum(x$val)
#' }
@@ -342,112 +372,120 @@ rtabulate.factor <- function(x,
#'
#' rtabulate(
#' x = iris,
-#' row_by_var = no_by("sum"),
-#' col_by_var = "Species",
+#' row_by = no_by("sum"),
+#' col_by = iris$Species,
#' FUN = function(x) sum(x$Sepal.Length)
#' )
#'
#' rtabulate(
#' x = iris,
-#' row_by_var = "Species",
-#' col_by_var = no_by("sum"),
+#' row_by = iris$Species,
+#' col_by = no_by("sum"),
#' FUN = function(x) sum(x$Sepal.Length)
#' )
#'
-#' tbl <- rtabulate(
-#' x = iris,
-#' FUN = function(cell_data) c(sum(cell_data$Sepal.Length), sd(cell_data$Sepal.Length)),
-#' format = "xx.xx (xx.xx%)"
-#' )
-#'
-#' tbl
-#'
-#' row.names(tbl)
-#' row.names(tbl) <- "Sum of Sepal Length"
-#'
-#' tbl
-#'
-#' iris2 <- iris
-#' iris2$fsl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE),
+#' fsl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE),
#' labels = c("S.L > 5", "S.L <= 5"))
#'
#' tbl <- rtabulate(
-#' x = iris2,
-#' row_by_var = "fsl5",
-#' col_by_var = "Species",
-#' FUN = function(x_cell, x_row, x_col) {
+#' x = iris,
+#' row_by = fsl5,
+#' col_by = iris$Species,
+#' FUN = function(x_cell) {
#' if (nrow(x_cell) < 10) {
#' rcell("-")
#' } else {
#' fit <- lm(Sepal.Length ~ Petal.Width, data = x_cell)
-#' m_col <- mean(x_col$Sepal.Length)
-#' m_row <- mean(x_row$Sepal.Length)
-#'
-#' rcell(list(fit, m_col, m_row), format = function(x, output) {
-#' paste("df:", x[[1]]$df.residual,", and", round(x[[2]],1), ", and", round(x[[3]],2))
+#'
+#' rcell(list(fit), format = function(x, output) {
+#' paste("df:", x[[1]]$df.residual)
#' })
#' }
-#' } ,
-#' row_col_data_args = TRUE
+#' }
#' )
#' tbl
+#'
+#' rtabulate(
+#' x = iris,
+#' row_by = fsl5,
+#' col_by = iris$Species,
+#' FUN = function(x_cell, N) {
+#' N
+#' },
+#' col_wise_args = list(N = c(10, 100, 200))
+#' )
+#'
+#'
+#'
rtabulate.data.frame <- function(x,
- row_by_var = no_by("row_1"),
- col_by_var = no_by("col_1"),
- FUN = nrow,
+ row_by,
+ col_by,
+ FUN,
...,
- row_col_data_args = FALSE,
- format = "xx",
+ format = NULL,
indent = 0,
- col_total = "(N=xx)") {
+ col_wise_args = NULL) {
- if (!is.no_by(row_by_var) && !is.factor(x[[row_by_var]])) stop("x[[row_by_var]] currently needs to be a factor")
- if (!is.no_by(col_by_var) && !is.factor(x[[col_by_var]])) stop("x[[col_by_var]] currently needs to be a factor")
+ force(FUN)
+ check_stop_col_by(col_by, col_wise_args)
+ check_stop_col_by(row_by)
- tbl_header <- rtabulate_header(if (is.no_by(col_by_var)) col_by_var else x[[col_by_var]], nrow(x), format=col_total)
-
- row_data <- if (is.no_by(row_by_var)) {
- setNames(list(x), row_by_var)
- } else {
- split(x, x[[row_by_var]], drop = FALSE)
- }
-
- col_data <- if (is.no_by(col_by_var)) {
- setNames(list(x), col_by_var)
- } else {
- split(x, x[[col_by_var]], drop = FALSE)
- }
-
- cell_data <- if (is.no_by(col_by_var)) {
- lapply(row_data, function(row_i) setNames(list(row_i), col_by_var))
+
+ # list(row1 = list(c1, c2, ...), row2 = list(c1, c2, ...), ...)
+ cell_data <- if (!is.no_by(row_by) && !is.no_by(col_by)) {
+ xs <- split(x, row_by, drop = FALSE)
+ cs <- split(col_by, row_by, drop = FALSE)
+ setNames(Map(function(xi, col_by_i) split(xi, col_by_i), xs, cs), levels(row_by))
+ } else if (is.no_by(row_by) && !is.no_by(col_by)) {
+ setNames(list(split(x, col_by, drop = FALSE)), row_by)
+ } else if (!is.no_by(row_by) && is.no_by(col_by)) {
+ lapply(split(x, row_by, drop = FALSE), function(xi) list(xi))
+ } else if (is.no_by(row_by) && is.no_by(col_by)) {
+ setNames(list(list(x)), row_by)
} else {
- lapply(row_data, function(row_i) split(row_i, row_i[[col_by_var]], drop = FALSE))
+ stop("unexpected col_by & row_by combination")
}
+
- rrow_data <- if (!row_col_data_args) {
+ cells_by_row <- if (is.null(col_wise_args)) {
lapply(cell_data, function(row_i) lapply(row_i, FUN, ...))
} else {
- rrow_data_tmp <- lapply(1:length(row_data), function(i) {
- rrow_data_i <- lapply(1:length(col_data), function(j) {
- FUN(cell_data[[i]][[j]], row_data[[i]], col_data[[j]], ...)
- })
- names(rrow_data_i) <- names(col_data)
- rrow_data_i
+ dots <- list(...)
+ args <- lapply(seq_len(nlevels(col_by)), function(i) c(dots, lapply(col_wise_args, `[[`, i)))
+
+ lapply(cell_data, function(row_i) {
+ Map(function(xi, argsi) {
+ do.call(FUN, c(list(xi), argsi))
+ }, row_i, args)
})
- names(rrow_data_tmp) <- names(row_data)
- rrow_data_tmp
+
}
- rrows <- Map(function(row_dat, rowname) {
- rrowl(row.name = rowname, row_dat, format = format, indent = indent)
- }, rrow_data, names(rrow_data))
+ rrows <- Map(function(cells_row, rowname) {
+ rrowl(row.name = rowname, cells_row, format = format, indent = indent)
+ }, cells_by_row, names(cells_by_row))
- rtablel(header = tbl_header, rrows)
+ rtablel(header = levels(col_by), rrows)
}
+check_stop_col_by <- function(col_by, col_wise_args = NULL) {
+ if (!is.factor(col_by) && !is.no_by(col_by)) stop("col_by is required to be a factor or no_by object")
+ if (any(is.na(col_by))) stop("col_by does currently not support any NAs")
+
+ if (!is.null(col_wise_args)) {
+ if (!is.list(col_wise_args))
+ stop("col_wise_args needs to be either a list or NULL")
+
+ if (!all(vapply(col_wise_args, length, numeric(1)) == nlevels(col_by)))
+ stop("not all elements in col_wise_args have length ", nlevels(col_by))
+ }
+ TRUE
+}
+
+
stop_if_has_na <- function(x) {
if (any(is.na(x))) stop(paste0(deparse(substitute(x)), " does currently not support any NAs"))
}
diff --git a/R/sort.R b/R/sort.R
new file mode 100644
index 000000000..9391a9ce7
--- /dev/null
+++ b/R/sort.R
@@ -0,0 +1,215 @@
+
+
+#' Sort rrows in rtable
+#'
+#' Return order of rows in rtable based on derived values within each row.
+#'
+#' @param x rtable object
+#' @param indices of column and cell within a row to access value
+#' used for sorting. If the column index is 0 then the sum of the cell
+#' values across all columns is derived and used for sorting.
+#' If \code{indices} is a function then the function argument is
+#' rrow object and the user must specify how to extract the information
+#' required to sort the rows. See examples.
+#' @param ... arguments passed on to \code{\link{order}}
+#'
+#' @export
+#'
+#' @return order of rows
+#'
+#' @seealso \code{\link{sort_rrows}}, \code{\link{order_rtables}},
+#' \code{\link{sort_rtables}}
+#'
+#' @examples
+#' tbl <- rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(3,1), c(9,8,19)),
+#' rrow("r2", c(4,-1), c(8,9,21)),
+#' rrow("r3", c(1,3), c(3,2,22))
+#' )
+#'
+#' order_rrows(tbl, c(1,1))
+#' order_rrows(tbl, c(2, 3), decreasing = TRUE)
+#' order_rrows(tbl, c(0,2))
+#'
+#' order_rrows(tbl, function(row) row[[2]][3] - row[[1]][1])
+
+order_rrows <- function(x, indices = c(1, 1), ...) {
+
+ stopifnot(is(x, "rtable"))
+
+ vals <- if (is.atomic(indices)) {
+ stopifnot(is.numeric(indices))
+ stopifnot(length(indices) == 2)
+
+ icol <- indices[1]
+ icell <- indices[2]
+
+ stopifnot(icell > 0)
+ stopifnot(icol >= 0)
+
+ if (icol == 0) {
+ vapply(x, function(row) {sum(vapply(row, `[[`, numeric(1), icell))} , numeric(1))
+ } else {
+ vapply(x, function(row) row[[icol]][[icell]], numeric(1))
+ }
+ } else if (is.function(indices)) {
+ vapply(x, indices, numeric(1))
+ } else {
+ stop("index is either an atomic vector of a function")
+ }
+ order(vals, ...)
+}
+
+#' Sort Rows in rtable
+#'
+#' Sort rows in rtable based on derived values within each row.
+#'
+#' @inheritParams order_rrows
+#'
+#' @return object of class rtable
+#'
+#' @export
+#'
+#' @seealso \code{\link{order_rrows}}, \code{\link{order_rtables}},
+#' \code{\link{sort_rtables}}
+#'
+#' @examples
+#' tbl <- rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(3,1), c(9,8,19)),
+#' rrow("r2", c(4,-1), c(8,9,21)),
+#' rrow("r3", c(1,3), c(3,2,22))
+#' )
+#'
+#' sort_rrows(tbl, c(1, 1))
+#' sort_rrows(tbl, c(0, 1))
+#'
+#' sort_rrows(tbl, c(2, 3), decreasing = FALSE)
+#' sort_rrows(tbl, c(2, 3), decreasing = TRUE)
+#'
+#' sort_rrows(tbl, function(row) row[[2]][3] - row[[1]][1])
+#'
+sort_rrows <- function(x, indices = c(1, 1), ...) {
+ x[order_rrows(x, indices , ...), ]
+}
+
+
+#' Order of rtables in a list
+#'
+#' Return the order of rtables in a list based on values within the tables.
+#'
+#' @inheritParams sort_rrows
+#' @param x a list of rtables
+#' @param indices of row, column, and cell to access a value within each rtable that
+#' is used for sorting the list \code{x}. If the column index is 0 then a sum of the
+#' cell values across all columns within the specified row is taken. If \code{indices}
+#' is a function then the function argument is rtable object and the user must
+#' specify how to extract the information required to sort \code{x}. See examples.
+#'
+#' @return order of rtables
+#'
+#' @export
+#'
+#' @seealso \code{\link{sort_rtables}}, \code{\link{sort_rrows}},
+#' \code{\link{order_rrows}}
+#'
+#' @examples
+#'
+#' tbls <- list(
+#' rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(4,1), c(9,1,19)),
+#' rrow("r2", c(5,-1), c(8,3,21)),
+#' rrow("r3", c(1,3), c(3,4,22))
+#' ),
+#' rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(6,1), c(9,2,19)),
+#' rrow("r2", c(5,-1), c(8,4,21)),
+#' rrow("r3", c(1,3), c(3,5,22))
+#' ),
+#' rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(1,1), c(100,0,19)),
+#' rrow("r2", c(5,-1), c(8,1,21)),
+#' rrow("r3", c(1,3), c(3,1,22))
+#' )
+#' )
+#'
+#' order_rtables(tbls, c(1,1,1))
+#' order_rtables(tbls, c(1,1,1), decreasing = TRUE)
+#'
+#' order_rtables(tbls, c(1,0,1))
+#'
+#' order_rtables(tbls, function(tbl) tbl[[1]][[2]][3] - tbl[[1]][[1]][1] )
+
+order_rtables <- function(x, indices = c(1,0,1), ...) {
+
+ stopifnot(is.list(x) && are(x, "rtable"))
+
+ vals <- if (is.function(indices)) {
+ vapply(x, indices, numeric(1))
+ } else if (is.atomic(indices) && length(indices) == 3) {
+ irow <- indices[1]
+ icol <- indices[2]
+ icell <- indices[3]
+
+ if (icol == 0) {
+ vapply(x, function(tbl) {sum(vapply(tbl[[irow]], `[[`, numeric(1), icell))} , numeric(1))
+ } else {
+ vapply(x, function(tbl) tbl[[irow]][[icol]][[icell]], numeric(1))
+ }
+
+ } else {
+ stop("indices needs to be a vector of length 3 or a function")
+ }
+ order(vals, ...)
+}
+
+#' Sort rtables within a list
+#'
+#' Return a sorted list of rtables in a list based on values within the tables.
+#'
+#' @inheritParams order_rtables
+#'
+#' @return list of rtables
+#'
+#' @export
+#'
+#' @seealso \code{\link{order_rtables}}, \code{\link{sort_rrows}},
+#' \code{\link{order_rrows}}
+#'
+#' @examples
+#'
+#' tbls <- list(
+#' "Table A" = rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(4,1), c(9,1,19)),
+#' rrow("r2", c(5,-1), c(8,3,21)),
+#' rrow("r3", c(1,3), c(3,4,22))
+#' ),
+#' "Table B" = rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(6,1), c(9,2,19)),
+#' rrow("r2", c(5,-1), c(8,4,21)),
+#' rrow("r3", c(1,3), c(3,5,22))
+#' ),
+#' "Table C" = rtable(
+#' header = c("A", "B"),
+#' rrow("r1", c(1,1), c(100,0,19)),
+#' rrow("r2", c(5,-1), c(8,1,21)),
+#' rrow("r3", c(1,3), c(3,1,22))
+#' )
+#' )
+#'
+#' sort_rtables(tbls, c(1,1,1))
+#' sort_rtables(tbls, c(1,1,1), decreasing = TRUE)
+#'
+#' sort_rtables(tbls, c(1,0,1))
+#'
+#' sort_rtables(tbls, function(tbl) tbl[[1]][[2]][3] - tbl[[1]][[1]][1] )
+#'
+sort_rtables <- function(x, indices = c(1,0,1), ...) {
+ x[order_rtables(x, indices , ...)]
+}
diff --git a/R/utils.R b/R/utils.R
index c82d65640..d77272dc0 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -3,4 +3,13 @@
## is each object in a collection from a class
are <- function(object_collection, class2) {
all(vapply(object_collection, is, logical(1), class2))
-}
\ No newline at end of file
+}
+
+num_all_equal <- function(x, tol = .Machine$double.eps ^ 0.5) {
+ stopifnot(is.numeric(x))
+
+ if (length(x) == 1) return(TRUE)
+
+ y <- range(x) / mean(x)
+ isTRUE(all.equal(y[1], y[2], tolerance = tol))
+}
diff --git a/README.Rmd b/README.Rmd
index ef4b6e1d3..616d67790 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -54,7 +54,7 @@ repository and to send us pull requests with the suggested improvements.
To install the stable release of `rtables` package run the following command in `R`:
```{r, eval = FALSE}
-devtools::install_github("roche/rtables", ref="v0.1.0")
+devtools::install_github("roche/rtables", ref="v0.1.1")
```
To install the test version of `rtables` run
diff --git a/_pkgdown.yml b/_pkgdown.yml
index d50e040c2..d4996660f 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -8,7 +8,7 @@ navbar:
href: https://github.com/roche/rtables
development:
- mode: devel
+ mode: release
reference:
- title: Core Table Create Functions
@@ -22,27 +22,46 @@ reference:
- rrowl
- title: Cell Formatting related Functions
contents:
+ - sprintf_format
- format_rcell
- list_rcell_format_labels
- - title: Accessor Functions
+ - title: Accessor and Modification Functions
desc: Access and Modify Functions
contents:
- starts_with("nam")
- starts_with("row.name")
- "[.rtable"
+ - "[.rheader"
- dim.rtable
- rbind.rtable
+ - rbindl_rtables
+ - header
+ - header<-
+ - dim.rheader
+ - header_add_N
+ - indented_row.names
+ - unlist.rtable
- title: Tabulation Functions
desc: rtabulate is a framework to derive rtables
contents:
- starts_with("rtabulate")
- no_by
- is.no_by
+ - levels.no_by
+ - title: Table sorting functions
+ desc: funtions to sort rows within tables and list of tables
+ contents:
+ - order_rrows
+ - sort_rrows
+ - order_rtables
+ - sort_rtables
- title: Output Functions
desc: These functions create ascii or html representations of the table
contents:
- toString.rtable
- as_html
+ - as_html.default
+ - as_html.rtable
- title: Coercion Functions
contents:
- as.rtable
@@ -52,7 +71,4 @@ reference:
- Viewer
- compare_rtables
-articles:
- contents:
- - rtabulate
diff --git a/docs/ISSUE_TEMPLATE.html b/docs/ISSUE_TEMPLATE.html
index 51742b506..0e65d1c1c 100644
--- a/docs/ISSUE_TEMPLATE.html
+++ b/docs/ISSUE_TEMPLATE.html
@@ -1,6 +1,6 @@
-
+
@@ -9,27 +9,34 @@
Reporting an Issue with rtables • rtables
-
+
-
-
+
+
-
+
-
+
+
+
+
-
+
+
+
+
-
+
+
@@ -102,7 +111,7 @@
-
+
Reporting an Issue with rtables
@@ -110,12 +119,12 @@
Reporting an Issue with rtables
-
Please briefly describe your problem and, when relevant, the output you expect. Please also provide the output of utils::sessionInfo() or devtools::session_info() at the end of your post.
+
Please briefly describe your problem and, when relevant, the output you expect. Please also provide the output of utils::sessionInfo() or devtools::session_info() at the end of your post.
If at all possible, please include a minimal, reproducible example. The rtables team will be much more likely to resolve your issue if they are able to reproduce it themselves locally.
Please delete this preamble after you have read it.
rtabulate provides a number of methods to compute rtable tables. The idea behind rtabulate is to have a similar function to tapply which returns rtables instead of lists. For example, for the iris data we might be interested in the average Sepal.Length for each Species which can be answered with tapply as follows:
-
attach(iris)
-tapply(X = Sepal.Length, INDEX = Species, FUN = mean)
+
rtabulate provides a number of methods to compute rtable tables. The idea behind rtabulate is to have a similar function to tapply which returns rtables instead of lists or vectors. For example, for the iris data we might be interested in the average Sepal.Length for each Species which can be answered with tapply as follows:
+
attach(iris)
+tapply(X = Sepal.Length, INDEX = Species, FUN = mean)
apply the function FUN to each element, e.g. FUN(x_s[[1]]), FUN(x_s[[2]]), etc…, which either should return an rcell or a data structure that gets wrapped into an rcell
combine the rcells to an rrow and create an rtable with a header according to the levels of col_by
@@ -148,12 +149,12 @@
Tabulating Boolean Data
Like rtabulate.numeric, rtabulate.logical returns an rtable with one row and columns according to the levels of col_by.
By default FUN=function(x) sum(x) * c(1, 1/length(x)).
@@ -173,8 +174,8 @@
Tabulation based on Data Frames
-
rtabulate.data.frame has a data.frame as x argument and the col_by_var and row_by_var need to specify which factor variables with x should be used to split the data for the table rows and columns. By default the argument to FUN is then a subset of x that is associated with a particular table cell. Let’s look at the CO2 dataset:
-
head(CO2)
+
rtabulate.data.frame has a data.frame as x argument and the col_by and row_by need to specify how to split the data for the table rows and columns. The argument to FUN is then a subset of x that is associated with a particular table cell. Let’s look at the CO2 dataset:
-Cell Data, Row Data, Column Data based Tabulation
-
rtabulate.numeric and rtabulate.logical have the argument row_data_arg which is by default set to FALSE. If set to TRUE then FUN receives a second argument with a copy of the x argument (as the row-associated data is the whole data for a numeric and logical vector).
-
rtabulate(
- Sepal.Length, Species,
- FUN =function(x_cell, x_row) c(length(x_cell), length(x_row)),
- row_data_arg =TRUE,
- format ="xx / xx",
- row.name ="length of cell and row data"
-)
-
## setosa versicolor virginica
-## ---------------------------------------------------------------------------------
-## length of cell and row data 50 / 150 50 / 150 50 / 150
-
Next, for rtabulate.factor and rtabulate.data.frame there is an row_col_data_args which when set to TRUE then the FUN function receives three arguments, the data that is associated with a table cell, row, column, respectively.
The arguments passed to the elipsis ... in rtabulate are optional arguments to FUN. This is the same behaviour as for the arguments passed to ... in lapply and tapply. This is useful, for example, when passing the na.rm argument to mean
+
x <-c(1, 2, 3, NA, 3, 2)
+cb <-factor(c("A", "B", "A", "B", "B", "A"))
-rtabulate(
- x = mtcars2, row_by_var ="carb", col_by_var ="gear",
- FUN =function(x_cell, x_row, x_col) c(nrow(x_cell), nrow(x_row), nrow(x_col)),
- row_col_data_args =TRUE,
- format =NULL
-)
## A B
+## --------------------------
+## mean 2 2.5
+
Often, howewer, it is also useful to iterate over multiple collections as for example is the case with the ... in mapply and Map. That is, given a collection x and y with each p elements, then we would like to evaluate FUN(x[i], y[i]) for i in 1 to p. In rtabulate this can be achieved with the collections wrapped in a named list and passed to the col_wise_args. For example:
rtabulate provides a number of methods to compute rtable tables. The idea behind rtabulate is to have a similar function to tapply which returns rtables instead of lists. For example, for the iris data we might be interested in the average Sepal.Length for each Species which can be answered with tapply as follows:
-
attach(iris)
-tapply(X = Sepal.Length, INDEX = Species, FUN = mean)
+
rtabulate provides a number of methods to compute rtable tables. The idea behind rtabulate is to have a similar function to tapply which returns rtables instead of lists or vectors. For example, for the iris data we might be interested in the average Sepal.Length for each Species which can be answered with tapply as follows:
+
attach(iris)
+tapply(X = Sepal.Length, INDEX = Species, FUN = mean)
apply the function FUN to each element, e.g. FUN(x_s[[1]]), FUN(x_s[[2]]), etc…, which either should return an rcell or a data structure that gets wrapped into an rcell
combine the rcells to an rrow and create an rtable with a header according to the levels of col_by
@@ -133,7 +132,6 @@
If x should not be split then the no_by function can be used to specify a column name (as the returned object is still an rtable with one column):
rtabulate.data.frame has a data.frame as x argument and the col_by_var and row_by_var need to specify which factor variables with x should be used to split the data for the table rows and columns. By default the argument to FUN is then a subset of x that is associated with a particular table cell. Let’s look at the CO2 dataset:
-
head(CO2)
+
rtabulate.data.frame has a data.frame as x argument and the col_by and row_by need to specify how to split the data for the table rows and columns. The argument to FUN is then a subset of x that is associated with a particular table cell. Let’s look at the CO2 dataset:
-Cell Data, Row Data, Column Data based Tabulation
-
rtabulate.numeric and rtabulate.logical have the argument row_data_arg which is by default set to FALSE. If set to TRUE then FUN receives a second argument with a copy of the x argument (as the row-associated data is the whole data for a numeric and logical vector).
-
rtabulate(
- Sepal.Length, Species,
- FUN =function(x_cell, x_row) c(length(x_cell), length(x_row)),
- row_data_arg =TRUE,
- format ="xx / xx",
- row.name ="length of cell and row data"
-)
-
## setosa versicolor virginica
-## (N=50) (N=50) (N=50)
-## ---------------------------------------------------------------------------------
-## length of cell and row data 50 / 150 50 / 150 50 / 150
-
Next, for rtabulate.factor and rtabulate.data.frame there is an row_col_data_args which when set to TRUE then the FUN function receives three arguments, the data that is associated with a table cell, row, column, respectively.
The arguments passed to the elipsis ... in rtabulate are optional arguments to FUN. This is the same behaviour as for the arguments passed to ... in lapply and tapply. This is useful, for example, when passing the na.rm argument to mean
+
x <-c(1, 2, 3, NA, 3, 2)
+cb <-factor(c("A", "B", "A", "B", "B", "A"))
-rtabulate(
- x = mtcars2, row_by_var ="carb", col_by_var ="gear",
- FUN =function(x_cell, x_row, x_col) c(nrow(x_cell), nrow(x_row), nrow(x_col)),
- row_col_data_args =TRUE,
- format =NULL
-)
## A B
+## --------------------------
+## mean 2 2.5
+
Often, howewer, it is also useful to iterate over multiple collections as for example is the case with the ... in mapply and Map. That is, given a collection x and y with each p elements, then we would like to evaluate FUN(x[i], y[i]) for i in 1 to p. In rtabulate this can be achieved with the collections wrapped in a named list and passed to the col_wise_args. For example:
of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If indices is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.
of row, column, and cell to access a value within each rtable that
+is used for sorting the list x. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If indices
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort x. See examples.
of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If indices is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.
of row, column, and cell to access a value within each rtable that
+is used for sorting the list x. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If indices
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort x. See examples.
+rtablulate family of functions do not support the row_*_data_args arguments anymore. Instead, the col_wise_args argument is introduced.
+
add functions order_rrows, sort_rrows, order_rtables, and sort_rtables are introduced.
+
prevent rtables from being unlisted with unlist.rtables
+
+
+
+
+
+rtables 0.1.0.6
+
+
+Viewer now also accepts objects of class shiny.tag (defined in package htmltools)
+
+as.html accepts class.table, class.tr, class.th, and class.td as an argument
+
+
+
+
+rtables 0.1.0.5
+
+
added sprintf_format for formatting rcells (thanks to Doug Kelkhoff for the suggestion)
+
added "(N=xx)" and ">999.9" format labels
+
+rtabulate has now an argument col_N and the function col_N()
+
+
+
+
-rtable 0.1.0
+rtables 0.1.0
Version 0.1.0 is a major re-design with lots of internal refactoring and the following API changes:
-
Redesign: rtable has now header argument instead of col.names. A header can be created with rheader and is a collection of rrows. If header is set to c("A", "B") then rtable will create the rheader with a single rrow and by setting row.name to NULL.
+
Redesign: rtable has now header argument instead of col.names. A header can be created with rheader and is a collection of rrows. If header is set to c("A", "B") then rtable will create the rheader with a single rrow and by setting row.name to NULL.
header and header<- function added
renamed get_rcell_formats to list_rcell_format_labels
-
If rcell format is NULL then the cell content will be converted to a string with paste(as.character(x), collapse = ', ')
of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If indices is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.
of row, column, and cell to access a value within each rtable that
+is used for sorting the list x. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If indices
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort x. See examples.
#> Sepal.Length Petal.Length
+#> mean median mean median
+#> -------------------------------------------------------------------
+#> All Species 5.84 5.8 3.76 4.35
+#> Setosa 5.01 5 1.46 1.5
#> Sepal.Length Petal.Length
#> mean median mean median
#> -------------------------------------------------------------------
#> All Species 5.84 5.8 3.76 4.35
+#>
+#> Setosa 5.01 5 1.46 1.5
#> Sepal.Length Petal.Length
+#> mean median mean median
+#> -------------------------------------------------------------------
+#> All Species 5.84 5.8 3.76 4.35
+#>
+#>
#> Setosa 5.01 5 1.46 1.5
The apply function family returns lists whose elements can be used as cell
data with the lrow function.
+
-
rrowl(row.name, ...)
+
rrowl(row.name, ..., format=NULL, indent=0)
Arguments
@@ -131,16 +143,31 @@
Arg
...
lists that get concatenated and then flattened by one level of
depth. If one elemenet is not a list then it gets placed into a list.
+
+
+
format
+
a valid format string or a format function for
+rcells. To get a list of all valid format strings use
+list_rcell_format_labels. If format is NULL then the elements
+of a cell get pasted separated by a comma.
+
+
+
indent
+
non-negative integer where 0 means that the row should not be
+indented
Reporting tables allow multiple values per cell, cell formatting and merging
cells. Currently an rtable can be converted to html and ascii.
+
rtable(header, ..., format=NULL)
@@ -149,7 +161,7 @@
Value
Details
Rtable objects can be currently exported to text with
-toString and to html with as_html. In
+toString and to html with as_html. In
future we would plan to add the as_latex and as_gridGrob
outputting function.
Note that the formats propagate to the rrow and
@@ -157,7 +169,7 @@
a valid format string or a format function for
+rcells. To get a list of all valid format strings use
+list_rcell_format_labels. If format is NULL then the elements
+of a cell get pasted separated by a comma.
a function that processes the cell data, if row_data_arg is
-set to TRUE the a second argument with the row data is passed to
-FUN
+
a function that processes the cell data
...
arguments passed to FUN
-
-
row_col_data_args
-
boolean, if TRUE then FUN is called
-with the first three arguments being the cell, row, and column data,
-respectively
-
format
if FUN does not return a formatted rcell
@@ -161,25 +164,33 @@
Arg
indent
non-negative integer where 0 means that the row should not be
indented
+
+
+
col_wise_args
+
a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument FUN corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in col_by. See examples.
a factor of length nrow(x) that defines
which levels in col_by define a column. If data should not be split
into columns use the no_by function.
FUN
-
a function that processes the cell data, if row_data_arg is
-set to TRUE the a second argument with the row data is passed to
-FUN
+
a function that processes the cell data
...
arguments passed to FUN
-
-
row_col_data_args
-
boolean, if TRUE then FUN is called
-with the first three arguments being the cell, row, and column data,
-respectively
-
useNA
-
boolean, if TRUE then NA values in x get
-turned into a factor level "NA", if FALSE then the NA
-values in x get dropped.
+
either one of ("no", "ifany", "always"). If "no" then NA values
+in x get dropped. When "ifany" is used a row for NA values is
+included in the summary if any NAs exist in x. For option "always"
+NA values are always included in the summary even if none exist in x.
format
@@ -165,12 +172,20 @@
Arg
indent
non-negative integer where 0 means that the row should not be
indented
+
+
+
col_wise_args
+
a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument FUN corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in col_by. See examples.
a factor of length nrow(x) that defines
which levels in col_by define a column. If data should not be split
into columns use the no_by function.
FUN
-
a function that processes the cell data, if row_data_arg is
-set to TRUE the a second argument with the row data is passed to
-FUN
+
a function that processes the cell data
...
arguments passed to FUN
-
-
row_data_arg
-
call FUN with the row data as the second argument
-
format
if FUN does not return a formatted rcell
@@ -162,33 +167,46 @@
Arg
indent
non-negative integer where 0 means that the row should not be
indented
+
+
+
col_wise_args
+
a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument FUN corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in col_by. See examples.
+# Custom FUN: number of TRUE records in a cell and precentages based on number of records
+# in each column
+with(iris, rtabulate(Sepal.Length<5, Species,
+ FUN=function(xi, N) sum(xi) * c(1, 1/N),
+ format="xx.xx (xx.xx%)",
+ row.name="Sepal.Length < 5",
+ col_wise_args=list(N=table(Species))
+))
-# precentages with proportion of cell number of \code{TRUE}s to overvall
-# number of \code{TRUE}s
-with(iris, rtabulate(Sepal.Length<5, Species, row.name="Sepal.Length < 5",
- FUN=function(cell_data, row_data) sum(cell_data) * c(1, 1/sum(row_data)),
- row_data_arg=TRUE
-))
a factor of length nrow(x) that defines
which levels in col_by define a column. If data should not be split
into columns use the no_by function.
FUN
-
a function that processes the cell data, if row_data_arg is
-set to TRUE the a second argument with the row data is passed to
-FUN
+
a function that processes the cell data
...
arguments passed to FUN
-
-
row_data_arg
-
call FUN with the row data as the second argument
-
format
if FUN does not return a formatted rcell
@@ -163,12 +168,20 @@
Arg
indent
non-negative integer where 0 means that the row should not be
indented
+
+
+
col_wise_args
+
a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument FUN corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in col_by. See examples.
#> A B C
+#> ---------------------------------------------------------------------------------------------------
+#> Mean (SD) and N 10.50 (5.92) and 20 35.50 (8.80) and 30 75.50 (14.58) and 50
of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If indices is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.
of row, column, and cell to access a value within each rtable that
+is used for sorting the list x. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If indices
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort x. See examples.
+
+
+
+
+
+
diff --git a/man/as_html.rtable.Rd b/man/as_html.rtable.Rd
new file mode 100644
index 000000000..5595b565f
--- /dev/null
+++ b/man/as_html.rtable.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/as_html.R
+\name{as_html.rtable}
+\alias{as_html.rtable}
+\title{Convert an rtable object to html}
+\usage{
+\method{as_html}{rtable}(x,
+ class.table = "table table-condensed table-hover", ...)
+}
+\arguments{
+\item{x}{an \code{\link{rtable}} object}
+
+\item{class.table}{class attributes for \code{
} html object}
+
+\item{...}{arguments passed as attributes to the table html objet}
+}
+\value{
+an object of class \code{shinyTag}
+}
+\description{
+Convert an rtable object to html
+}
diff --git a/man/header_add_N.Rd b/man/header_add_N.Rd
new file mode 100644
index 000000000..36134f57b
--- /dev/null
+++ b/man/header_add_N.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/header_add_N.R
+\name{header_add_N}
+\alias{header_add_N}
+\title{Add N=xx to header}
+\usage{
+header_add_N(x, N)
+}
+\arguments{
+\item{x}{\code{rtable}}
+
+\item{N}{vector with counts to be displayed in the header. The
+length must match the number of columns in \code{x}}
+}
+\description{
+Helper function used to add the population total (N) in the
+column header of \code{\link{rtable}} object.
+}
+\examples{
+
+tbl <- rtable(
+ header = letters[1:3],
+ rrow("X", 1, 2, 3),
+ rrow("Y", 4, 5, 6)
+)
+
+tbl
+
+header_add_N(tbl, 1:3)
+
+}
diff --git a/man/levels.no_by.Rd b/man/levels.no_by.Rd
new file mode 100644
index 000000000..1094d5cf0
--- /dev/null
+++ b/man/levels.no_by.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rtabulate.R
+\name{levels.no_by}
+\alias{levels.no_by}
+\title{Access levels attribute for an object of \code{no_by} Class}
+\usage{
+\method{levels}{no_by}(x)
+}
+\arguments{
+\item{x}{\code{no_by} class object}
+}
+\description{
+Access levels attribute for an object of \code{no_by} Class
+}
diff --git a/man/no_by.Rd b/man/no_by.Rd
index ca0eb016f..47396f508 100644
--- a/man/no_by.Rd
+++ b/man/no_by.Rd
@@ -2,15 +2,20 @@
% Please edit documentation in R/rtabulate.R
\name{no_by}
\alias{no_by}
-\title{Do not split data into columns or row in \code{rtabulate}}
+\title{Do not split data into columns in \code{rtabulate}}
\usage{
no_by(name)
}
\arguments{
-\item{name}{row name or column name}
+\item{name}{character column name to display in the table header}
}
\description{
-\code{\link{rtabulate}} has the arguments \code{col_by} and \code{row_by}
+\code{\link{rtabulate}} has the argument \code{col_by}
which can either take a vector or if no splitting is needed the return value
-of \code{no_by}.
+of \code{no_by}. Using \code{no_by} creates a table with a single
+column.
+}
+\examples{
+
+rtabulate(iris$Species, col_by = no_by("Total"))
}
diff --git a/man/order_rrows.Rd b/man/order_rrows.Rd
new file mode 100644
index 000000000..ab0fc19bc
--- /dev/null
+++ b/man/order_rrows.Rd
@@ -0,0 +1,44 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sort.R
+\name{order_rrows}
+\alias{order_rrows}
+\title{Sort rrows in rtable}
+\usage{
+order_rrows(x, indices = c(1, 1), ...)
+}
+\arguments{
+\item{x}{rtable object}
+
+\item{indices}{of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If \code{indices} is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.}
+
+\item{...}{arguments passed on to \code{\link{order}}}
+}
+\value{
+order of rows
+}
+\description{
+Return order of rows in rtable based on derived values within each row.
+}
+\examples{
+tbl <- rtable(
+ header = c("A", "B"),
+ rrow("r1", c(3,1), c(9,8,19)),
+ rrow("r2", c(4,-1), c(8,9,21)),
+ rrow("r3", c(1,3), c(3,2,22))
+)
+
+order_rrows(tbl, c(1,1))
+order_rrows(tbl, c(2, 3), decreasing = TRUE)
+order_rrows(tbl, c(0,2))
+
+order_rrows(tbl, function(row) row[[2]][3] - row[[1]][1])
+}
+\seealso{
+\code{\link{sort_rrows}}, \code{\link{order_rtables}},
+ \code{\link{sort_rtables}}
+}
diff --git a/man/order_rtables.Rd b/man/order_rtables.Rd
new file mode 100644
index 000000000..6139eca54
--- /dev/null
+++ b/man/order_rtables.Rd
@@ -0,0 +1,59 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sort.R
+\name{order_rtables}
+\alias{order_rtables}
+\title{Order of rtables in a list}
+\usage{
+order_rtables(x, indices = c(1, 0, 1), ...)
+}
+\arguments{
+\item{x}{a list of rtables}
+
+\item{indices}{of row, column, and cell to access a value within each rtable that
+is used for sorting the list \code{x}. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If \code{indices}
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort \code{x}. See examples.}
+
+\item{...}{arguments passed on to \code{\link{order}}}
+}
+\value{
+order of rtables
+}
+\description{
+Return the order of rtables in a list based on values within the tables.
+}
+\examples{
+
+tbls <- list(
+ rtable(
+ header = c("A", "B"),
+ rrow("r1", c(4,1), c(9,1,19)),
+ rrow("r2", c(5,-1), c(8,3,21)),
+ rrow("r3", c(1,3), c(3,4,22))
+ ),
+ rtable(
+ header = c("A", "B"),
+ rrow("r1", c(6,1), c(9,2,19)),
+ rrow("r2", c(5,-1), c(8,4,21)),
+ rrow("r3", c(1,3), c(3,5,22))
+ ),
+ rtable(
+ header = c("A", "B"),
+ rrow("r1", c(1,1), c(100,0,19)),
+ rrow("r2", c(5,-1), c(8,1,21)),
+ rrow("r3", c(1,3), c(3,1,22))
+ )
+)
+
+order_rtables(tbls, c(1,1,1))
+order_rtables(tbls, c(1,1,1), decreasing = TRUE)
+
+order_rtables(tbls, c(1,0,1))
+
+order_rtables(tbls, function(tbl) tbl[[1]][[2]][3] - tbl[[1]][[1]][1] )
+}
+\seealso{
+\code{\link{sort_rtables}}, \code{\link{sort_rrows}},
+ \code{\link{order_rrows}}
+}
diff --git a/man/rbind.rtable.Rd b/man/rbind.rtable.Rd
index f9b4c11e4..568e0adeb 100644
--- a/man/rbind.rtable.Rd
+++ b/man/rbind.rtable.Rd
@@ -1,19 +1,22 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/accessors_modifiers.R
+% Please edit documentation in R/rbind.R
\name{rbind.rtable}
\alias{rbind.rtable}
-\title{stack rtable objects}
+\title{Stack rtable objects}
\usage{
-\method{rbind}{rtable}(...)
+\method{rbind}{rtable}(..., gap = 0)
}
\arguments{
\item{...}{\code{\link{rtable}} objects}
+
+\item{gap}{number of empty rows to add between tables}
}
\value{
an \code{\link{rtable}} object
}
\description{
-stack rtable objects
+Note that the columns order are not mached by the header: the first table
+header is taken as the reference.
}
\examples{
@@ -44,7 +47,12 @@ mtbl2 <- with(subset(iris, Species == 'setosa'), rtable(
))
tbl <- rbind(mtbl, mtbl2)
-
tbl
+tbl <- rbind(mtbl, mtbl2, gap = 1)
+tbl
+
+tbl <- rbind(mtbl, mtbl2, gap = 2)
+tbl
+
}
diff --git a/man/rbindl_rtables.Rd b/man/rbindl_rtables.Rd
new file mode 100644
index 000000000..3351c9a7c
--- /dev/null
+++ b/man/rbindl_rtables.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rbind.R
+\name{rbindl_rtables}
+\alias{rbindl_rtables}
+\title{Stack a list of rtables}
+\usage{
+rbindl_rtables(x, gap = 0)
+}
+\arguments{
+\item{x}{a list of rtable objects}
+
+\item{gap}{number of empty rows to add between tables}
+}
+\description{
+See \code{\link{rbind.rtable}} for details
+}
diff --git a/man/rtabulate.Rd b/man/rtabulate.Rd
index 9d447c3d7..45893ea8f 100644
--- a/man/rtabulate.Rd
+++ b/man/rtabulate.Rd
@@ -12,7 +12,7 @@ rtabulate(x, ...)
\item{...}{arguments passed to methods}
}
\value{
-an \code{\link{rtable}} project
+an \code{\link{rtable}} object
}
\description{
\code{rtablulate} provides a number of methods to derive
@@ -20,7 +20,7 @@ an \code{\link{rtable}} project
\code{\link{tapply}}.
}
\details{
-The data is split into cell-data and a function can be specified that return
+The data is split into cell-data and a function can be specified that returns
a data structre (or \code{\link{rcell}}).
}
\author{
diff --git a/man/rtabulate.data.frame.Rd b/man/rtabulate.data.frame.Rd
index 9528d0657..4192f135c 100644
--- a/man/rtabulate.data.frame.Rd
+++ b/man/rtabulate.data.frame.Rd
@@ -4,40 +4,34 @@
\alias{rtabulate.data.frame}
\title{Split data.frame and apply functions}
\usage{
-\method{rtabulate}{data.frame}(x, row_by_var = no_by("row_1"),
- col_by_var = no_by("col_1"), FUN = nrow, ...,
- row_col_data_args = FALSE, format = "xx", indent = 0,
- col_total = "(N=xx)")
+\method{rtabulate}{data.frame}(x, row_by, col_by, FUN, ...,
+ format = NULL, indent = 0, col_wise_args = NULL)
}
\arguments{
-\item{x}{a vecor}
+\item{x}{data.frame}
-\item{row_by_var}{name of factor variable in \code{x}}
+\item{row_by}{name of factor variable in \code{x}}
-\item{col_by_var}{name of factor variable in \code{x}}
+\item{col_by}{name of factor variable in \code{x}}
-\item{FUN}{a function that processes the cell data, if \code{row_data_arg} is
-set to \code{TRUE} the a second argument with the row data is passed to
-\code{FUN}}
+\item{FUN}{a function that processes the cell data}
\item{...}{arguments passed to \code{FUN}}
-\item{row_col_data_args}{boolean, if \code{TRUE} then \code{FUN} is called
-with the first three arguments being the cell, row, and column data,
-respectively}
-
\item{format}{if \code{FUN} does not return a formatted \code{\link{rcell}}
then the \code{format} is applied}
\item{indent}{non-negative integer where 0 means that the row should not be
indented}
-\item{col_total}{a format string for displaying the number of elements in the
-column header. If \code{NULL} then no header row for the column is
-displayed.}
+\item{col_wise_args}{a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument \code{FUN} corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in \code{col_by}. See examples.}
}
\value{
-an \code{\link{rtable}} project
+an \code{\link{rtable}} object
}
\description{
Split data.frame and apply functions
@@ -49,8 +43,8 @@ df$val <- 1:nrow(df)
rtabulate(
x = df,
- row_by_var = "aaa",
- col_by_var = "bbb",
+ row_by = df$aaa,
+ col_by = df$bbb,
FUN = function(x) {
sum(x$val)
}
@@ -58,53 +52,49 @@ rtabulate(
rtabulate(
x = iris,
- row_by_var = no_by("sum"),
- col_by_var = "Species",
+ row_by = no_by("sum"),
+ col_by = iris$Species,
FUN = function(x) sum(x$Sepal.Length)
)
rtabulate(
x = iris,
- row_by_var = "Species",
- col_by_var = no_by("sum"),
+ row_by = iris$Species,
+ col_by = no_by("sum"),
FUN = function(x) sum(x$Sepal.Length)
)
-tbl <- rtabulate(
- x = iris,
- FUN = function(cell_data) c(sum(cell_data$Sepal.Length), sd(cell_data$Sepal.Length)),
- format = "xx.xx (xx.xx\%)"
-)
-
-tbl
-
-row.names(tbl)
-row.names(tbl) <- "Sum of Sepal Length"
-
-tbl
-
-iris2 <- iris
-iris2$fsl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE),
+fsl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE),
labels = c("S.L > 5", "S.L <= 5"))
tbl <- rtabulate(
- x = iris2,
- row_by_var = "fsl5",
- col_by_var = "Species",
- FUN = function(x_cell, x_row, x_col) {
+ x = iris,
+ row_by = fsl5,
+ col_by = iris$Species,
+ FUN = function(x_cell) {
if (nrow(x_cell) < 10) {
rcell("-")
} else {
fit <- lm(Sepal.Length ~ Petal.Width, data = x_cell)
- m_col <- mean(x_col$Sepal.Length)
- m_row <- mean(x_row$Sepal.Length)
-
- rcell(list(fit, m_col, m_row), format = function(x, output) {
- paste("df:", x[[1]]$df.residual,", and", round(x[[2]],1), ", and", round(x[[3]],2))
+
+ rcell(list(fit), format = function(x, output) {
+ paste("df:", x[[1]]$df.residual)
})
}
- } ,
- row_col_data_args = TRUE
+ }
)
tbl
+
+rtabulate(
+ x = iris,
+ row_by = fsl5,
+ col_by = iris$Species,
+ FUN = function(x_cell, N) {
+ N
+ },
+ col_wise_args = list(N = c(10, 100, 200))
+)
+
+
+
}
diff --git a/man/rtabulate.factor.Rd b/man/rtabulate.factor.Rd
index dce879198..34a2da1e2 100644
--- a/man/rtabulate.factor.Rd
+++ b/man/rtabulate.factor.Rd
@@ -5,8 +5,8 @@
\title{Tabulate Factors}
\usage{
\method{rtabulate}{factor}(x, col_by = no_by("col_1"), FUN = length,
- ..., row_col_data_args = FALSE, useNA = c("no", "ifany", "always"),
- format = "xx", indent = 0, col_total = "(N=xx)")
+ ..., useNA = c("no", "ifany", "always"), format = NULL, indent = 0,
+ col_wise_args = NULL)
}
\arguments{
\item{x}{a vecor}
@@ -15,19 +15,14 @@
which levels in \code{col_by} define a column. If data should not be split
into columns use the \code{\link{no_by}} function.}
-\item{FUN}{a function that processes the cell data, if \code{row_data_arg} is
-set to \code{TRUE} the a second argument with the row data is passed to
-\code{FUN}}
+\item{FUN}{a function that processes the cell data}
\item{...}{arguments passed to \code{FUN}}
-\item{row_col_data_args}{boolean, if \code{TRUE} then \code{FUN} is called
-with the first three arguments being the cell, row, and column data,
-respectively}
-
-\item{useNA}{boolean, if \code{TRUE} then \code{NA} values in \code{x} get
-turned into a factor level \code{"NA"}, if \code{FALSE} then the \code{NA}
-values in \code{x} get dropped.}
+\item{useNA}{either one of ("no", "ifany", "always"). If \code{"no"} then \code{NA} values
+in \code{x} get dropped. When \code{"ifany"} is used a row for \code{NA} values is
+included in the summary if any \code{NA}s exist in \code{x}. For option \code{"always"}
+\code{NA} values are always included in the summary even if none exist in \code{x}.}
\item{format}{if \code{FUN} does not return a formatted \code{\link{rcell}}
then the \code{format} is applied}
@@ -35,15 +30,18 @@ then the \code{format} is applied}
\item{indent}{non-negative integer where 0 means that the row should not be
indented}
-\item{col_total}{a format string for displaying the number of elements in the
-column header. If \code{NULL} then no header row for the column is
-displayed.}
+\item{col_wise_args}{a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument \code{FUN} corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in \code{col_by}. See examples.}
}
\value{
-an \code{\link{rtable}} project
+an \code{\link{rtable}} object
}
\description{
-Tabulate Factors
+By default each cell reports the number of observations in
+each level of \code{x}.
}
\examples{
@@ -55,19 +53,32 @@ sl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE),
labels = c("S.L > 5", "S.L <= 5"))
rtabulate(iris$Species, col_by=sl5)
+rtabulate(sl5, iris$Species)
rtabulate(iris$Species, col_by=sl5,
- FUN = function(cell_data, row_data, col_data) {
+ FUN = function(cell_data, N) {
if (length(cell_data) > 10) {
- length(cell_data) * c(1, 1/length(col_data))
+ length(cell_data) * c(1, 1/N)
} else {
rcell("-", format = "xx")
}
},
- row_col_data_args = TRUE,
- format = "xx (xx.xx\%)"
+ format = "xx (xx.xx\%)",
+ col_wise_args = list(N = table(sl5))
+)
+
+rtabulate(x = factor(c("X", "Y"), c("X", "Y")),
+ col_by = factor(c("a", "a"), c("a", "b")), FUN = length)
+
+rtabulate(factor(c("Y", "Y"), c("X", "Y")),
+ factor(c("b", "b"), c("a", "b")), length)
+
+rtabulate(
+ x = factor(c("Y", "Y"), c("X", "Y")),
+ col_by = factor(c("b", "b"), c("a", "b")),
+ FUN = function(x, N) list(length(x), N),
+ col_wise_args = list(N = c(1,2))
)
-rtabulate(sl5, iris$Species)
}
diff --git a/man/rtabulate.logical.Rd b/man/rtabulate.logical.Rd
index 4d6534cbd..966a581f3 100644
--- a/man/rtabulate.logical.Rd
+++ b/man/rtabulate.logical.Rd
@@ -2,12 +2,10 @@
% Please edit documentation in R/rtabulate.R
\name{rtabulate.logical}
\alias{rtabulate.logical}
-\title{tabulate a logical vector}
+\title{Tabulate a logical vector}
\usage{
-\method{rtabulate}{logical}(x, col_by = no_by("col_1"),
- FUN = function(x) sum(x) * c(1, 1/length(x)), ...,
- row_data_arg = FALSE, format = "xx.xx (xx.xx\%)", row.name = "",
- indent = 0, col_total = "(N=xx)")
+\method{rtabulate}{logical}(x, col_by = no_by("col_1"), FUN = sum, ...,
+ format = NULL, row.name = NULL, indent = 0, col_wise_args = NULL)
}
\arguments{
\item{x}{a vecor}
@@ -16,14 +14,10 @@
which levels in \code{col_by} define a column. If data should not be split
into columns use the \code{\link{no_by}} function.}
-\item{FUN}{a function that processes the cell data, if \code{row_data_arg} is
-set to \code{TRUE} the a second argument with the row data is passed to
-\code{FUN}}
+\item{FUN}{a function that processes the cell data}
\item{...}{arguments passed to \code{FUN}}
-\item{row_data_arg}{call \code{FUN} with the row data as the second argument}
-
\item{format}{if \code{FUN} does not return a formatted \code{\link{rcell}}
then the \code{format} is applied}
@@ -33,29 +27,36 @@ used as \code{row.name} of the \code{\link{rrow}}}
\item{indent}{non-negative integer where 0 means that the row should not be
indented}
-\item{col_total}{a format string for displaying the number of elements in the
-column header. If \code{NULL} then no header row for the column is
-displayed.}
+\item{col_wise_args}{a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument \code{FUN} corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in \code{col_by}. See examples.}
}
\value{
-an \code{\link{rtable}} project
+an \code{\link{rtable}} object
}
\description{
-tabulate a logical vector
+By default each cell reports the number of \code{TRUE} observations from the associated vector.
}
\examples{
rtabulate(iris$Species == "setosa")
-rtabulate(iris$Species == "setosa", no_by("Species"), row.name = "n (n/N)")
+rtabulate(iris$Species == "setosa", no_by("Species"),
+ FUN = function(x, N) list(sum(x), sum(x)/N),
+ row.name = "n (n/N)",
+ col_wise_args = list(N = 150))
-# default: percentages equal \\code{TRUE}
+# default FUN is number of observations equal to TRUE
with(iris, rtabulate(Sepal.Length < 5, Species, row.name = "Sepal.Length < 5"))
-# precentages with proportion of cell number of \\code{TRUE}s to overvall
-# number of \\code{TRUE}s
-with(iris, rtabulate(Sepal.Length < 5, Species, row.name = "Sepal.Length < 5",
- FUN = function(cell_data, row_data) sum(cell_data) * c(1, 1/sum(row_data)),
- row_data_arg = TRUE
+# Custom FUN: number of TRUE records in a cell and precentages based on number of records
+# in each column
+with(iris, rtabulate(Sepal.Length < 5, Species,
+ FUN = function(xi, N) sum(xi) * c(1, 1/N),
+ format = "xx.xx (xx.xx\%)",
+ row.name = "Sepal.Length < 5",
+ col_wise_args = list(N = table(Species))
))
}
diff --git a/man/rtabulate.numeric.Rd b/man/rtabulate.numeric.Rd
index d740f1cde..046bb1441 100644
--- a/man/rtabulate.numeric.Rd
+++ b/man/rtabulate.numeric.Rd
@@ -2,11 +2,11 @@
% Please edit documentation in R/rtabulate.R
\name{rtabulate.numeric}
\alias{rtabulate.numeric}
-\title{tabulate a numeric vector}
+\title{Tabulate a numeric vector}
\usage{
\method{rtabulate}{numeric}(x, col_by = no_by("col_1"), FUN = mean,
- ..., row_data_arg = FALSE, format = NULL, row.name = NULL,
- indent = 0, col_total = "(N=xx)")
+ ..., format = NULL, row.name = NULL, indent = 0,
+ col_wise_args = NULL)
}
\arguments{
\item{x}{a vecor}
@@ -15,14 +15,10 @@
which levels in \code{col_by} define a column. If data should not be split
into columns use the \code{\link{no_by}} function.}
-\item{FUN}{a function that processes the cell data, if \code{row_data_arg} is
-set to \code{TRUE} the a second argument with the row data is passed to
-\code{FUN}}
+\item{FUN}{a function that processes the cell data}
\item{...}{arguments passed to \code{FUN}}
-\item{row_data_arg}{call \code{FUN} with the row data as the second argument}
-
\item{format}{if \code{FUN} does not return a formatted \code{\link{rcell}}
then the \code{format} is applied}
@@ -32,24 +28,25 @@ used as \code{row.name} of the \code{\link{rrow}}}
\item{indent}{non-negative integer where 0 means that the row should not be
indented}
-\item{col_total}{a format string for displaying the number of elements in the
-column header. If \code{NULL} then no header row for the column is
-displayed.}
+\item{col_wise_args}{a named list containing collections (e.g. vectors or
+lists) with data elements for each column of the resulting table. The data
+elements are then passed to the named argument \code{FUN} corresponding to
+the element name of the outer list. Hence, the length and order of each
+collection must match the levels in \code{col_by}. See examples.}
}
\value{
-an \code{\link{rtable}} project
+an \code{\link{rtable}} object
}
\description{
-by default the \code{\link[stats]{fivenum}} function is applied to the
-vectors associated to the cells
+By default each cell reports the mean based on the associated vector.
}
\examples{
rtabulate(iris$Sepal.Length)
-
+
rtabulate(iris$Sepal.Length, col_by = no_by("Sepal.Length"))
-
-with(iris, rtabulate(x = Sepal.Length, col_by = Species, row.name = "fivenum"))
+
+with(iris, rtabulate(x = Sepal.Length, col_by = Species, row.name = "mean"))
SL <- iris$Sepal.Length
Sp <- iris$Species
@@ -60,6 +57,13 @@ rbind(
rtabulate(SL, Sp, range, format = "xx.xx - xx.xx", row.name = "Min - Max")
)
+x <- 1:100
+cb <- factor(rep(LETTERS[1:3], c(20, 30, 50)))
+rtabulate(
+ x = x, col_by = cb, FUN = function(x, N) list(mean(x), sd(x), N),
+ format = sprintf_format("\%.2f (\%.2f) and \%i"), row.name = "Mean (SD) and N",
+ col_wise_args = list(N = table(cb))
+)
}
diff --git a/man/sort_rrows.Rd b/man/sort_rrows.Rd
new file mode 100644
index 000000000..92bd66be5
--- /dev/null
+++ b/man/sort_rrows.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sort.R
+\name{sort_rrows}
+\alias{sort_rrows}
+\title{Sort Rows in rtable}
+\usage{
+sort_rrows(x, indices = c(1, 1), ...)
+}
+\arguments{
+\item{x}{rtable object}
+
+\item{indices}{of column and cell within a row to access value
+used for sorting. If the column index is 0 then the sum of the cell
+values across all columns is derived and used for sorting.
+If \code{indices} is a function then the function argument is
+rrow object and the user must specify how to extract the information
+required to sort the rows. See examples.}
+
+\item{...}{arguments passed on to \code{\link{order}}}
+}
+\value{
+object of class rtable
+}
+\description{
+Sort rows in rtable based on derived values within each row.
+}
+\examples{
+tbl <- rtable(
+ header = c("A", "B"),
+ rrow("r1", c(3,1), c(9,8,19)),
+ rrow("r2", c(4,-1), c(8,9,21)),
+ rrow("r3", c(1,3), c(3,2,22))
+)
+
+sort_rrows(tbl, c(1, 1))
+sort_rrows(tbl, c(0, 1))
+
+sort_rrows(tbl, c(2, 3), decreasing = FALSE)
+sort_rrows(tbl, c(2, 3), decreasing = TRUE)
+
+sort_rrows(tbl, function(row) row[[2]][3] - row[[1]][1])
+
+}
+\seealso{
+\code{\link{order_rrows}}, \code{\link{order_rtables}},
+ \code{\link{sort_rtables}}
+}
diff --git a/man/sort_rtables.Rd b/man/sort_rtables.Rd
new file mode 100644
index 000000000..9c88a1c71
--- /dev/null
+++ b/man/sort_rtables.Rd
@@ -0,0 +1,60 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sort.R
+\name{sort_rtables}
+\alias{sort_rtables}
+\title{Sort rtables within a list}
+\usage{
+sort_rtables(x, indices = c(1, 0, 1), ...)
+}
+\arguments{
+\item{x}{a list of rtables}
+
+\item{indices}{of row, column, and cell to access a value within each rtable that
+is used for sorting the list \code{x}. If the column index is 0 then a sum of the
+cell values across all columns within the specified row is taken. If \code{indices}
+is a function then the function argument is rtable object and the user must
+specify how to extract the information required to sort \code{x}. See examples.}
+
+\item{...}{arguments passed on to \code{\link{order}}}
+}
+\value{
+list of rtables
+}
+\description{
+Return a sorted list of rtables in a list based on values within the tables.
+}
+\examples{
+
+tbls <- list(
+ "Table A" = rtable(
+ header = c("A", "B"),
+ rrow("r1", c(4,1), c(9,1,19)),
+ rrow("r2", c(5,-1), c(8,3,21)),
+ rrow("r3", c(1,3), c(3,4,22))
+ ),
+ "Table B" = rtable(
+ header = c("A", "B"),
+ rrow("r1", c(6,1), c(9,2,19)),
+ rrow("r2", c(5,-1), c(8,4,21)),
+ rrow("r3", c(1,3), c(3,5,22))
+ ),
+ "Table C" = rtable(
+ header = c("A", "B"),
+ rrow("r1", c(1,1), c(100,0,19)),
+ rrow("r2", c(5,-1), c(8,1,21)),
+ rrow("r3", c(1,3), c(3,1,22))
+ )
+)
+
+sort_rtables(tbls, c(1,1,1))
+sort_rtables(tbls, c(1,1,1), decreasing = TRUE)
+
+sort_rtables(tbls, c(1,0,1))
+
+sort_rtables(tbls, function(tbl) tbl[[1]][[2]][3] - tbl[[1]][[1]][1] )
+
+}
+\seealso{
+\code{\link{order_rtables}}, \code{\link{sort_rrows}},
+ \code{\link{order_rrows}}
+}
diff --git a/man/unlist.rtable.Rd b/man/unlist.rtable.Rd
new file mode 100644
index 000000000..027e4aaf0
--- /dev/null
+++ b/man/unlist.rtable.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rbind.R
+\name{unlist.rtable}
+\alias{unlist.rtable}
+\title{Unlist method for rtables}
+\usage{
+\method{unlist}{rtable}(x, recursive = TRUE, use.names = TRUE)
+}
+\arguments{
+\item{x}{an \R object, typically a list or vector.}
+
+\item{recursive}{logical. Should unlisting be applied to list
+ components of \code{x}?}
+
+\item{use.names}{logical. Should names be preserved?}
+}
+\value{
+rtable object
+}
+\description{
+rtable objects should not be unlisted. This allows us to create nested lists with rtables objects and then flatten
+them to a list of rtable objects.
+}
diff --git a/tests/testthat/test-rtables.R b/tests/testthat/test-rtables.R
index e63d12ff1..251030710 100644
--- a/tests/testthat/test-rtables.R
+++ b/tests/testthat/test-rtables.R
@@ -96,4 +96,11 @@ test_that("test sprintf based format", {
expect_equal(format_rcell(rcell(c(12.21, 7.321), sprintf_format("%.1f and %.2f"))), "12.2 and 7.32")
-})
\ No newline at end of file
+})
+
+test_that("unlisting rtables has no effect on them", {
+
+ t1 <- rtable(header = c("A", "B"), format = "xx", rrow("row 1", 1, 2))
+
+ expect_identical(t1, unlist(t1))
+})
diff --git a/tests/testthat/test-rtabulate.R b/tests/testthat/test-rtabulate.R
new file mode 100644
index 000000000..efa08a676
--- /dev/null
+++ b/tests/testthat/test-rtabulate.R
@@ -0,0 +1,89 @@
+context("rtabulate")
+
+test_that("rtabulate length tests", {
+
+ cells <- function(x) as.vector(unlist(unclass(x))) # current implementation
+
+ cb <- letters[1:2]
+ cbf <- factor(cb, levels = cb)
+
+ rt_test <- function(x) {
+ tbl <- rtabulate(x, cbf, length)
+ expect_true(all(cells(tbl) == 1), "subset fail")
+
+ tbl <- rtabulate(x[1], cbf[1], length)
+ expect_identical(cells(tbl), c(1L, 0L), "0 count")
+
+ tbl <- rtabulate(numeric(0), cbf[c(FALSE, FALSE)], length)
+ expect_identical(cells(tbl), c(0L, 0L), "0 count 2")
+ }
+
+
+ rt_test(seq_along(cb)) # numeric
+ rt_test(rep(TRUE, length(cb))) # logical
+
+ # factor
+
+ tbl <- rtabulate(cbf, cbf)
+ expect_identical(cells(tbl), c(1L, 0L, 0L, 1L), "factor")
+
+ tbl <- rtabulate(factor(c("X", "X"), c("X", "Y")), cbf, length)
+ expect_identical(cells(tbl), c(1L, 1L, 0L, 0L), "factor")
+
+ tbl <- rtabulate(factor(c("X", "Y"), c("X", "Y")), factor(c("a", "a"), c("a", "b")), length)
+ expect_identical(cells(tbl), c(1L, 0L, 1L, 0L), "factor")
+
+ tbl <- rtabulate(factor(c("Y", "Y"), c("X", "Y")), factor(c("b", "b"), c("a", "b")), length)
+ expect_identical(cells(tbl), c(0L, 0L, 0L, 2L), "factor")
+
+
+})
+
+
+test_that("rtabulate: col_wise_args argument", {
+
+ get_elmts <- function(x, i) {
+ stopifnot(is(x, "rtable") && nrow(x) == 1)
+ as.vector(sapply(x[[1]], `[[`, i))
+ }
+
+ check_row1 <- function(x) {
+ expect_identical(get_elmts(x, 2), c(3, 2, 1))
+ expect_identical(get_elmts(x, 3), LETTERS[1:3])
+ }
+
+ tbl1 <- rtabulate(1:3, factor(letters[1:3]), function(xi, a, b) {
+ list(xi, a, b)
+ }, col_wise_args = list(a = c(3, 2, 1), b = LETTERS[1:3]), row.name = "-")
+
+ check_row1(tbl1)
+
+
+ tbl2 <- rtabulate((1:3)>2, factor(letters[1:3]), function(xi, a, b) {
+ list(xi, a, b)
+ }, col_wise_args = list(a = c(3, 2, 1), b = LETTERS[1:3]), row.name = "-")
+
+ check_row1(tbl2)
+
+ check_all_rows <- function(x) {
+ for (i in 1:nrow(x)) {
+ check_row1(x[i, ])
+ }
+ }
+
+ tbl3 <- rtabulate(factor(letters[1:3]), factor(letters[1:3]), function(xi, a, b) {
+ list(if (length(xi) == 0) "-" else xi, a, b)
+ }, col_wise_args = list(a = c(3, 2, 1), b = LETTERS[1:3]))
+
+ check_all_rows(tbl3)
+
+ df <- data.frame(
+ v1 = 1:3, v2 = 3:5
+ )
+ tbl4 <- rtabulate(df, factor(letters[1:3]), factor(LETTERS[1:3]), function(xi, a, b) {
+ list(nrow(xi), a, b)
+ }, col_wise_args = list(a = c(3, 2, 1), b = LETTERS[1:3]))
+
+ check_all_rows(tbl4)
+
+})
diff --git a/vignettes/rtables.Rmd b/vignettes/rtables.Rmd
index 1ae7f8884..7b39aaecc 100644
--- a/vignettes/rtables.Rmd
+++ b/vignettes/rtables.Rmd
@@ -4,11 +4,9 @@ author: "Adrian Waddell"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
- %\VignetteIndexEntry{Vignette Title}
+ %\VignetteIndexEntry{Introduction to rtables}
%\VignetteEncoding{UTF-8}
%\VignetteEngine{knitr::rmarkdown}
-editor_options:
- chunk_output_type: console
---
# Overview
diff --git a/vignettes/rtabulate.Rmd b/vignettes/rtabulate.Rmd
index 496fb8fb3..50d64e87b 100644
--- a/vignettes/rtabulate.Rmd
+++ b/vignettes/rtabulate.Rmd
@@ -2,11 +2,11 @@
title: "rtabulate"
author: "Adrian Waddell"
date: "3/21/2018"
-output:
- pdf_document: default
- html_document: default
-editor_options:
- chunk_output_type: console
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{rtabulate}
+ %\VignetteEngine{knitr::rmarkdown}
+ \usepackage[utf8]{inputenc}
---
```{r setup, include=FALSE}
@@ -22,9 +22,9 @@ library(rtables)
`rtabulate` provides a number of methods to compute `rtable` tables. The idea
behind `rtabulate` is to have a similar function to `tapply` which returns
-rtables instead of lists. For example, for the iris data we might be interested
-in the average `Sepal.Length` for each `Species` which can be answered with
-`tapply` as follows:
+rtables instead of lists or vectors. For example, for the iris data we might be
+interested in the average `Sepal.Length` for each `Species` which can be
+answered with `tapply` as follows:
```{r}
attach(iris)
@@ -109,11 +109,10 @@ rtabulate(x = esoph$agegp, col_by = esoph$alcgp)
#### Tabulation based on Data Frames
-`rtabulate.data.frame` has a `data.frame` as `x` argument and the `col_by_var`
-and `row_by_var` need to specify which factor variables with `x` should be used
-to split the data for the table rows and columns. By default the argument to
-`FUN` is then a subset of `x` that is associated with a particular table cell.
-Let's look at the `CO2` dataset:
+`rtabulate.data.frame` has a `data.frame` as `x` argument and the `col_by` and
+`row_by` need to specify how to split the data for the table rows and columns.
+The argument to `FUN` is then a subset of `x` that is associated with a
+particular table cell. Let's look at the `CO2` dataset:
```{r}
head(CO2)
@@ -122,48 +121,49 @@ head(CO2)
Say we want to calculate the total `uptake` for each `Type` and `Treatment`
```{r}
-rtabulate(x = CO2, row_by_var = "Type", col_by_var = "Treatment",
+rtabulate(x = CO2, row_by = CO2$Type, col_by = CO2$Treatment,
FUN = function(xi) sum(xi$uptake))
```
-### Cell Data, Row Data, Column Data based Tabulation
-
-`rtabulate.numeric` and `rtabulate.logical` have the argument `row_data_arg`
-which is by default set to `FALSE`. If set to `TRUE` then `FUN` receives a
-second argument with a copy of the `x` argument (as the row-associated data is
-the whole data for a `numeric` and `logical` vector).
+### Column Wise Arguments
+The arguments passed to the elipsis `...` in `rtabulate` are optional arguments
+to `FUN`. This is the same behaviour as for the arguments passed to `...` in
+`lapply` and `tapply`. This is useful, for example, when passing the `na.rm`
+argument to `mean`
```{r}
-rtabulate(
- Sepal.Length, Species,
- FUN = function(x_cell, x_row) c(length(x_cell), length(x_row)),
- row_data_arg = TRUE,
- format = "xx / xx",
- row.name = "length of cell and row data"
-)
+x <- c(1, 2, 3, NA, 3, 2)
+cb <- factor(c("A", "B", "A", "B", "B", "A"))
+
+rtabulate(x, cb, mean, na.rm = TRUE)
```
-Next, for `rtabulate.factor` and `rtabulate.data.frame` there is an
-`row_col_data_args` which when set to `TRUE` then the `FUN` function receives
-three arguments, the data that is associated with a table cell, row, column,
-respectively.
+Often, howewer, it is also useful to iterate over multiple collections as for
+example is the case with the `...` in `mapply` and `Map`. That is, given a
+collection `x` and `y` with each `p` elements, then we would like to evaluate
+`FUN(x[i], y[i])` for `i` in `1` to `p`. In `rtabulate` this can be achieved
+with the collections wrapped in a named list and passed to the `col_wise_args`.
+For example:
```{r}
-mtcars2 <- mtcars
-mtcars2$gear <- factor(mtcars2$gear)
-mtcars2$carb <- factor(mtcars2$carb)
-
-rtabulate(
- x = mtcars2, row_by_var = "carb", col_by_var = "gear",
- FUN = function(x_cell, x_row, x_col) c(nrow(x_cell), nrow(x_row), nrow(x_col)),
- row_col_data_args = TRUE,
- format = NULL
-)
+x <- c(1, 2, 3, NA, 3, 2)
+cb <- factor(c("A", "A", "A", "B", "B", "A"), levels = c("A", "B"))
+
+tbl <- rtabulate(x, cb, FUN = function(xi, na.rm, N) {
+ list(mean(xi, na.rm=na.rm), N)
+}, na.rm = TRUE, col_wise_args = list(N = table(cb)), row.name = "mean, N")
+
+tbl
```
Note that `format=NULL` is equivalent to `paste(x, collapse = ", ")` on the cell
data structure.
+In order to add a `(N=xx)` type of row to the header we can add `header_add_N` function:
+
+```{r}
+header_add_N(tbl, table(cb))
+```