Title: | Reporting Tables |
---|---|
Description: | Reporting tables often have structure that goes beyond simple rectangular data. The 'rtables' package provides a framework for declaring complex multi-level tabulations and then applying them to data. This framework models both tabulation and the resulting tables as hierarchical, tree-like objects which support sibling sub-tables, arbitrary splitting or grouping of data in row and column dimensions, cells containing multiple values, and the concept of contextual summary computations. A convenient pipe-able interface is provided for declaring table layouts and the corresponding computations, and then applying them to data. |
Authors: | Gabriel Becker [aut] (Original creator of the package), Adrian Waddell [aut], Daniel Sabanés Bové [ctb], Maximilian Mordig [ctb], Davide Garolini [ctb], Emily de la Rua [ctb], Abinaya Yogasekaram [ctb], Joe Zhu [ctb, cre], F. Hoffmann-La Roche AG [cph, fnd] |
Maintainer: | Joe Zhu <[email protected]> |
License: | Apache License 2.0 | file LICENSE |
Version: | 0.6.10.9002 |
Built: | 2024-10-29 10:18:28 UTC |
Source: | https://github.com/insightsengineering/rtables |
Add the data derived column counts.
add_colcounts(lyt, format = "(N=xx)")
add_colcounts(lyt, format = "(N=xx)")
lyt |
( |
format |
( |
It is often the case that the the column counts derived from the
input data to build_table()
is not representative of the population counts.
For example, if events are counted in the table and the header should
display the number of subjects and not the total number of events.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
lyt <- basic_table() %>% split_cols_by("ARM") %>% add_colcounts() %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE", afun = function(x) list(min = min(x), max = max(x))) lyt tbl <- build_table(lyt, DM) tbl
lyt <- basic_table() %>% split_cols_by("ARM") %>% add_colcounts() %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE", afun = function(x) list(min = min(x), max = max(x))) lyt tbl <- build_table(lyt, DM) tbl
Add a combination facet during the post-processing stage in a custom split fun.
add_combo_facet(name, label = name, levels, extra = list()) add_overall_facet(name, label, extra = list())
add_combo_facet(name, label = name, levels, extra = list()) add_overall_facet(name, label, extra = list())
name |
( |
label |
( |
levels |
( |
extra |
( |
For add_combo_facet
, the data associated with the resulting facet will be the data associated with the facets for
each level in levels
, row-bound together. In particular, this means that if those levels are overlapping, data
that appears in both will be duplicated.
A function which can be used within the post
argument in make_split_fun()
.
Other make_custom_split:
drop_facet_levels()
,
make_split_fun()
,
make_split_result()
,
trim_levels_in_facets()
mysplfun <- make_split_fun(post = list( add_combo_facet("A_B", label = "Arms A+B", levels = c("A: Drug X", "B: Placebo") ), add_overall_facet("ALL", label = "All Arms") )) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplfun) %>% analyze("AGE") tbl <- build_table(lyt, DM)
mysplfun <- make_split_fun(post = list( add_combo_facet("A_B", label = "Arms A+B", levels = c("A: Drug X", "B: Placebo") ), add_overall_facet("ALL", label = "All Arms") )) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplfun) %>% analyze("AGE") tbl <- build_table(lyt, DM)
Add an already calculated table to the layout
add_existing_table(lyt, tt, indent_mod = 0)
add_existing_table(lyt, tt, indent_mod = 0)
lyt |
( |
tt |
( |
indent_mod |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
lyt1 <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = mean, format = "xx.xx") tbl1 <- build_table(lyt1, DM) tbl1 lyt2 <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = sd, format = "xx.xx") %>% add_existing_table(tbl1) tbl2 <- build_table(lyt2, DM) tbl2 table_structure(tbl2) row_paths_summary(tbl2)
lyt1 <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = mean, format = "xx.xx") tbl1 <- build_table(lyt1, DM) tbl1 lyt2 <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = sd, format = "xx.xx") %>% add_existing_table(tbl1) tbl2 <- build_table(lyt2, DM) tbl2 table_structure(tbl2) row_paths_summary(tbl2)
This function will only add an overall column at the top level of splitting, NOT within existing column splits.
See add_overall_level()
for the recommended way to add overall columns more generally within existing splits.
add_overall_col(lyt, label)
add_overall_col(lyt, label)
lyt |
( |
label |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
lyt <- basic_table() %>% split_cols_by("ARM") %>% add_overall_col("All Patients") %>% analyze("AGE") lyt tbl <- build_table(lyt, DM) tbl
lyt <- basic_table() %>% split_cols_by("ARM") %>% add_overall_col("All Patients") %>% analyze("AGE") lyt tbl <- build_table(lyt, DM) tbl
add_overall_level
is a split function that adds a global level to the current levels in the split. Similarly,
add_combo_df
uses a user-provided data.frame
to define the combine the levels to be added. If you need a
single overall column, after all splits, please check add_overall_col()
. Consider also defining
your custom split function if you need more flexibility (see custom_split_funs).
add_overall_level( valname = "Overall", label = valname, extra_args = list(), first = TRUE, trim = FALSE ) select_all_levels add_combo_levels(combosdf, trim = FALSE, first = FALSE, keep_levels = NULL)
add_overall_level( valname = "Overall", label = valname, extra_args = list(), first = TRUE, trim = FALSE ) select_all_levels add_combo_levels(combosdf, trim = FALSE, first = FALSE, keep_levels = NULL)
valname |
( |
label |
( |
extra_args |
( |
first |
( |
trim |
( |
combosdf |
( |
keep_levels |
( |
An object of class AllLevelsSentinel
of length 0.
A splitting function (splfun
) that adds or changes the levels of a split.
Analysis or summary functions for which the order matters should never be used within the tabulation framework.
custom_split_funs and split_funcs.
lyt <- basic_table() %>% split_cols_by("ARM", split_fun = add_overall_level("All Patients", first = FALSE )) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE", split_fun = add_overall_level("All Ethnicities") ) %>% summarize_row_groups(label_fstr = "%s (n)") %>% analyze("AGE") lyt2 tbl2 <- build_table(lyt2, DM) tbl2 library(tibble) combodf <- tribble( ~valname, ~label, ~levelcombo, ~exargs, "A_B", "Arms A+B", c("A: Drug X", "B: Placebo"), list(), "A_C", "Arms A+C", c("A: Drug X", "C: Combination"), list() ) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf)) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl lyt1 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf, keep_levels = c( "A_B", "A_C" ) ) ) %>% analyze("AGE") tbl1 <- build_table(lyt1, DM) tbl1 smallerDM <- droplevels(subset(DM, SEX %in% c("M", "F") & grepl("^(A|B)", ARM))) lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf[1, ])) %>% split_cols_by("SEX", split_fun = add_overall_level("SEX_ALL", "All Genders") ) %>% analyze("AGE") lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf)) %>% split_rows_by("SEX", split_fun = add_overall_level("SEX_ALL", "All Genders") ) %>% summarize_row_groups() %>% analyze("AGE") tbl3 <- build_table(lyt3, smallerDM) tbl3
lyt <- basic_table() %>% split_cols_by("ARM", split_fun = add_overall_level("All Patients", first = FALSE )) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE", split_fun = add_overall_level("All Ethnicities") ) %>% summarize_row_groups(label_fstr = "%s (n)") %>% analyze("AGE") lyt2 tbl2 <- build_table(lyt2, DM) tbl2 library(tibble) combodf <- tribble( ~valname, ~label, ~levelcombo, ~exargs, "A_B", "Arms A+B", c("A: Drug X", "B: Placebo"), list(), "A_C", "Arms A+C", c("A: Drug X", "C: Combination"), list() ) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf)) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl lyt1 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf, keep_levels = c( "A_B", "A_C" ) ) ) %>% analyze("AGE") tbl1 <- build_table(lyt1, DM) tbl1 smallerDM <- droplevels(subset(DM, SEX %in% c("M", "F") & grepl("^(A|B)", ARM))) lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf[1, ])) %>% split_cols_by("SEX", split_fun = add_overall_level("SEX_ALL", "All Genders") ) %>% analyze("AGE") lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = add_combo_levels(combodf)) %>% split_rows_by("SEX", split_fun = add_overall_level("SEX_ALL", "All Genders") ) %>% summarize_row_groups() %>% analyze("AGE") tbl3 <- build_table(lyt3, smallerDM) tbl3
afun
/cfun
)It is possible to add specific parameters to afun
and cfun
, in analyze()
and summarize_row_groups()
,
respectively. These parameters grant access to relevant information like the row split structure (see
spl_context) and the predefined baseline (.ref_group
).
We list and describe all the parameters that can be added to a custom analysis function below:
Column-wise N (column count) for the full column being tabulated within.
Overall N (all observation count, defined as sum of column counts) for the tabulation.
Row-wise N (row group count) for the group of observations being analyzed (i.e. with no column-based subsetting).
data.frame
for observations in the row group being analyzed (i.e. with no column-based
subsetting).
Variable being analyzed.
data.frame
or vector of subset corresponding to the ref_group
column including subsetting
defined by row-splitting. Only required/meaningful if a ref_group
column has been defined.
data.frame
or vector of subset corresponding to the ref_group
column without subsetting
defined by row-splitting. Only required/meaningful if a ref_group
column has been defined.
Boolean indicating if calculation is done for cells within the reference column.
data.frame
where each row gives information about a previous 'ancestor' split state.
See spl_context.
data.frame
, i.e. the alt_count_df
after row splitting. It can be used with
.all_col_exprs
and .spl_context
information to retrieve current faceting, but for alt_count_df
.
It can be an empty table if all the entries are filtered out.
data.frame
, .alt_df_row
but filtered by columns expression. This data present the same
faceting of main data df
. This also filters NA
s out if related parameters are set to do so (e.g. inclNAs
in analyze()
). Similarly to .alt_df_row
, it can be an empty table if all the entries are filtered out.
List of expressions. Each of them represents a different column splitting.
Vector of integers. Each of them represents the global count for each column. It differs
if alt_counts_df
is used (see build_table()
).
If any of these formals is specified incorrectly or not present in the tabulation machinery, it will be
treated as if missing. For example, .ref_group
will be missing if no baseline is previously defined during
data splitting (via ref_group
parameters in, e.g., split_rows_by()
). Similarly, if no alt_counts_df
is
provided to build_table()
, .alt_df_row
and .alt_df
will not be present.
Criteria functions (and constructors thereof) for trimming and pruning tables.
all_zero_or_na(tr) all_zero(tr) content_all_zeros_nas(tt, criteria = all_zero_or_na) prune_empty_level(tt) prune_zeros_only(tt) low_obs_pruner(min, type = c("sum", "mean"))
all_zero_or_na(tr) all_zero(tr) content_all_zeros_nas(tt, criteria = all_zero_or_na) prune_empty_level(tt) prune_zeros_only(tt) low_obs_pruner(min, type = c("sum", "mean"))
tr |
( |
tt |
( |
criteria |
( |
min |
( |
type |
( |
all_zero_or_na
returns TRUE
(and thus indicates trimming/pruning) for any non-LabelRow
TableRow
which contain only any mix of NA
(including NaN
), 0
, Inf
and -Inf
values.
all_zero
returns TRUE
for any non-LabelRow
which contains only (non-missing) zero values.
content_all_zeros_nas
prunes a subtable if both of the following are true:
It has a content table with exactly one row in it.
all_zero_or_na
returns TRUE
for that single content row. In practice, when the default summary/content
function is used, this represents pruning any subtable which corresponds to an empty set of the input data
(e.g. because a factor variable was used in split_rows_by()
but not all levels were present in the data).
prune_empty_level
combines all_zero_or_na
behavior for TableRow
objects, content_all_zeros_nas
on
content_table(tt)
for TableTree
objects, and an additional check that returns TRUE
if the tt
has no
children.
prune_zeros_only
behaves as prune_empty_level
does, except that like all_zero
it prunes
only in the case of all non-missing zero values.
low_obs_pruner
is a constructor function which, when called, returns a pruning criteria function which
will prune on content rows by comparing sum or mean (dictated by type
) of the count portions of the cell
values (defined as the first value per cell regardless of how many values per cell there are) against min
.
A logical value indicating whether tr
should be included (TRUE
) or pruned (FALSE
) during pruning.
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") adsl$AGE[adsl$SEX == "UNDIFFERENTIATED"] <- 0 adsl$BMRKR1 <- 0 tbl_to_prune <- basic_table() %>% analyze("BMRKR1") %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_prune %>% prune_table(all_zero_or_na) tbl_to_prune %>% prune_table(all_zero) tbl_to_prune %>% prune_table(content_all_zeros_nas) tbl_to_prune %>% prune_table(prune_empty_level) tbl_to_prune %>% prune_table(prune_zeros_only) min_prune <- low_obs_pruner(70, "sum") tbl_to_prune %>% prune_table(min_prune)
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") adsl$AGE[adsl$SEX == "UNDIFFERENTIATED"] <- 0 adsl$BMRKR1 <- 0 tbl_to_prune <- basic_table() %>% analyze("BMRKR1") %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_prune %>% prune_table(all_zero_or_na) tbl_to_prune %>% prune_table(all_zero) tbl_to_prune %>% prune_table(content_all_zeros_nas) tbl_to_prune %>% prune_table(prune_empty_level) tbl_to_prune %>% prune_table(prune_zeros_only) min_prune <- low_obs_pruner(70, "sum") tbl_to_prune %>% prune_table(min_prune)
Adding analyzed variables to our table layout defines the primary tabulation to be performed. We do this by
adding calls to analyze
and/or analyze_colvars()
into our layout pipeline. As with adding further splitting,
the tabulation will occur at the current/next level of nesting by default.
analyze( lyt, vars, afun = simple_analysis, var_labels = vars, table_names = vars, format = NULL, na_str = NA_character_, nested = TRUE, inclNAs = FALSE, extra_args = list(), show_labels = c("default", "visible", "hidden"), indent_mod = 0L, section_div = NA_character_ )
analyze( lyt, vars, afun = simple_analysis, var_labels = vars, table_names = vars, format = NULL, na_str = NA_character_, nested = TRUE, inclNAs = FALSE, extra_args = list(), show_labels = c("default", "visible", "hidden"), indent_mod = 0L, section_div = NA_character_ )
lyt |
( |
vars |
( |
afun |
( |
var_labels |
( |
table_names |
( |
format |
( |
na_str |
( |
nested |
( |
inclNAs |
( |
extra_args |
( |
show_labels |
( |
indent_mod |
( |
section_div |
( |
When non-NULL
, format
is used to specify formats for all generated rows, and can be a character vector, a
function, or a list of functions. It will be repped out to the number of rows once this is calculated during the
tabulation process, but will be overridden by formats specified within rcell
calls in afun
.
The analysis function (afun
) should take as its first parameter either x
or df
. Whichever of these the
function accepts will change the behavior when tabulation is performed as follows:
If afun
's first parameter is x
, it will receive the corresponding subset vector of data from the relevant
column (from var
here) of the raw data being used to build the table.
If afun
's first parameter is df
, it will receive the corresponding subset data frame (i.e. all columns) of
the raw data being tabulated.
In addition to differentiation on the first argument, the analysis function can optionally accept a number of other parameters which, if and only if present in the formals, will be passed to the function by the tabulation machinery. These are listed and described in additional_fun_params.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
None of the arguments described in the Details section can be overridden via extra_args
or when calling
make_afun()
. .N_col
and .N_total
can be overridden via the col_counts
argument to build_table()
.
Alternative values for the others must be calculated within afun
based on a combination of extra arguments and
the unmodified values provided by the tabulation framework.
Gabriel Becker
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") lyt tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("Species") %>% analyze(head(names(iris), -1), afun = function(x) { list( "mean / sd" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = rcell(diff(range(x)), format = "xx.xx") ) }) lyt2 tbl2 <- build_table(lyt2, iris) tbl2
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") lyt tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("Species") %>% analyze(head(names(iris), -1), afun = function(x) { list( "mean / sd" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = rcell(diff(range(x)), format = "xx.xx") ) }) lyt2 tbl2 <- build_table(lyt2, iris) tbl2
Generate rows analyzing different variables across columns
analyze_colvars( lyt, afun, format = NULL, na_str = NA_character_, nested = TRUE, extra_args = list(), indent_mod = 0L, inclNAs = FALSE )
analyze_colvars( lyt, afun, format = NULL, na_str = NA_character_, nested = TRUE, extra_args = list(), indent_mod = 0L, inclNAs = FALSE )
lyt |
( |
afun |
( |
format |
( |
na_str |
( |
nested |
( |
extra_args |
( |
indent_mod |
( |
inclNAs |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
library(dplyr) ANL <- DM %>% mutate(value = rnorm(n()), pctdiff = runif(n())) ## toy example where we take the mean of the first variable and the ## count of >.5 for the second. colfuns <- list( function(x) rcell(mean(x), format = "xx.x"), function(x) rcell(sum(x > .5), format = "xx") ) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff")) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = colfuns) lyt tbl <- build_table(lyt, ANL) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff"), varlabels = c("Measurement", "Pct Diff") ) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = mean, format = "xx.xx") tbl2 <- build_table(lyt2, ANL) tbl2
library(dplyr) ANL <- DM %>% mutate(value = rnorm(n()), pctdiff = runif(n())) ## toy example where we take the mean of the first variable and the ## count of >.5 for the second. colfuns <- list( function(x) rcell(mean(x), format = "xx.x"), function(x) rcell(sum(x > .5), format = "xx") ) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff")) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = colfuns) lyt tbl <- build_table(lyt, ANL) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff"), varlabels = c("Measurement", "Pct Diff") ) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = mean, format = "xx.xx") tbl2 <- build_table(lyt2, ANL) tbl2
Define a subset tabulation/analysis
Define a subset tabulation/analysis
AnalyzeVarSplit( var, split_label = var, afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, split_name = var, extra_args = list(), indent_mod = 0L, label_pos = "default", cvar = "", section_div = NA_character_ ) AnalyzeColVarSplit( afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, split_name = "", extra_args = list(), indent_mod = 0L, label_pos = "default", cvar = "", section_div = NA_character_ ) AnalyzeMultiVars( var, split_label = "", afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, .payload = NULL, split_name = NULL, extra_args = list(), indent_mod = 0L, child_labels = c("default", "topleft", "visible", "hidden"), child_names = var, cvar = "", section_div = NA_character_ )
AnalyzeVarSplit( var, split_label = var, afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, split_name = var, extra_args = list(), indent_mod = 0L, label_pos = "default", cvar = "", section_div = NA_character_ ) AnalyzeColVarSplit( afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, split_name = "", extra_args = list(), indent_mod = 0L, label_pos = "default", cvar = "", section_div = NA_character_ ) AnalyzeMultiVars( var, split_label = "", afun, defrowlab = "", cfun = NULL, cformat = NULL, split_format = NULL, split_na_str = NA_character_, inclNAs = FALSE, .payload = NULL, split_name = NULL, extra_args = list(), indent_mod = 0L, child_labels = c("default", "topleft", "visible", "hidden"), child_names = var, cvar = "", section_div = NA_character_ )
var |
( |
split_label |
( |
afun |
( |
defrowlab |
( |
cfun |
( |
cformat |
( |
split_format |
( |
split_na_str |
( |
inclNAs |
( |
split_name |
( |
extra_args |
( |
indent_mod |
( |
label_pos |
( |
cvar |
( |
section_div |
( |
.payload |
( |
child_labels |
( |
child_names |
( |
An AnalyzeVarSplit
object.
An AnalyzeMultiVars
split object.
Gabriel Becker
This function adds newlines
to the current set of "top-left materials".
append_topleft(lyt, newlines)
append_topleft(lyt, newlines)
lyt |
( |
newlines |
( |
Adds newlines
to the set of strings representing the 'top-left' materials declared in the layout (the content
displayed to the left of the column labels when the resulting tables are printed).
Top-left material strings are stored and then displayed exactly as is, no structure or indenting is applied to them either when they are added or when they are displayed.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Currently, where in the construction of the layout this is called makes no difference, as it is independent of the actual splitting keywords. This may change in the future.
This function is experimental, its name and the details of its behavior are subject to change in future versions.
library(dplyr) DM2 <- DM %>% mutate(RACE = factor(RACE), SEX = factor(SEX)) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% split_rows_by("RACE") %>% append_topleft("Ethnicity") %>% analyze("AGE") %>% append_topleft(" Age") tbl <- build_table(lyt, DM2) tbl
library(dplyr) DM2 <- DM %>% mutate(RACE = factor(RACE), SEX = factor(SEX)) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% split_rows_by("RACE") %>% append_topleft("Ethnicity") %>% analyze("AGE") %>% append_topleft(" Age") tbl <- build_table(lyt, DM2) tbl
rtable
object to a shiny.tag
HTML objectThe returned HTML object can be immediately used in shiny
and rmarkdown
.
as_html( x, width = NULL, class_table = "table table-condensed table-hover", class_tr = NULL, class_th = NULL, link_label = NULL, bold = c("header"), header_sep_line = TRUE, no_spaces_between_cells = FALSE, expand_newlines = FALSE )
as_html( x, width = NULL, class_table = "table table-condensed table-hover", class_tr = NULL, class_th = NULL, link_label = NULL, bold = c("header"), header_sep_line = TRUE, no_spaces_between_cells = FALSE, expand_newlines = FALSE )
x |
( |
width |
( |
class_table |
( |
class_tr |
( |
class_th |
( |
link_label |
( |
bold |
( |
header_sep_line |
( |
no_spaces_between_cells |
( |
expand_newlines |
( |
A shiny.tag
object representing x
in HTML.
tbl <- rtable( header = LETTERS[1:3], format = "xx", rrow("r1", 1, 2, 3), rrow("r2", 4, 3, 2, indent = 1), rrow("r3", indent = 2) ) as_html(tbl) as_html(tbl, class_table = "table", class_tr = "row") as_html(tbl, bold = c("header", "row_names")) ## Not run: Viewer(tbl) ## End(Not run)
tbl <- rtable( header = LETTERS[1:3], format = "xx", rrow("r1", 1, 2, 3), rrow("r2", 4, 3, 2, indent = 1), rrow("r3", indent = 2) ) as_html(tbl) as_html(tbl, class_table = "table", class_tr = "row") as_html(tbl, bold = c("header", "row_names")) ## Not run: Viewer(tbl) ## End(Not run)
Convert an rtables
framework object into a vector, if possible. This is unlikely to be useful in
realistic scenarios.
## S4 method for signature 'VTableTree' as.vector(x, mode = "any")
## S4 method for signature 'VTableTree' as.vector(x, mode = "any")
x |
( |
mode |
( |
A vector of the chosen mode (or an error is raised if more than one row was present).
This only works for a table with a single row or a row object.
Every layout must start with a basic table.
basic_table( title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = NA, colcount_format = "(N=xx)", header_section_div = NA_character_, top_level_section_div = NA_character_, inset = 0L )
basic_table( title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = NA, colcount_format = "(N=xx)", header_section_div = NA_character_, top_level_section_div = NA_character_, inset = 0L )
title |
( |
subtitles |
( |
main_footer |
( |
prov_footer |
( |
show_colcounts |
( |
colcount_format |
( |
header_section_div |
( |
top_level_section_div |
( |
inset |
( |
colcount_format
is ignored if show_colcounts
is FALSE
(the default). When show_colcounts
is TRUE
,
and colcount_format
is 2-dimensional with a percent component, the value component for the percent is always
populated with 1
(i.e. 100%). 1d formats are used to render the counts exactly as they normally would be,
while 2d formats which don't include a percent, and all 3d formats result in an error. Formats in the form of
functions are not supported for colcount
format. See formatters::list_valid_format_labels()
for the list
of valid format labels to select from.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Because percent components in colcount_format
are always populated with the value 1, we can get arguably
strange results, such as that individual arm columns and a combined "all patients" column all list "100%" as
their percentage, even though the individual arm columns represent strict subsets of the "all patients" column.
Note that subtitles (formatters::subtitles()
) and footers (formatters::main_footer()
and
formatters::prov_footer()
) that span more than one line can be supplied as a character vector to maintain
indentation on multiple lines.
lyt <- basic_table() %>% analyze("AGE", afun = mean) tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table( title = "Title of table", subtitles = c("a number", "of subtitles"), main_footer = "test footer", prov_footer = paste( "test.R program, executed at", Sys.time() ) ) %>% split_cols_by("ARM") %>% analyze("AGE", mean) tbl2 <- build_table(lyt2, DM) tbl2 lyt3 <- basic_table( show_colcounts = TRUE, colcount_format = "xx. (xx.%)" ) %>% split_cols_by("ARM")
lyt <- basic_table() %>% analyze("AGE", afun = mean) tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table( title = "Title of table", subtitles = c("a number", "of subtitles"), main_footer = "test footer", prov_footer = paste( "test.R program, executed at", Sys.time() ) ) %>% split_cols_by("ARM") %>% analyze("AGE", mean) tbl2 <- build_table(lyt2, DM) tbl2 lyt3 <- basic_table( show_colcounts = TRUE, colcount_format = "xx. (xx.%)" ) %>% split_cols_by("ARM")
TableTree
Retrieve and assign elements of a TableTree
## S4 replacement method for signature 'VTableTree,ANY,ANY,list' x[i, j, ...] <- value ## S4 method for signature 'VTableTree,logical,logical' x[i, j, ..., drop = FALSE]
## S4 replacement method for signature 'VTableTree,ANY,ANY,list' x[i, j, ...] <- value ## S4 method for signature 'VTableTree,logical,logical' x[i, j, ..., drop = FALSE]
x |
( |
i |
( |
j |
( |
... |
additional arguments. Includes:
|
value |
( |
drop |
( |
By default, subsetting drops the information about title, subtitle, main footer, provenance footer, and topleft
.
If only a column is selected and all rows are kept, the topleft
information remains as default. Any referential
footnote is kept whenever the subset table contains the referenced element.
A TableTree
(or ElementaryTable
) object, unless a single cell was selected with drop = TRUE
, in which
case the (possibly multi-valued) fully stripped raw value of the selected cell.
Subsetting always preserve the original order, even if provided indexes do not preserve it. If sorting is needed,
please consider using sort_at_path()
. Also note that character
indices are treated as paths, not vectors of
names in both [
and [<-
.
sort_at_path()
to understand sorting.
summarize_row_groups()
to understand path structure.
lyt <- basic_table( title = "Title", subtitles = c("Sub", "titles"), prov_footer = "prov footer", main_footer = "main footer" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze(c("AGE")) tbl <- build_table(lyt, DM) top_left(tbl) <- "Info" tbl # As default header, footer, and topleft information is lost tbl[1, ] tbl[1:2, 2] # Also boolean filters can work tbl[, c(FALSE, TRUE, FALSE)] # If drop = TRUE, the content values are directly retrieved tbl[2, 1] tbl[2, 1, drop = TRUE] # Drop works also if vectors are selected, but not matrices tbl[, 1, drop = TRUE] tbl[2, , drop = TRUE] tbl[1, 1, drop = TRUE] # NULL because it is a label row tbl[2, 1:2, drop = TRUE] # vectors can be returned only with cell_values() tbl[1:2, 1:2, drop = TRUE] # no dropping because it is a matrix # If all rows are selected, topleft is kept by default tbl[, 2] tbl[, 1] # It is possible to deselect values tbl[-2, ] tbl[, -1] # Values can be reassigned tbl[2, 1] <- rcell(999) tbl[2, ] <- list(rrow("FFF", 888, 666, 777)) tbl[6, ] <- list(-111, -222, -333) tbl # We can keep some information from the original table if we need tbl[1, 2, keep_titles = TRUE] tbl[1, 2, keep_footers = TRUE, keep_titles = FALSE] tbl[1, 2, keep_footers = FALSE, keep_titles = TRUE] tbl[1, 2, keep_footers = TRUE] tbl[1, 2, keep_topleft = TRUE] # Keeps the referential footnotes when subset contains them fnotes_at_path(tbl, rowpath = c("SEX", "M", "AGE", "Mean")) <- "important" tbl[4, 1] tbl[2, 1] # None present # We can reindex referential footnotes, so that the new table does not depend # on the original one fnotes_at_path(tbl, rowpath = c("SEX", "U", "AGE", "Mean")) <- "important" tbl[, 1] # both present tbl[5:6, 1] # {1} because it has been indexed again tbl[5:6, 1, reindex_refs = FALSE] # {2} -> not reindexed # Note that order can not be changed with subsetting tbl[c(4, 3, 1), c(3, 1)] # It preserves order and wanted selection
lyt <- basic_table( title = "Title", subtitles = c("Sub", "titles"), prov_footer = "prov footer", main_footer = "main footer" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze(c("AGE")) tbl <- build_table(lyt, DM) top_left(tbl) <- "Info" tbl # As default header, footer, and topleft information is lost tbl[1, ] tbl[1:2, 2] # Also boolean filters can work tbl[, c(FALSE, TRUE, FALSE)] # If drop = TRUE, the content values are directly retrieved tbl[2, 1] tbl[2, 1, drop = TRUE] # Drop works also if vectors are selected, but not matrices tbl[, 1, drop = TRUE] tbl[2, , drop = TRUE] tbl[1, 1, drop = TRUE] # NULL because it is a label row tbl[2, 1:2, drop = TRUE] # vectors can be returned only with cell_values() tbl[1:2, 1:2, drop = TRUE] # no dropping because it is a matrix # If all rows are selected, topleft is kept by default tbl[, 2] tbl[, 1] # It is possible to deselect values tbl[-2, ] tbl[, -1] # Values can be reassigned tbl[2, 1] <- rcell(999) tbl[2, ] <- list(rrow("FFF", 888, 666, 777)) tbl[6, ] <- list(-111, -222, -333) tbl # We can keep some information from the original table if we need tbl[1, 2, keep_titles = TRUE] tbl[1, 2, keep_footers = TRUE, keep_titles = FALSE] tbl[1, 2, keep_footers = FALSE, keep_titles = TRUE] tbl[1, 2, keep_footers = TRUE] tbl[1, 2, keep_topleft = TRUE] # Keeps the referential footnotes when subset contains them fnotes_at_path(tbl, rowpath = c("SEX", "M", "AGE", "Mean")) <- "important" tbl[4, 1] tbl[2, 1] # None present # We can reindex referential footnotes, so that the new table does not depend # on the original one fnotes_at_path(tbl, rowpath = c("SEX", "U", "AGE", "Mean")) <- "important" tbl[, 1] # both present tbl[5:6, 1] # {1} because it has been indexed again tbl[5:6, 1, reindex_refs = FALSE] # {2} -> not reindexed # Note that order can not be changed with subsetting tbl[c(4, 3, 1), c(3, 1)] # It preserves order and wanted selection
Layouts are used to describe a table pre-data. build_table
is used to create a table
using a layout and a dataset.
build_table( lyt, df, alt_counts_df = NULL, col_counts = NULL, col_total = if (is.null(alt_counts_df)) nrow(df) else nrow(alt_counts_df), topleft = NULL, hsep = default_hsep(), ... )
build_table( lyt, df, alt_counts_df = NULL, col_counts = NULL, col_total = if (is.null(alt_counts_df)) nrow(df) else nrow(alt_counts_df), topleft = NULL, hsep = default_hsep(), ... )
lyt |
( |
df |
( |
alt_counts_df |
( |
col_counts |
( |
col_total |
( |
topleft |
( |
hsep |
( |
... |
ignored. |
When alt_counts_df
is specified, column counts are calculated by applying the exact column subsetting
expressions determined when applying column splitting to the main data (df
) to alt_counts_df
and
counting the observations in each resulting subset.
In particular, this means that in the case of splitting based on cuts of the data, any dynamic cuts will have
been calculated based on df
and simply re-used for the count calculation.
A TableTree
or ElementaryTable
object representing the table created by performing the tabulations
declared in lyt
to the data df
.
When overriding the column counts or totals care must be taken that, e.g., length()
or nrow()
are not called
within tabulation functions, because those will NOT give the overridden counts. Writing/using tabulation
functions which accept .N_col
and .N_total
or do not rely on column counts at all (even implicitly) is the
only way to ensure overridden counts are fully respected.
Gabriel Becker
lyt <- basic_table() %>% split_cols_by("Species") %>% analyze("Sepal.Length", afun = function(x) { list( "mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = diff(range(x)) ) }) lyt tbl <- build_table(lyt, iris) tbl # analyze multiple variables lyt2 <- basic_table() %>% split_cols_by("Species") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = function(x) { list( "mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = diff(range(x)) ) }) tbl2 <- build_table(lyt2, iris) tbl2 # an example more relevant for clinical trials with column counts lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% analyze("AGE", afun = function(x) { setNames(as.list(fivenum(x)), c( "minimum", "lower-hinge", "median", "upper-hinge", "maximum" )) }) tbl3 <- build_table(lyt3, DM) tbl3 tbl4 <- build_table(lyt3, subset(DM, AGE > 40)) tbl4 # with column counts calculated based on different data miniDM <- DM[sample(1:NROW(DM), 100), ] tbl5 <- build_table(lyt3, DM, alt_counts_df = miniDM) tbl5 tbl6 <- build_table(lyt3, DM, col_counts = 1:3) tbl6
lyt <- basic_table() %>% split_cols_by("Species") %>% analyze("Sepal.Length", afun = function(x) { list( "mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = diff(range(x)) ) }) lyt tbl <- build_table(lyt, iris) tbl # analyze multiple variables lyt2 <- basic_table() %>% split_cols_by("Species") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = function(x) { list( "mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "range" = diff(range(x)) ) }) tbl2 <- build_table(lyt2, iris) tbl2 # an example more relevant for clinical trials with column counts lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% analyze("AGE", afun = function(x) { setNames(as.list(fivenum(x)), c( "minimum", "lower-hinge", "median", "upper-hinge", "maximum" )) }) tbl3 <- build_table(lyt3, DM) tbl3 tbl4 <- build_table(lyt3, subset(DM, AGE > 40)) tbl4 # with column counts calculated based on different data miniDM <- DM[sample(1:NROW(DM), 100), ] tbl5 <- build_table(lyt3, DM, alt_counts_df = miniDM) tbl5 tbl6 <- build_table(lyt3, DM, col_counts = 1:3) tbl6
TableTree
objectsColumn-bind two TableTree
objects
cbind_rtables(x, ..., sync_count_vis = TRUE)
cbind_rtables(x, ..., sync_count_vis = TRUE)
x |
( |
... |
one or more further objects of the same class as |
sync_count_vis |
( |
A formal table object.
x <- rtable(c("A", "B"), rrow("row 1", 1, 2), rrow("row 2", 3, 4)) y <- rtable("C", rrow("row 1", 5), rrow("row 2", 6)) z <- rtable("D", rrow("row 1", 9), rrow("row 2", 10)) t1 <- cbind_rtables(x, y) t1 t2 <- cbind_rtables(x, y, z) t2 col_paths_summary(t1) col_paths_summary(t2)
x <- rtable(c("A", "B"), rrow("row 1", 1, 2), rrow("row 2", 3, 4)) y <- rtable("C", rrow("row 1", 5), rrow("row 2", 6)) z <- rtable("D", rrow("row 1", 9), rrow("row 2", 10)) t1 <- cbind_rtables(x, y) t1 t2 <- cbind_rtables(x, y, z) t2 col_paths_summary(t1) col_paths_summary(t2)
Retrieve cell values by row and column path
cell_values(tt, rowpath = NULL, colpath = NULL, omit_labrows = TRUE) value_at(tt, rowpath = NULL, colpath = NULL) ## S4 method for signature 'VTableTree' value_at(tt, rowpath = NULL, colpath = NULL)
cell_values(tt, rowpath = NULL, colpath = NULL, omit_labrows = TRUE) value_at(tt, rowpath = NULL, colpath = NULL) ## S4 method for signature 'VTableTree' value_at(tt, rowpath = NULL, colpath = NULL)
tt |
( |
rowpath |
( |
colpath |
( |
omit_labrows |
( |
cell_values
returns a list
(regardless of the type of value the cells hold). If rowpath
defines a path to
a single row, cell_values
returns the list of cell values for that row, otherwise a list of such lists, one for
each row captured underneath rowpath
. This occurs after subsetting to colpath
has occurred.
value_at
returns the "unwrapped" value of a single cell, or an error, if the combination of rowpath
and
colpath
do not define the location of a single cell in tt
.
cell_values
will return a single cell's value wrapped in a list. Use value_at
to receive the "bare" cell
value.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% split_rows_by("RACE") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% analyze("AGE") library(dplyr) ## for mutate tbl <- build_table(lyt, DM %>% mutate(SEX = droplevels(SEX), RACE = droplevels(RACE))) row_paths_summary(tbl) col_paths_summary(tbl) cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B"), c("ARM", "A: Drug X", "SEX", "F") ) # it's also possible to access multiple values by being less specific cell_values( tbl, c("RACE", "ASIAN", "STRATA1"), c("ARM", "A: Drug X", "SEX", "F") ) cell_values(tbl, c("RACE", "ASIAN"), c("ARM", "A: Drug X", "SEX", "M")) ## any arm, male columns from the ASIAN content (i.e. summary) row cell_values( tbl, c("RACE", "ASIAN", "@content"), c("ARM", "B: Placebo", "SEX", "M") ) cell_values( tbl, c("RACE", "ASIAN", "@content"), c("ARM", "*", "SEX", "M") ) ## all columns cell_values(tbl, c("RACE", "ASIAN", "STRATA1", "B")) ## all columns for the Combination arm cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B"), c("ARM", "C: Combination") ) cvlist <- cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B", "AGE", "Mean"), c("ARM", "B: Placebo", "SEX", "M") ) cvnolist <- value_at( tbl, c("RACE", "ASIAN", "STRATA1", "B", "AGE", "Mean"), c("ARM", "B: Placebo", "SEX", "M") ) stopifnot(identical(cvlist[[1]], cvnolist))
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% split_rows_by("RACE") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% analyze("AGE") library(dplyr) ## for mutate tbl <- build_table(lyt, DM %>% mutate(SEX = droplevels(SEX), RACE = droplevels(RACE))) row_paths_summary(tbl) col_paths_summary(tbl) cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B"), c("ARM", "A: Drug X", "SEX", "F") ) # it's also possible to access multiple values by being less specific cell_values( tbl, c("RACE", "ASIAN", "STRATA1"), c("ARM", "A: Drug X", "SEX", "F") ) cell_values(tbl, c("RACE", "ASIAN"), c("ARM", "A: Drug X", "SEX", "M")) ## any arm, male columns from the ASIAN content (i.e. summary) row cell_values( tbl, c("RACE", "ASIAN", "@content"), c("ARM", "B: Placebo", "SEX", "M") ) cell_values( tbl, c("RACE", "ASIAN", "@content"), c("ARM", "*", "SEX", "M") ) ## all columns cell_values(tbl, c("RACE", "ASIAN", "STRATA1", "B")) ## all columns for the Combination arm cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B"), c("ARM", "C: Combination") ) cvlist <- cell_values( tbl, c("RACE", "ASIAN", "STRATA1", "B", "AGE", "Mean"), c("ARM", "B: Placebo", "SEX", "M") ) cvnolist <- value_at( tbl, c("RACE", "ASIAN", "STRATA1", "B", "AGE", "Mean"), c("ARM", "B: Placebo", "SEX", "M") ) stopifnot(identical(cvlist[[1]], cvnolist))
Constructor for Cell Value
CellValue( val, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, footnotes = NULL, align = NULL, format_na_str = NULL )
CellValue( val, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, footnotes = NULL, align = NULL, format_na_str = NULL )
val |
( |
format |
( |
colspan |
( |
label |
( |
indent_mod |
( |
footnotes |
( |
align |
( |
format_na_str |
( |
An object representing the value within a single cell within a populated table. The underlying structure of this object is an implementation detail and should not be relied upon beyond calling accessors for the class.
Column information/structure accessors
clayout(obj) ## S4 method for signature 'VTableNodeInfo' clayout(obj) ## S4 method for signature 'PreDataTableLayouts' clayout(obj) ## S4 method for signature 'ANY' clayout(obj) clayout(object) <- value ## S4 replacement method for signature 'PreDataTableLayouts' clayout(object) <- value col_info(obj) ## S4 method for signature 'VTableNodeInfo' col_info(obj) col_info(obj) <- value ## S4 replacement method for signature 'TableRow' col_info(obj) <- value ## S4 replacement method for signature 'ElementaryTable' col_info(obj) <- value ## S4 replacement method for signature 'TableTree' col_info(obj) <- value coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'InstantiatedColumnInfo' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'PreDataTableLayouts' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'PreDataColLayout' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'LayoutColTree' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'VTableTree' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'TableRow' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) col_exprs(obj, df = NULL) ## S4 method for signature 'PreDataTableLayouts' col_exprs(obj, df = NULL) ## S4 method for signature 'PreDataColLayout' col_exprs(obj, df = NULL) ## S4 method for signature 'InstantiatedColumnInfo' col_exprs(obj, df = NULL) col_counts(obj, path = NULL) ## S4 method for signature 'InstantiatedColumnInfo' col_counts(obj, path = NULL) ## S4 method for signature 'VTableNodeInfo' col_counts(obj, path = NULL) col_counts(obj, path = NULL) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' col_counts(obj, path = NULL) <- value ## S4 replacement method for signature 'VTableNodeInfo' col_counts(obj, path = NULL) <- value col_total(obj) ## S4 method for signature 'InstantiatedColumnInfo' col_total(obj) ## S4 method for signature 'VTableNodeInfo' col_total(obj) col_total(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' col_total(obj) <- value ## S4 replacement method for signature 'VTableNodeInfo' col_total(obj) <- value
clayout(obj) ## S4 method for signature 'VTableNodeInfo' clayout(obj) ## S4 method for signature 'PreDataTableLayouts' clayout(obj) ## S4 method for signature 'ANY' clayout(obj) clayout(object) <- value ## S4 replacement method for signature 'PreDataTableLayouts' clayout(object) <- value col_info(obj) ## S4 method for signature 'VTableNodeInfo' col_info(obj) col_info(obj) <- value ## S4 replacement method for signature 'TableRow' col_info(obj) <- value ## S4 replacement method for signature 'ElementaryTable' col_info(obj) <- value ## S4 replacement method for signature 'TableTree' col_info(obj) <- value coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'InstantiatedColumnInfo' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'PreDataTableLayouts' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'PreDataColLayout' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'LayoutColTree' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'VTableTree' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) ## S4 method for signature 'TableRow' coltree( obj, df = NULL, rtpos = TreePos(), alt_counts_df = df, ccount_format = "(N=xx)" ) col_exprs(obj, df = NULL) ## S4 method for signature 'PreDataTableLayouts' col_exprs(obj, df = NULL) ## S4 method for signature 'PreDataColLayout' col_exprs(obj, df = NULL) ## S4 method for signature 'InstantiatedColumnInfo' col_exprs(obj, df = NULL) col_counts(obj, path = NULL) ## S4 method for signature 'InstantiatedColumnInfo' col_counts(obj, path = NULL) ## S4 method for signature 'VTableNodeInfo' col_counts(obj, path = NULL) col_counts(obj, path = NULL) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' col_counts(obj, path = NULL) <- value ## S4 replacement method for signature 'VTableNodeInfo' col_counts(obj, path = NULL) <- value col_total(obj) ## S4 method for signature 'InstantiatedColumnInfo' col_total(obj) ## S4 method for signature 'VTableNodeInfo' col_total(obj) col_total(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' col_total(obj) <- value ## S4 replacement method for signature 'VTableNodeInfo' col_total(obj) <- value
obj |
( |
object |
( |
value |
( |
df |
( |
rtpos |
( |
alt_counts_df |
( |
ccount_format |
( |
path |
( |
A LayoutColTree
object.
Returns various information about columns, depending on the accessor used.
Clear all indent modifiers from a table
clear_indent_mods(tt) ## S4 method for signature 'VTableTree' clear_indent_mods(tt) ## S4 method for signature 'TableRow' clear_indent_mods(tt)
clear_indent_mods(tt) ## S4 method for signature 'VTableTree' clear_indent_mods(tt) ## S4 method for signature 'TableRow' clear_indent_mods(tt)
tt |
( |
The same class as tt
, with all indent modifiers set to zero.
lyt1 <- basic_table() %>% summarize_row_groups("STUDYID", label_fstr = "overall summary") %>% split_rows_by("AEBODSYS", child_labels = "visible") %>% summarize_row_groups("STUDYID", label_fstr = "subgroup summary") %>% analyze("AGE", indent_mod = -1L) tbl1 <- build_table(lyt1, ex_adae) tbl1 clear_indent_mods(tbl1)
lyt1 <- basic_table() %>% summarize_row_groups("STUDYID", label_fstr = "overall summary") %>% split_rows_by("AEBODSYS", child_labels = "visible") %>% summarize_row_groups("STUDYID", label_fstr = "subgroup summary") %>% analyze("AGE", indent_mod = -1L) tbl1 <- build_table(lyt1, ex_adae) tbl1 clear_indent_mods(tbl1)
Value and Visibility of specific column counts by path
colcount_visible(obj, path) ## S4 method for signature 'VTableTree' colcount_visible(obj, path) ## S4 method for signature 'InstantiatedColumnInfo' colcount_visible(obj, path) ## S4 method for signature 'LayoutColTree' colcount_visible(obj, path) colcount_visible(obj, path) <- value ## S4 replacement method for signature 'VTableTree' colcount_visible(obj, path) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' colcount_visible(obj, path) <- value ## S4 replacement method for signature 'LayoutColTree' colcount_visible(obj, path) <- value
colcount_visible(obj, path) ## S4 method for signature 'VTableTree' colcount_visible(obj, path) ## S4 method for signature 'InstantiatedColumnInfo' colcount_visible(obj, path) ## S4 method for signature 'LayoutColTree' colcount_visible(obj, path) colcount_visible(obj, path) <- value ## S4 replacement method for signature 'VTableTree' colcount_visible(obj, path) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' colcount_visible(obj, path) <- value ## S4 replacement method for signature 'LayoutColTree' colcount_visible(obj, path) <- value
obj |
( |
path |
( |
value |
( |
for colcount_visible
a logical scalar
indicating whether the specified position in
the column hierarchy is set to display its column count;
for colcount_visible<-
, obj
updated with
the specified count displaying behavior set.
Users generally should not call colcount_visible
directly, as setting sibling facets to have differing
column count visibility will result in an error when
printing or paginating the table.
TableTree
Collect leaves of a TableTree
collect_leaves(tt, incl.cont = TRUE, add.labrows = FALSE)
collect_leaves(tt, incl.cont = TRUE, add.labrows = FALSE)
tt |
( |
incl.cont |
( |
add.labrows |
( |
A list of TableRow
objects for all rows in the table.
Displays the tree structure of the columns of a table or column structure object.
coltree_structure(obj)
coltree_structure(obj)
obj |
( |
Nothing, called for its side effect of displaying a summary to the terminal.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("STRATA1") %>% split_cols_by("SEX", nested = FALSE) %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) coltree_structure(tbl)
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("STRATA1") %>% split_cols_by("SEX", nested = FALSE) %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) coltree_structure(tbl)
Prints a matrix where .
means cell matches, X
means cell does
not match, +
cell (row) is missing, and -
cell (row)
should not be there. If structure
is set to TRUE
, C
indicates
column-structure mismatch, R
indicates row-structure mismatch, and
S
indicates mismatch in both row and column structure.
compare_rtables( object, expected, tol = 0.1, comp.attr = TRUE, structure = FALSE )
compare_rtables( object, expected, tol = 0.1, comp.attr = TRUE, structure = FALSE )
object |
( |
expected |
( |
tol |
( |
comp.attr |
( |
structure |
( |
A matrix of class rtables_diff
representing the differences
between object
and expected
as described above.
In its current form, compare_rtables
does not take structure into
account, only row and cell position.
t1 <- rtable(header = c("A", "B"), format = "xx", rrow("row 1", 1, 2)) t2 <- rtable(header = c("A", "B", "C"), format = "xx", rrow("row 1", 1, 2, 3)) compare_rtables(object = t1, expected = t2) if (interactive()) { Viewer(t1, t2) } expected <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) expected object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) compare_rtables(object, expected, comp.attr = FALSE) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title") ) compare_rtables(object, expected) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 14, 15.03), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) compare_rtables(object, expected) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.x, xx.x)")) ) compare_rtables(object, expected)
t1 <- rtable(header = c("A", "B"), format = "xx", rrow("row 1", 1, 2)) t2 <- rtable(header = c("A", "B", "C"), format = "xx", rrow("row 1", 1, 2, 3)) compare_rtables(object = t1, expected = t2) if (interactive()) { Viewer(t1, t2) } expected <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) expected object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) compare_rtables(object, expected, comp.attr = FALSE) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title") ) compare_rtables(object, expected) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 14, 15.03), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.xx, xx.xx)")) ) compare_rtables(object, expected) object <- rtable( header = c("ARM A\nN=100", "ARM B\nN=200"), format = "xx", rrow("row 1", 10, 15), rrow(), rrow("section title"), rrow("row colspan", rcell(c(.345543, .4432423), colspan = 2, format = "(xx.x, xx.x)")) ) compare_rtables(object, expected)
Compatibility argument conventions
compat_args(.lst, row.name, format, indent, label, inset)
compat_args(.lst, row.name, format, indent, label, inset)
.lst |
( |
row.name |
( |
format |
( |
indent |
|
label |
( |
inset |
( |
No return value.
Other conventions:
constr_args()
,
gen_args()
,
lyt_args()
,
sf_args()
TableTrees
Score functions for sorting TableTrees
cont_n_allcols(tt) cont_n_onecol(j)
cont_n_allcols(tt) cont_n_onecol(j)
tt |
( |
j |
( |
A single numeric value indicating score according to the relevant metric for tt
, to be used when sorting.
For examples and details, please read the documentation for sort_at_path()
and the
Sorting and Pruning
vignette.
TableTree
Returns the content table of obj
if it is a TableTree
object, or NULL
otherwise.
content_table(obj) content_table(obj) <- value
content_table(obj) content_table(obj) <- value
obj |
( |
value |
( |
the ElementaryTable
containing the (top level) content rows of obj
(or NULL
if obj
is not
a formal table object).
Analysis function to count levels of a factor with percentage of the column total
counts_wpcts(x, .N_col)
counts_wpcts(x, .N_col)
x |
( |
.N_col |
( |
A RowsVerticalSection
object with counts (and percents) for each level of the factor.
counts_wpcts(DM$SEX, 400)
counts_wpcts(DM$SEX, 400)
Split functions provide the work-horse for rtables
's generalized partitioning. These functions accept a (sub)set
of incoming data and a split object, and return "splits" of that data.
User-defined custom split functions can perform any type of computation on the incoming data provided that they meet the requirements for generating "splits" of the incoming data based on the split object.
Split functions are functions that accept:
a data.frame
of incoming data to be split.
a Split object. This is largely an internal detail custom functions will not need to worry about,
but obj_name(spl)
, for example, will give the name of the split as it will appear in paths in the resulting
table.
any pre-calculated values. If given non-NULL
values, the values returned should match these.
Should be NULL
in most cases and can usually be ignored.
any pre-calculated value labels. Same as above for values
.
if TRUE
, resulting splits that are empty are removed.
a data.frame
describing previously performed splits which collectively
arrived at df
.
The function must then output a named list
with the following elements:
the vector of all values corresponding to the splits of df
.
a list of data.frame
s representing the groupings of the actual observations from df
.
a character vector giving a string label for each value listed in the values
element above.
if present, extra arguments are to be passed to summary and analysis functions
whenever they are executed on the corresponding element of datasplit
or a subset thereof.
One way to generate custom splitting functions is to wrap existing split functions and modify either the incoming data before they are called or their outputs.
make_split_fun()
for the API for creating custom split functions, and split_funcs for a variety of
pre-defined split functions.
# Example of a picky split function. The number of values in the column variable # var decrees if we are going to print also the column with all observation # or not. picky_splitter <- function(var) { # Main layout function function(df, spl, vals, labels, trim) { orig_vals <- vals # Check for number of levels if all are selected if (is.null(vals)) { vec <- df[[var]] vals <- unique(vec) } # Do a split with or without All obs if (length(vals) == 1) { do_base_split(spl = spl, df = df, vals = vals, labels = labels, trim = trim) } else { fnc_tmp <- add_overall_level("Overall", label = "All Obs", first = FALSE) fnc_tmp(df = df, spl = spl, vals = orig_vals, trim = trim) } } } # Data sub-set d1 <- subset(ex_adsl, ARM == "A: Drug X" | (ARM == "B: Placebo" & SEX == "F")) d1 <- subset(d1, SEX %in% c("M", "F")) d1$SEX <- factor(d1$SEX) # This table uses the number of values in the SEX column to add the overall col or not lyt <- basic_table() %>% split_cols_by("ARM", split_fun = drop_split_levels) %>% split_cols_by("SEX", split_fun = picky_splitter("SEX")) %>% analyze("AGE", show_labels = "visible") tbl <- build_table(lyt, d1) tbl
# Example of a picky split function. The number of values in the column variable # var decrees if we are going to print also the column with all observation # or not. picky_splitter <- function(var) { # Main layout function function(df, spl, vals, labels, trim) { orig_vals <- vals # Check for number of levels if all are selected if (is.null(vals)) { vec <- df[[var]] vals <- unique(vec) } # Do a split with or without All obs if (length(vals) == 1) { do_base_split(spl = spl, df = df, vals = vals, labels = labels, trim = trim) } else { fnc_tmp <- add_overall_level("Overall", label = "All Obs", first = FALSE) fnc_tmp(df = df, spl = spl, vals = orig_vals, trim = trim) } } } # Data sub-set d1 <- subset(ex_adsl, ARM == "A: Drug X" | (ARM == "B: Placebo" & SEX == "F")) d1 <- subset(d1, SEX %in% c("M", "F")) d1$SEX <- factor(d1$SEX) # This table uses the number of values in the SEX column to add the overall col or not lyt <- basic_table() %>% split_cols_by("ARM", split_fun = drop_split_levels) %>% split_cols_by("SEX", split_fun = picky_splitter("SEX")) %>% analyze("AGE", show_labels = "visible") tbl <- build_table(lyt, d1) tbl
Collection of utilities to extract data.frame
objects from TableTree
objects.
as_result_df(tt, spec = "v0_experimental", simplify = FALSE, ...) result_df_specs() path_enriched_df(tt, path_fun = collapse_path, value_fun = collapse_values)
as_result_df(tt, spec = "v0_experimental", simplify = FALSE, ...) result_df_specs() path_enriched_df(tt, path_fun = collapse_path, value_fun = collapse_values)
tt |
( |
spec |
( |
simplify |
( |
... |
additional arguments passed to spec-specific result data frame conversion function. Currently it can be
one or more of the following parameters (valid only for
|
path_fun |
( |
value_fun |
( |
as_result_df()
: Result data frame specifications may differ in the exact information
they include and the form in which they represent it. Specifications whose names end in "_experimental"
are subject to change without notice, but specifications without the "_experimental"
suffix will remain available including any bugs in their construction indefinitely.
as_result_df
returns a result data.frame
.
result_df_specs()
returns a named list of result data frame extraction functions by "specification".
path_enriched_df()
returns a data.frame
of tt
's cell values (processed by value_fun
, with columns named by
the full column paths (processed by path_fun
and an additional row_path
column with the row paths (processed
by path_fun
).
result_df_specs()
: A list of functions that extract result data frames from TableTree
s.
path_enriched_df()
: Transform a TableTree
object to a path-enriched data.frame
.
df_to_tt()
when using as_is = TRUE
and formatters::make_row_df()
to have a comprehensive view of the
hierarchical structure of the rows.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("STRATA1") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) as_result_df(tbl) result_df_specs() lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) path_enriched_df(tbl)
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("STRATA1") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) as_result_df(tbl) result_df_specs() lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) path_enriched_df(tbl)
ElementaryTable
from a data.frame
Create an ElementaryTable
from a data.frame
df_to_tt(df)
df_to_tt(df)
df |
( |
If row names are not defined in df
(or they are simple numbers), then the row names are taken from the column
label_name
, if it exists. If label_name
exists, then it is also removed from the original data. This behavior
is compatible with as_result_df()
, when as_is = TRUE
and the row names are not unique.
as_result_df()
for the inverse operation.
df_to_tt(mtcars)
df_to_tt(mtcars)
This function is intended for use inside custom split functions. It applies the current split as if it had no custom splitting function so that those default splits can be further manipulated.
do_base_split(spl, df, vals = NULL, labels = NULL, trim = FALSE)
do_base_split(spl, df, vals = NULL, labels = NULL, trim = FALSE)
spl |
( |
df |
( |
vals |
( |
labels |
( |
trim |
( |
The result of the split being applied as if it had no custom split function. See custom_split_funs.
uneven_splfun <- function(df, spl, vals = NULL, labels = NULL, trim = FALSE) { ret <- do_base_split(spl, df, vals, labels, trim) if (NROW(df) == 0) { ret <- lapply(ret, function(x) x[1]) } ret } lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("USUBJID", "AESEQ", "BMRKR1"), varlabels = c("N", "E", "BMR1"), split_fun = uneven_splfun ) %>% analyze_colvars(list( USUBJID = function(x, ...) length(unique(x)), AESEQ = max, BMRKR1 = mean )) tbl <- build_table(lyt, subset(ex_adae, as.numeric(ARM) <= 2)) tbl
uneven_splfun <- function(df, spl, vals = NULL, labels = NULL, trim = FALSE) { ret <- do_base_split(spl, df, vals, labels, trim) if (NROW(df) == 0) { ret <- lapply(ret, function(x) x[1]) } ret } lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("USUBJID", "AESEQ", "BMRKR1"), varlabels = c("N", "E", "BMR1"), split_fun = uneven_splfun ) %>% analyze_colvars(list( USUBJID = function(x, ...) length(unique(x)), AESEQ = max, BMRKR1 = mean )) tbl <- build_table(lyt, subset(ex_adae, as.numeric(ARM) <= 2)) tbl
make_split_fun
This function is intended for use as a pre-processing component in make_split_fun
, and should not be called
directly by end users.
drop_facet_levels(df, spl, ...)
drop_facet_levels(df, spl, ...)
df |
( |
spl |
( |
... |
additional parameters passed internally. |
Other make_custom_split:
add_combo_facet()
,
make_split_fun()
,
make_split_result()
,
trim_levels_in_facets()
TableTree
classesTableTree
classes
Table constructors and classes
ElementaryTable( kids = list(), name = "", lev = 1L, label = "", labelrow = LabelRow(lev = lev, label = label, vis = !isTRUE(iscontent) && !is.na(label) && nzchar(label)), rspans = data.frame(), cinfo = NULL, iscontent = NA, var = NA_character_, format = NULL, na_str = NA_character_, indent_mod = 0L, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), header_section_div = NA_character_, hsep = default_hsep(), trailing_section_div = NA_character_, inset = 0L ) TableTree( kids = list(), name = if (!is.na(var)) var else "", cont = EmptyElTable, lev = 1L, label = name, labelrow = LabelRow(lev = lev, label = label, vis = nrow(cont) == 0 && !is.na(label) && nzchar(label)), rspans = data.frame(), iscontent = NA, var = NA_character_, cinfo = NULL, format = NULL, na_str = NA_character_, indent_mod = 0L, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), page_title = NA_character_, hsep = default_hsep(), header_section_div = NA_character_, trailing_section_div = NA_character_, inset = 0L )
ElementaryTable( kids = list(), name = "", lev = 1L, label = "", labelrow = LabelRow(lev = lev, label = label, vis = !isTRUE(iscontent) && !is.na(label) && nzchar(label)), rspans = data.frame(), cinfo = NULL, iscontent = NA, var = NA_character_, format = NULL, na_str = NA_character_, indent_mod = 0L, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), header_section_div = NA_character_, hsep = default_hsep(), trailing_section_div = NA_character_, inset = 0L ) TableTree( kids = list(), name = if (!is.na(var)) var else "", cont = EmptyElTable, lev = 1L, label = name, labelrow = LabelRow(lev = lev, label = label, vis = nrow(cont) == 0 && !is.na(label) && nzchar(label)), rspans = data.frame(), iscontent = NA, var = NA_character_, cinfo = NULL, format = NULL, na_str = NA_character_, indent_mod = 0L, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), page_title = NA_character_, hsep = default_hsep(), header_section_div = NA_character_, trailing_section_div = NA_character_, inset = 0L )
kids |
( |
name |
( |
lev |
( |
label |
( |
labelrow |
( |
rspans |
( |
cinfo |
( |
iscontent |
( |
var |
( |
format |
( |
na_str |
( |
indent_mod |
( |
title |
( |
subtitles |
( |
main_footer |
( |
prov_footer |
( |
header_section_div |
( |
hsep |
( |
trailing_section_div |
( |
inset |
( |
cont |
( |
page_title |
( |
A formal object representing a populated table.
Gabriel Becker
Empty objects of various types to compare against efficiently.
From a table, produce a self-contained word document or attach it to a template word
file (template_file
). This function is based on the tt_to_flextable()
transformer and
the officer
package.
export_as_docx( tt, file, doc_metadata = NULL, titles_as_header = FALSE, footers_as_text = TRUE, template_file = NULL, section_properties = section_properties_default(), ... ) section_properties_default( page_size = c("letter", "A4"), orientation = c("portrait", "landscape") ) margins_potrait() margins_landscape()
export_as_docx( tt, file, doc_metadata = NULL, titles_as_header = FALSE, footers_as_text = TRUE, template_file = NULL, section_properties = section_properties_default(), ... ) section_properties_default( page_size = c("letter", "A4"), orientation = c("portrait", "landscape") ) margins_potrait() margins_landscape()
tt |
( |
file |
( |
doc_metadata |
( |
titles_as_header |
( |
footers_as_text |
( |
template_file |
( |
section_properties |
( |
... |
( |
page_size |
( |
orientation |
( |
section_properties_default()
: Helper function that defines standard portrait properties for tables.
margins_potrait()
: Helper function that defines standard portrait margins for tables.
margins_landscape()
: Helper function that defines standard landscape margins for tables.
export_as_docx()
has few customization options available. If you require specific formats and details,
we suggest that you use tt_to_flextable()
prior to export_as_docx
. Only the titles_as_header
and
footer_as_text
parameters must be re-specified if the table is changed first using tt_to_flextable()
.
library(flextable) lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2", "COUNTRY")) tbl <- build_table(lyt, ex_adsl) # See how section_properties_portrait function is built for custom tf <- tempfile(fileext = ".docx") export_as_docx(tbl, file = tf, section_properties = section_properties_default(orientation = "landscape") )
library(flextable) lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2", "COUNTRY")) tbl <- build_table(lyt, ex_adsl) # See how section_properties_portrait function is built for custom tf <- tempfile(fileext = ".docx") export_as_docx(tbl, file = tf, section_properties = section_properties_default(orientation = "landscape") )
This function creates a flat tabular file of cell values and corresponding paths via path_enriched_df()
. It then
writes that data frame out as a tsv
file.
export_as_tsv( tt, file = NULL, path_fun = collapse_path, value_fun = collapse_values, sep = "\t", ... ) import_from_tsv(file)
export_as_tsv( tt, file = NULL, path_fun = collapse_path, value_fun = collapse_values, sep = "\t", ... ) import_from_tsv(file)
tt |
( |
file |
( |
path_fun |
( |
value_fun |
( |
sep |
( |
... |
( |
By default (i.e. when value_func
is not specified, list columns where at least one value has length > 1 are
collapsed to character vectors by collapsing the list element with "|"
.
export_as_tsv
returns NULL
silently.
import_from_tsv
returns a data.frame
with re-constituted list values.
There is currently no round-trip capability for this type of export. You can read values exported this way back in
via import_from_tsv
but you will receive only the data.frame
version back, NOT a TableTree
.
path_enriched_df()
for the underlying function that does the work.
Get or set column count for a facet in column space
facet_colcount(obj, path) ## S4 method for signature 'LayoutColTree' facet_colcount(obj, path = NULL) ## S4 method for signature 'LayoutColLeaf' facet_colcount(obj, path = NULL) ## S4 method for signature 'VTableTree' facet_colcount(obj, path) ## S4 method for signature 'InstantiatedColumnInfo' facet_colcount(obj, path) facet_colcount(obj, path) <- value ## S4 replacement method for signature 'LayoutColTree' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'LayoutColLeaf' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'VTableTree' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' facet_colcount(obj, path) <- value
facet_colcount(obj, path) ## S4 method for signature 'LayoutColTree' facet_colcount(obj, path = NULL) ## S4 method for signature 'LayoutColLeaf' facet_colcount(obj, path = NULL) ## S4 method for signature 'VTableTree' facet_colcount(obj, path) ## S4 method for signature 'InstantiatedColumnInfo' facet_colcount(obj, path) facet_colcount(obj, path) <- value ## S4 replacement method for signature 'LayoutColTree' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'LayoutColLeaf' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'VTableTree' facet_colcount(obj, path) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' facet_colcount(obj, path) <- value
obj |
( |
path |
character. This path must end on a split value, e.g., the level of a categorical variable that was split on in column space, but it need not be the path to an individual column. |
value |
( |
for facet_colcount
the current count associated
with that facet in column space, for facet_colcount<-
,
obj
modified with the new column count for the specified
facet.
Updating a lower-level (more specific) column count manually will not update the counts for its parent facets. This cannot be made automatic because the rtables framework does not require sibling facets to be mutually exclusive (e.g., total "arm", faceting into cumulative quantiles, etc) and thus the count of a parent facet will not always be simply the sum of the counts for all of its children.
lyt <- basic_table() %>% split_cols_by("ARM", show_colcounts = TRUE) %>% split_cols_by("SEX", split_fun = keep_split_levels(c("F", "M")), show_colcounts = TRUE ) %>% split_cols_by("STRATA1", show_colcounts = TRUE) %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) facet_colcount(tbl, c("ARM", "A: Drug X")) facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F")) facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "A")) ## modify specific count after table creation facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "A")) <- 25 ## show black space for certain counts by assign NA facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "C")) <- NA
lyt <- basic_table() %>% split_cols_by("ARM", show_colcounts = TRUE) %>% split_cols_by("SEX", split_fun = keep_split_levels(c("F", "M")), show_colcounts = TRUE ) %>% split_cols_by("STRATA1", show_colcounts = TRUE) %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) facet_colcount(tbl, c("ARM", "A: Drug X")) facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F")) facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "A")) ## modify specific count after table creation facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "A")) <- 25 ## show black space for certain counts by assign NA facet_colcount(tbl, c("ARM", "A: Drug X", "SEX", "F", "STRATA1", "C")) <- NA
Set visibility of column counts for a group of sibling facets
facet_colcounts_visible(obj, path) <- value
facet_colcounts_visible(obj, path) <- value
obj |
( |
path |
( |
value |
( |
obj, modified with the desired column count. display behavior
This function returns a list with the row-paths to all structural subtables which contain no data rows (even if they have associated content rows).
find_degen_struct(tt)
find_degen_struct(tt)
tt |
( |
A list of character vectors representing the row paths, if any, to degenerate substructures within the table.
Other table structure validation functions:
sanitize_table_struct()
,
validate_table_struct()
find_degen_struct(rtable("hi"))
find_degen_struct(rtable("hi"))
rcell
objectsThis is a wrapper for formatters::format_value()
for use with CellValue
objects
format_rcell( x, format, output = c("ascii", "html"), na_str = obj_na_str(x) %||% "NA", pr_row_format = NULL, pr_row_na_str = NULL, shell = FALSE )
format_rcell( x, format, output = c("ascii", "html"), na_str = obj_na_str(x) %||% "NA", pr_row_format = NULL, pr_row_na_str = NULL, shell = FALSE )
x |
( |
format |
( |
output |
( |
na_str |
( |
pr_row_format |
( |
pr_row_na_str |
( |
shell |
( |
Formatted text.
cll <- CellValue(pi, format = "xx.xxx") format_rcell(cll) # Cell values precedes the row values cll <- CellValue(pi, format = "xx.xxx") format_rcell(cll, pr_row_format = "xx.x") # Similarly for NA values cll <- CellValue(NA, format = "xx.xxx", format_na_str = "This is THE NA") format_rcell(cll, pr_row_na_str = "This is NA")
cll <- CellValue(pi, format = "xx.xxx") format_rcell(cll) # Cell values precedes the row values cll <- CellValue(pi, format = "xx.xxx") format_rcell(cll, pr_row_format = "xx.x") # Similarly for NA values cll <- CellValue(NA, format = "xx.xxx", format_na_str = "This is THE NA") format_rcell(cll, pr_row_na_str = "This is NA")
Get formatted cells
get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'TableTree' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'ElementaryTable' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'TableRow' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'LabelRow' get_formatted_cells(obj, shell = FALSE) get_cell_aligns(obj) ## S4 method for signature 'TableTree' get_cell_aligns(obj) ## S4 method for signature 'ElementaryTable' get_cell_aligns(obj) ## S4 method for signature 'TableRow' get_cell_aligns(obj) ## S4 method for signature 'LabelRow' get_cell_aligns(obj)
get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'TableTree' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'ElementaryTable' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'TableRow' get_formatted_cells(obj, shell = FALSE) ## S4 method for signature 'LabelRow' get_formatted_cells(obj, shell = FALSE) get_cell_aligns(obj) ## S4 method for signature 'TableTree' get_cell_aligns(obj) ## S4 method for signature 'ElementaryTable' get_cell_aligns(obj) ## S4 method for signature 'TableRow' get_cell_aligns(obj) ## S4 method for signature 'LabelRow' get_cell_aligns(obj)
obj |
( |
shell |
( |
The formatted print-strings for all (body) cells in obj
.
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() tbl <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") %>% build_table(iris2) get_formatted_cells(tbl)
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() tbl <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") %>% build_table(iris2) get_formatted_cells(tbl)
Head and tail methods
head(x, ...) ## S4 method for signature 'VTableTree' head( x, n = 6, ..., keep_topleft = TRUE, keep_titles = TRUE, keep_footers = keep_titles, reindex_refs = FALSE ) tail(x, ...) ## S4 method for signature 'VTableTree' tail( x, n = 6, ..., keep_topleft = TRUE, keep_titles = TRUE, keep_footers = keep_titles, reindex_refs = FALSE )
head(x, ...) ## S4 method for signature 'VTableTree' head( x, n = 6, ..., keep_topleft = TRUE, keep_titles = TRUE, keep_footers = keep_titles, reindex_refs = FALSE ) tail(x, ...) ## S4 method for signature 'VTableTree' tail( x, n = 6, ..., keep_topleft = TRUE, keep_titles = TRUE, keep_footers = keep_titles, reindex_refs = FALSE )
x |
an object |
... |
arguments to be passed to or from other methods. |
n |
an integer vector of length up to |
keep_topleft |
( |
keep_titles |
( |
keep_footers |
( |
reindex_refs |
( |
Access or recursively set header-body separator for tables
horizontal_sep(obj) ## S4 method for signature 'VTableTree' horizontal_sep(obj) horizontal_sep(obj) <- value ## S4 replacement method for signature 'VTableTree' horizontal_sep(obj) <- value ## S4 replacement method for signature 'TableRow' horizontal_sep(obj) <- value
horizontal_sep(obj) ## S4 method for signature 'VTableTree' horizontal_sep(obj) horizontal_sep(obj) <- value ## S4 replacement method for signature 'VTableTree' horizontal_sep(obj) <- value ## S4 replacement method for signature 'TableRow' horizontal_sep(obj) <- value
obj |
( |
value |
( |
horizontal_sep
returns the string acting as the header separator.
horizontal_sep<-
returns obj
, with the new header separator applied recursively to it and all its
subtables.
Define the cells that get placed into multiple rows in afun
.
in_rows( ..., .list = NULL, .names = NULL, .labels = NULL, .formats = NULL, .indent_mods = NULL, .cell_footnotes = list(NULL), .row_footnotes = list(NULL), .aligns = NULL, .format_na_strs = NULL )
in_rows( ..., .list = NULL, .names = NULL, .labels = NULL, .formats = NULL, .indent_mods = NULL, .cell_footnotes = list(NULL), .row_footnotes = list(NULL), .aligns = NULL, .format_na_strs = NULL )
... |
single row defining expressions. |
.list |
( |
.names |
( |
.labels |
( |
.formats |
( |
.indent_mods |
( |
.cell_footnotes |
( |
.row_footnotes |
( |
.aligns |
( |
.format_na_strs |
( |
A RowsVerticalSection
object (or NULL
). The details of this object should be considered an
internal implementation detail.
In post-processing, referential footnotes can also be added using row and column
paths with fnotes_at_path<-
.
in_rows(1, 2, 3, .names = c("a", "b", "c")) in_rows(1, 2, 3, .labels = c("a", "b", "c")) in_rows(1, 2, 3, .names = c("a", "b", "c"), .labels = c("AAA", "BBB", "CCC")) in_rows(.list = list(a = 1, b = 2, c = 3)) in_rows(1, 2, .list = list(3), .names = c("a", "b", "c")) lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = function(x) { in_rows( "Mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "Range" = rcell(range(x), format = "xx.xx - xx.xx") ) }) tbl <- build_table(lyt, ex_adsl) tbl
in_rows(1, 2, 3, .names = c("a", "b", "c")) in_rows(1, 2, 3, .labels = c("a", "b", "c")) in_rows(1, 2, 3, .names = c("a", "b", "c"), .labels = c("AAA", "BBB", "CCC")) in_rows(.list = list(a = 1, b = 2, c = 3)) in_rows(1, 2, .list = list(3), .names = c("a", "b", "c")) lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze("AGE", afun = function(x) { in_rows( "Mean (sd)" = rcell(c(mean(x), sd(x)), format = "xx.xx (xx.xx)"), "Range" = rcell(range(x), format = "xx.xx - xx.xx") ) }) tbl <- build_table(lyt, ex_adsl) tbl
rrows
in an rtable
Change indentation of all rrows
in an rtable
indent(x, by = 1)
indent(x, by = 1)
x |
( |
by |
( |
x
with its indent modifier incremented by by
.
is_setosa <- iris$Species == "setosa" m_tbl <- 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" ), rrow( row.name = "Setosa", mean(iris$Sepal.Length[is_setosa]), median(iris$Sepal.Length[is_setosa]), mean(iris$Petal.Length[is_setosa]), median(iris$Petal.Length[is_setosa]), format = "xx.xx" ) ) indent(m_tbl) indent(m_tbl, 2)
is_setosa <- iris$Species == "setosa" m_tbl <- 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" ), rrow( row.name = "Setosa", mean(iris$Sepal.Length[is_setosa]), median(iris$Sepal.Length[is_setosa]), mean(iris$Petal.Length[is_setosa]), median(iris$Petal.Length[is_setosa]), format = "xx.xx" ) ) indent(m_tbl) indent(m_tbl, 2)
Used in rtables to indent row names for the ASCII output.
indent_string(x, indent = 0, incr = 2, including_newline = TRUE)
indent_string(x, indent = 0, incr = 2, including_newline = TRUE)
x |
( |
indent |
( |
incr |
( |
including_newline |
( |
x
, indented with left-padding with indent * incr
white-spaces.
indent_string("a", 0) indent_string("a", 1) indent_string(letters[1:3], 0:2) indent_string(paste0(letters[1:3], "\n", LETTERS[1:3]), 0:2)
indent_string("a", 0) indent_string("a", 1) indent_string(letters[1:3], 0:2) indent_string(paste0(letters[1:3], "\n", LETTERS[1:3]), 0:2)
Insert a row into an existing table directly before or directly after an existing data (i.e., non-content and non-label) row, specified by its path.
insert_row_at_path(tt, path, value, after = FALSE) ## S4 method for signature 'VTableTree,DataRow' insert_row_at_path(tt, path, value, after = FALSE) ## S4 method for signature 'VTableTree,ANY' insert_row_at_path(tt, path, value)
insert_row_at_path(tt, path, value, after = FALSE) ## S4 method for signature 'VTableTree,DataRow' insert_row_at_path(tt, path, value, after = FALSE) ## S4 method for signature 'VTableTree,ANY' insert_row_at_path(tt, path, value)
tt |
( |
path |
( |
value |
( |
after |
( |
lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("CHN", "USA"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl2 <- insert_row_at_path( tbl, c("COUNTRY", "CHN", "AGE", "Mean"), rrow("new row", 555) ) tbl2 tbl3 <- insert_row_at_path(tbl2, c("COUNTRY", "CHN", "AGE", "Mean"), rrow("new row redux", 888), after = TRUE ) tbl3
lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("CHN", "USA"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl2 <- insert_row_at_path( tbl, c("COUNTRY", "CHN", "AGE", "Mean"), rrow("new row", 555) ) tbl2 tbl3 <- insert_row_at_path(tbl2, c("COUNTRY", "CHN", "AGE", "Mean"), rrow("new row redux", 888), after = TRUE ) tbl3
rrow
s at (before) a specific locationinsert_rrow(tbl, rrow, at = 1, ascontent = FALSE)
insert_rrow(tbl, rrow, at = 1, ascontent = FALSE)
tbl |
( |
rrow |
( |
at |
( |
ascontent |
( |
This function is deprecated and will be removed in a future release of rtables
. Please use
insert_row_at_path()
or label_at_path()
instead.
A TableTree
of the same specific class as tbl
.
Label rows (i.e. a row with no data values, only a row.name
) can only be inserted at positions which do
not already contain a label row when there is a non-trivial nested row structure in tbl
.
o <- options(warn = 0) lyt <- basic_table() %>% split_cols_by("Species") %>% analyze("Sepal.Length") tbl <- build_table(lyt, iris) insert_rrow(tbl, rrow("Hello World")) insert_rrow(tbl, rrow("Hello World"), at = 2) lyt2 <- basic_table() %>% split_cols_by("Species") %>% split_rows_by("Species") %>% analyze("Sepal.Length") tbl2 <- build_table(lyt2, iris) insert_rrow(tbl2, rrow("Hello World")) insert_rrow(tbl2, rrow("Hello World"), at = 2) insert_rrow(tbl2, rrow("Hello World"), at = 4) insert_rrow(tbl2, rrow("new row", 5, 6, 7)) insert_rrow(tbl2, rrow("new row", 5, 6, 7), at = 3) options(o)
o <- options(warn = 0) lyt <- basic_table() %>% split_cols_by("Species") %>% analyze("Sepal.Length") tbl <- build_table(lyt, iris) insert_rrow(tbl, rrow("Hello World")) insert_rrow(tbl, rrow("Hello World"), at = 2) lyt2 <- basic_table() %>% split_cols_by("Species") %>% split_rows_by("Species") %>% analyze("Sepal.Length") tbl2 <- build_table(lyt2, iris) insert_rrow(tbl2, rrow("Hello World")) insert_rrow(tbl2, rrow("Hello World"), at = 2) insert_rrow(tbl2, rrow("Hello World"), at = 4) insert_rrow(tbl2, rrow("new row", 5, 6, 7)) insert_rrow(tbl2, rrow("new row", 5, 6, 7), at = 3) options(o)
Instantiated column info
InstantiatedColumnInfo( treelyt = LayoutColTree(colcount = total_cnt), csubs = list(expression(TRUE)), extras = list(list()), cnts = NA_integer_, total_cnt = NA_integer_, dispcounts = FALSE, countformat = "(N=xx)", count_na_str = "", topleft = character() )
InstantiatedColumnInfo( treelyt = LayoutColTree(colcount = total_cnt), csubs = list(expression(TRUE)), extras = list(list()), cnts = NA_integer_, total_cnt = NA_integer_, dispcounts = FALSE, countformat = "(N=xx)", count_na_str = "", topleft = character() )
treelyt |
( |
csubs |
( |
extras |
( |
cnts |
( |
total_cnt |
( |
dispcounts |
( |
countformat |
( |
count_na_str |
( |
topleft |
( |
An InstantiateadColumnInfo
object.
rtable
Check if an object is a valid rtable
is_rtable(x)
is_rtable(x)
x |
( |
TRUE
if x
is a formal TableTree
object, FALSE
otherwise.
is_rtable(build_table(basic_table(), iris))
is_rtable(build_table(basic_table(), iris))
Accesses or sets the label at a path.
label_at_path(tt, path) label_at_path(tt, path) <- value
label_at_path(tt, path) label_at_path(tt, path) <- value
tt |
( |
path |
( |
value |
( |
If path
resolves to a single row, the label for that row is retrieved or set. If, instead, path
resolves to a
subtable, the text for the row-label associated with that path is retrieved or set. In the subtable case, if the
label text is set to a non-NA
value, the labelrow
will be set to visible, even if it was not before. Similarly,
if the label row text for a subtable is set to NA
, the label row will bet set to non-visible, so the row will not
appear at all when the table is printed.
When changing the row labels for content rows, it is important to path all the way to the row. Paths
ending in "@content"
will not exhibit the behavior you want, and are thus an error. See row_paths()
for help
determining the full paths to content rows.
lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("CHN", "USA"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) label_at_path(tbl, c("COUNTRY", "CHN")) label_at_path(tbl, c("COUNTRY", "USA")) <- "United States" tbl
lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("CHN", "USA"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) label_at_path(tbl, c("COUNTRY", "CHN")) label_at_path(tbl, c("COUNTRY", "USA")) <- "United States" tbl
Row classes and constructors
Row constructors and classes
LabelRow( lev = 1L, label = "", name = label, vis = !is.na(label) && nzchar(label), cinfo = EmptyColInfo, indent_mod = 0L, table_inset = 0L, trailing_section_div = NA_character_ ) .tablerow( vals = list(), name = "", lev = 1L, label = name, cspan = rep(1L, length(vals)), cinfo = EmptyColInfo, var = NA_character_, format = NULL, na_str = NA_character_, klass, indent_mod = 0L, footnotes = list(), table_inset = 0L, trailing_section_div = NA_character_ ) DataRow(...) ContentRow(...)
LabelRow( lev = 1L, label = "", name = label, vis = !is.na(label) && nzchar(label), cinfo = EmptyColInfo, indent_mod = 0L, table_inset = 0L, trailing_section_div = NA_character_ ) .tablerow( vals = list(), name = "", lev = 1L, label = name, cspan = rep(1L, length(vals)), cinfo = EmptyColInfo, var = NA_character_, format = NULL, na_str = NA_character_, klass, indent_mod = 0L, footnotes = list(), table_inset = 0L, trailing_section_div = NA_character_ ) DataRow(...) ContentRow(...)
lev |
( |
label |
( |
name |
( |
vis |
( |
cinfo |
( |
indent_mod |
( |
table_inset |
( |
trailing_section_div |
( |
vals |
( |
cspan |
( |
var |
( |
format |
( |
na_str |
( |
klass |
( |
footnotes |
( |
... |
additional parameters passed to shared constructor ( |
A formal object representing a table row of the constructed type.
Gabriel Becker
Length of a Cell value
## S4 method for signature 'CellValue' length(x)
## S4 method for signature 'CellValue' length(x)
x |
( |
Always returns 1L
.
Returns a function that coerces the return values of a function to a list
list_wrap_x(f) list_wrap_df(f)
list_wrap_x(f) list_wrap_df(f)
f |
( |
list_wrap_x
generates a wrapper which takes x
as its first argument, while list_wrap_df
generates an
otherwise identical wrapper function whose first argument is named df
.
We provide both because when using the functions as tabulation in analyze()
, functions which take df
as
their first argument are passed the full subset data frame, while those which accept anything else notably
including x
are passed only the relevant subset of the variable being analyzed.
A function that returns a list of CellValue
objects.
Gabriel Becker
summary(iris$Sepal.Length) f <- list_wrap_x(summary) f(x = iris$Sepal.Length) f2 <- list_wrap_df(summary) f2(df = iris$Sepal.Length)
summary(iris$Sepal.Length) f <- list_wrap_x(summary) f(x = iris$Sepal.Length) f2 <- list_wrap_df(summary) f2(df = iris$Sepal.Length)
Create a custom analysis function wrapping an existing function
make_afun( fun, .stats = NULL, .formats = NULL, .labels = NULL, .indent_mods = NULL, .ungroup_stats = NULL, .format_na_strs = NULL, ..., .null_ref_cells = ".in_ref_col" %in% names(formals(fun)) )
make_afun( fun, .stats = NULL, .formats = NULL, .labels = NULL, .indent_mods = NULL, .ungroup_stats = NULL, .format_na_strs = NULL, ..., .null_ref_cells = ".in_ref_col" %in% names(formals(fun)) )
fun |
( |
.stats |
( |
.formats |
( |
.labels |
( |
.indent_mods |
( |
.ungroup_stats |
( |
.format_na_strs |
( |
... |
additional arguments to |
.null_ref_cells |
( |
A function suitable for use in analyze()
with element selection, reformatting, and relabeling
performed automatically.
Setting .ungroup_stats
to non-NULL
changes the structure of the value(s) returned by fun
, rather than
just labeling (.labels
), formatting (.formats
), and selecting amongst (.stats
) them. This means that
subsequent make_afun
calls to customize the output further both can and must operate on the new structure,
not the original structure returned by fun
. See the final pair of examples below.
s_summary <- function(x) { stopifnot(is.numeric(x)) list( n = sum(!is.na(x)), mean_sd = c(mean = mean(x), sd = sd(x)), min_max = range(x) ) } s_summary(iris$Sepal.Length) a_summary <- make_afun( fun = s_summary, .formats = c(n = "xx", mean_sd = "xx.xx (xx.xx)", min_max = "xx.xx - xx.xx"), .labels = c(n = "n", mean_sd = "Mean (sd)", min_max = "min - max") ) a_summary(x = iris$Sepal.Length) a_summary2 <- make_afun(a_summary, .stats = c("n", "mean_sd")) a_summary2(x = iris$Sepal.Length) a_summary3 <- make_afun(a_summary, .formats = c(mean_sd = "(xx.xxx, xx.xxx)")) s_foo <- function(df, .N_col, a = 1, b = 2) { list( nrow_df = nrow(df), .N_col = .N_col, a = a, b = b ) } s_foo(iris, 40) a_foo <- make_afun(s_foo, b = 4, .formats = c(nrow_df = "xx.xx", ".N_col" = "xx.", a = "xx", b = "xx.x"), .labels = c( nrow_df = "Nrow df", ".N_col" = "n in cols", a = "a value", b = "b value" ), .indent_mods = c(nrow_df = 2L, a = 1L) ) a_foo(iris, .N_col = 40) a_foo2 <- make_afun(a_foo, .labels = c(nrow_df = "Number of Rows")) a_foo2(iris, .N_col = 40) # grouping and further customization s_grp <- function(df, .N_col, a = 1, b = 2) { list( nrow_df = nrow(df), .N_col = .N_col, letters = list( a = a, b = b ) ) } a_grp <- make_afun(s_grp, b = 3, .labels = c( nrow_df = "row count", .N_col = "count in column" ), .formats = c(nrow_df = "xx.", .N_col = "xx."), .indent_mods = c(letters = 1L), .ungroup_stats = "letters" ) a_grp(iris, 40) a_aftergrp <- make_afun(a_grp, .stats = c("nrow_df", "b"), .formats = c(b = "xx.") ) a_aftergrp(iris, 40) s_ref <- function(x, .in_ref_col, .ref_group) { list( mean_diff = mean(x) - mean(.ref_group) ) } a_ref <- make_afun(s_ref, .labels = c(mean_diff = "Mean Difference from Ref") ) a_ref(iris$Sepal.Length, .in_ref_col = TRUE, 1:10) a_ref(iris$Sepal.Length, .in_ref_col = FALSE, 1:10)
s_summary <- function(x) { stopifnot(is.numeric(x)) list( n = sum(!is.na(x)), mean_sd = c(mean = mean(x), sd = sd(x)), min_max = range(x) ) } s_summary(iris$Sepal.Length) a_summary <- make_afun( fun = s_summary, .formats = c(n = "xx", mean_sd = "xx.xx (xx.xx)", min_max = "xx.xx - xx.xx"), .labels = c(n = "n", mean_sd = "Mean (sd)", min_max = "min - max") ) a_summary(x = iris$Sepal.Length) a_summary2 <- make_afun(a_summary, .stats = c("n", "mean_sd")) a_summary2(x = iris$Sepal.Length) a_summary3 <- make_afun(a_summary, .formats = c(mean_sd = "(xx.xxx, xx.xxx)")) s_foo <- function(df, .N_col, a = 1, b = 2) { list( nrow_df = nrow(df), .N_col = .N_col, a = a, b = b ) } s_foo(iris, 40) a_foo <- make_afun(s_foo, b = 4, .formats = c(nrow_df = "xx.xx", ".N_col" = "xx.", a = "xx", b = "xx.x"), .labels = c( nrow_df = "Nrow df", ".N_col" = "n in cols", a = "a value", b = "b value" ), .indent_mods = c(nrow_df = 2L, a = 1L) ) a_foo(iris, .N_col = 40) a_foo2 <- make_afun(a_foo, .labels = c(nrow_df = "Number of Rows")) a_foo2(iris, .N_col = 40) # grouping and further customization s_grp <- function(df, .N_col, a = 1, b = 2) { list( nrow_df = nrow(df), .N_col = .N_col, letters = list( a = a, b = b ) ) } a_grp <- make_afun(s_grp, b = 3, .labels = c( nrow_df = "row count", .N_col = "count in column" ), .formats = c(nrow_df = "xx.", .N_col = "xx."), .indent_mods = c(letters = 1L), .ungroup_stats = "letters" ) a_grp(iris, 40) a_aftergrp <- make_afun(a_grp, .stats = c("nrow_df", "b"), .formats = c(b = "xx.") ) a_aftergrp(iris, 40) s_ref <- function(x, .in_ref_col, .ref_group) { list( mean_diff = mean(x) - mean(.ref_group) ) } a_ref <- make_afun(s_ref, .labels = c(mean_diff = "Mean Difference from Ref") ) a_ref(iris$Sepal.Length, .in_ref_col = TRUE, 1:10) a_ref(iris$Sepal.Length, .in_ref_col = FALSE, 1:10)
Used for pagination. Generate a structural summary of the columns of an rtables
table and return it as a
data.frame
.
make_col_df( tt, colwidths = NULL, visible_only = TRUE, na_str = "", ccount_format = colcount_format(tt) %||% "(N=xx)" )
make_col_df( tt, colwidths = NULL, visible_only = TRUE, na_str = "", ccount_format = colcount_format(tt) %||% "(N=xx)" )
tt |
( |
colwidths |
( |
visible_only |
( |
na_str |
( |
ccount_format |
( |
Create a custom splitting function
make_split_fun(pre = list(), core_split = NULL, post = list())
make_split_fun(pre = list(), core_split = NULL, post = list())
pre |
( |
core_split |
( |
post |
( |
Custom split functions can be thought of as (up to) 3 different types of manipulations of the splitting process:
Pre-processing of the incoming data to be split.
(Row-splitting only) Customization of the core mapping of incoming data to facets.
Post-processing operations on the set of facets (groups) generated by the split.
This function provides an interface to create custom split functions by implementing and specifying sets of operations in each of those classes of customization independently.
Pre-processing functions (1), must accept: df
, spl
, vals
, and labels
, and can optionally accept
.spl_context
. They then manipulate df
(the incoming data for the split) and return a modified data frame.
This modified data frame must contain all columns present in the incoming data frame, but can add columns if
necessary (though we note that these new columns cannot be used in the layout as split or analysis variables,
because they will not be present when validity checking is done).
The preprocessing component is useful for things such as manipulating factor levels, e.g., to trim unobserved ones or to reorder levels based on observed counts, etc.
Core splitting functions override the fundamental
splitting procedure, and are only necessary in rare cases. These
must accept spl
, df
, vals
, labels
, and can optionally
accept .spl_context
. They should return a split result object
constructed via make_split_result()
.
In particular, if the custom split function will be used in
column space, subsetting expressions (e.g., as returned by
quote()
or bquote
must be provided, while they are
optional (and largely ignored, currently) in row space.
Post-processing functions (3) must accept the result of the core split as their first argument (which can be
anything), in addition to spl
, and fulldf
, and can optionally accept .spl_context
. They must each return a
modified version of the same structure specified above for core splitting.
In both the pre- and post-processing cases, multiple functions can be specified. When this happens, they are applied
sequentially, in the order they appear in the list passed to the relevant argument (pre
and post
, respectively).
A custom function that can be used as a split function.
custom_split_funs for a more detailed discussion on what custom split functions do.
Other make_custom_split:
add_combo_facet()
,
drop_facet_levels()
,
make_split_result()
,
trim_levels_in_facets()
mysplitfun <- make_split_fun( pre = list(drop_facet_levels), post = list(add_overall_facet("ALL", "All Arms")) ) basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplitfun) %>% analyze("AGE") %>% build_table(subset(DM, ARM %in% c("B: Placebo", "C: Combination"))) ## post (and pre) arguments can take multiple functions, here ## we add an overall facet and the reorder the facets reorder_facets <- function(splret, spl, fulldf, ...) { ord <- order(names(splret$values)) make_split_result( splret$values[ord], splret$datasplit[ord], splret$labels[ord] ) } mysplitfun2 <- make_split_fun( pre = list(drop_facet_levels), post = list( add_overall_facet("ALL", "All Arms"), reorder_facets ) ) basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplitfun2) %>% analyze("AGE") %>% build_table(subset(DM, ARM %in% c("B: Placebo", "C: Combination"))) very_stupid_core <- function(spl, df, vals, labels, .spl_context) { make_split_result(c("stupid", "silly"), datasplit = list(df[1:10, ], df[11:30, ]), labels = c("first 10", "second 20") ) } dumb_30_facet <- add_combo_facet("dumb", label = "thirty patients", levels = c("stupid", "silly") ) nonsense_splfun <- make_split_fun( core_split = very_stupid_core, post = list(dumb_30_facet) ) ## recall core split overriding is not supported in column space ## currently, but we can see it in action in row space lyt_silly <- basic_table() %>% split_rows_by("ARM", split_fun = nonsense_splfun) %>% summarize_row_groups() %>% analyze("AGE") silly_table <- build_table(lyt_silly, DM) silly_table
mysplitfun <- make_split_fun( pre = list(drop_facet_levels), post = list(add_overall_facet("ALL", "All Arms")) ) basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplitfun) %>% analyze("AGE") %>% build_table(subset(DM, ARM %in% c("B: Placebo", "C: Combination"))) ## post (and pre) arguments can take multiple functions, here ## we add an overall facet and the reorder the facets reorder_facets <- function(splret, spl, fulldf, ...) { ord <- order(names(splret$values)) make_split_result( splret$values[ord], splret$datasplit[ord], splret$labels[ord] ) } mysplitfun2 <- make_split_fun( pre = list(drop_facet_levels), post = list( add_overall_facet("ALL", "All Arms"), reorder_facets ) ) basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = mysplitfun2) %>% analyze("AGE") %>% build_table(subset(DM, ARM %in% c("B: Placebo", "C: Combination"))) very_stupid_core <- function(spl, df, vals, labels, .spl_context) { make_split_result(c("stupid", "silly"), datasplit = list(df[1:10, ], df[11:30, ]), labels = c("first 10", "second 20") ) } dumb_30_facet <- add_combo_facet("dumb", label = "thirty patients", levels = c("stupid", "silly") ) nonsense_splfun <- make_split_fun( core_split = very_stupid_core, post = list(dumb_30_facet) ) ## recall core split overriding is not supported in column space ## currently, but we can see it in action in row space lyt_silly <- basic_table() %>% split_rows_by("ARM", split_fun = nonsense_splfun) %>% summarize_row_groups() %>% analyze("AGE") silly_table <- build_table(lyt_silly, DM) silly_table
These functions can be used to create or add to a split result in functions which implement core splitting or post-processing within a custom split function.
make_split_result( values, datasplit, labels, extras = NULL, subset_exprs = vector("list", length(values)) ) add_to_split_result( splres, values, datasplit, labels, extras = NULL, subset_exprs = NULL )
make_split_result( values, datasplit, labels, extras = NULL, subset_exprs = vector("list", length(values)) ) add_to_split_result( splres, values, datasplit, labels, extras = NULL, subset_exprs = NULL )
values |
( |
datasplit |
( |
labels |
( |
extras |
( |
subset_exprs |
( |
splres |
( |
These functions performs various housekeeping tasks to ensure that the split result list is as the rtables internals expect it, most of which are not relevant to end users.
A named list representing the facets generated by the split with elements values
, datasplit
, and
labels
, which are the same length and correspond to each other element-wise.
Other make_custom_split:
add_combo_facet()
,
drop_facet_levels()
,
make_split_fun()
,
trim_levels_in_facets()
Other make_custom_split:
add_combo_facet()
,
drop_facet_levels()
,
make_split_fun()
,
trim_levels_in_facets()
splres <- make_split_result( values = c("hi", "lo"), datasplit = list(hi = mtcars, lo = mtcars[1:10, ]), labels = c("more data", "less data"), subset_exprs = list(expression(TRUE), expression(seq_along(wt) <= 10)) ) splres2 <- add_to_split_result(splres, values = "med", datasplit = list(med = mtcars[1:20, ]), labels = "kinda some data", subset_exprs = quote(seq_along(wt) <= 20) )
splres <- make_split_result( values = c("hi", "lo"), datasplit = list(hi = mtcars, lo = mtcars[1:10, ]), labels = c("more data", "less data"), subset_exprs = list(expression(TRUE), expression(seq_along(wt) <= 10)) ) splres2 <- add_to_split_result(splres, values = "med", datasplit = list(med = mtcars[1:20, ]), labels = "kinda some data", subset_exprs = quote(seq_along(wt) <= 20) )
Manual column declaration
manual_cols(..., .lst = list(...), ccount_format = NULL)
manual_cols(..., .lst = list(...), ccount_format = NULL)
... |
one or more vectors of levels to appear in the column space. If more than one set of levels is given, the values of the second are nested within each value of the first, and so on. |
.lst |
( |
ccount_format |
( |
An InstantiatedColumnInfo
object, suitable for declaring the column structure for a manually constructed
table.
Gabriel Becker
# simple one level column space rows <- lapply(1:5, function(i) { DataRow(rep(i, times = 3)) }) tbl <- TableTree(kids = rows, cinfo = manual_cols(split = c("a", "b", "c"))) tbl # manually declared nesting tbl2 <- TableTree( kids = list(DataRow(as.list(1:4))), cinfo = manual_cols( Arm = c("Arm A", "Arm B"), Gender = c("M", "F") ) ) tbl2
# simple one level column space rows <- lapply(1:5, function(i) { DataRow(rep(i, times = 3)) }) tbl <- TableTree(kids = rows, cinfo = manual_cols(split = c("a", "b", "c"))) tbl # manually declared nesting tbl2 <- TableTree( kids = list(DataRow(as.list(1:4))), cinfo = manual_cols( Arm = c("Arm A", "Arm B"), Gender = c("M", "F") ) ) tbl2
Manually defined split
ManualSplit( levels, label, name = "manual", extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", page_prefix = NA_character_, section_div = NA_character_ )
ManualSplit( levels, label, name = "manual", extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", page_prefix = NA_character_, section_div = NA_character_ )
levels |
( |
label |
( |
name |
( |
extra_args |
( |
indent_mod |
( |
cindent_mod |
( |
cvar |
( |
cextra_args |
( |
label_pos |
( |
page_prefix |
( |
section_div |
( |
A ManualSplit
object.
Gabriel Becker
rtable
to a list of matrices which can be used for outputtingAlthough rtables
are represented as a tree data structure when outputting the table to ASCII or HTML
it is useful to map the rtable
to an in-between state with the formatted cells in a matrix form.
## S4 method for signature 'VTableTree' matrix_form( obj, indent_rownames = FALSE, expand_newlines = TRUE, indent_size = 2, fontspec = NULL, col_gap = 3L )
## S4 method for signature 'VTableTree' matrix_form( obj, indent_rownames = FALSE, expand_newlines = TRUE, indent_size = 2, fontspec = NULL, col_gap = 3L )
obj |
( |
indent_rownames |
( |
expand_newlines |
( |
indent_size |
( |
fontspec |
( |
col_gap |
( |
The strings in the return object are defined as follows: row labels are those determined by make_row_df
and cell
values are determined using get_formatted_cells
. (Column labels are calculated using a non-exported internal
function.
A list with the following elements:
strings
The content, as it should be printed, of the top-left material, column headers, row labels,
and cell values of tt
.
spans
The column-span information for each print-string in the strings
matrix.
aligns
The text alignment for each print-string in the strings
matrix.
display
Whether each print-string in the strings matrix should be printed.
row_info
The data.frame
generated by make_row_df
.
With an additional nrow_header
attribute indicating the number of pseudo "rows" that the column structure defines.
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt tbl <- build_table(lyt, iris2) matrix_form(tbl)
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt tbl <- build_table(lyt, iris2) matrix_form(tbl)
Split between two or more different variables
MultiVarSplit( vars, split_label = "", varlabels = NULL, varnames = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = "multivars", child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", split_fun = NULL, page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL )
MultiVarSplit( vars, split_label = "", varlabels = NULL, varnames = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = "multivars", child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", split_fun = NULL, page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL )
vars |
( |
split_label |
( |
varlabels |
( |
varnames |
( |
cfun |
( |
cformat |
( |
cna_str |
( |
split_format |
( |
split_na_str |
( |
split_name |
( |
child_labels |
( |
extra_args |
( |
indent_mod |
( |
cindent_mod |
( |
cvar |
( |
cextra_args |
( |
label_pos |
( |
split_fun |
( |
page_prefix |
( |
section_div |
( |
show_colcounts |
( |
colcount_format |
( |
A MultiVarSplit
object.
Gabriel Becker
TableTree
Names of a TableTree
## S4 method for signature 'VTableNodeInfo' names(x) ## S4 method for signature 'InstantiatedColumnInfo' names(x) ## S4 method for signature 'LayoutColTree' names(x) ## S4 method for signature 'VTableTree' row.names(x)
## S4 method for signature 'VTableNodeInfo' names(x) ## S4 method for signature 'InstantiatedColumnInfo' names(x) ## S4 method for signature 'LayoutColTree' names(x) ## S4 method for signature 'VTableTree' row.names(x)
x |
( |
For TableTree
s with more than one level of splitting in columns, the names are defined to be the top-level
split values repped out across the columns that they span.
The column names of x
, as defined in the details above.
tern
Does the table
/row
/InstantiatedColumnInfo
object contain no column structure information?
no_colinfo(obj) ## S4 method for signature 'VTableNodeInfo' no_colinfo(obj) ## S4 method for signature 'InstantiatedColumnInfo' no_colinfo(obj)
no_colinfo(obj) ## S4 method for signature 'VTableNodeInfo' no_colinfo(obj) ## S4 method for signature 'InstantiatedColumnInfo' no_colinfo(obj)
obj |
( |
TRUE
if the object has no/empty instantiated column information, FALSE
otherwise.
Table dimensions
## S4 method for signature 'VTableTree' nrow(x) ## S4 method for signature 'VTableNodeInfo' ncol(x) ## S4 method for signature 'VTableNodeInfo' dim(x)
## S4 method for signature 'VTableTree' nrow(x) ## S4 method for signature 'VTableNodeInfo' ncol(x) ## S4 method for signature 'VTableNodeInfo' dim(x)
x |
( |
The number of rows (nrow
), columns (ncol
), or both (dim
) of the object.
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("SEX", "AGE")) tbl <- build_table(lyt, ex_adsl) dim(tbl) nrow(tbl) ncol(tbl) NROW(tbl) NCOL(tbl)
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("SEX", "AGE")) tbl <- build_table(lyt, ex_adsl) dim(tbl) nrow(tbl) ncol(tbl) NROW(tbl) NCOL(tbl)
Row attribute accessors
obj_avar(obj) ## S4 method for signature 'TableRow' obj_avar(obj) ## S4 method for signature 'ElementaryTable' obj_avar(obj) row_cells(obj) ## S4 method for signature 'TableRow' row_cells(obj) row_cells(obj) <- value ## S4 replacement method for signature 'TableRow' row_cells(obj) <- value row_values(obj) ## S4 method for signature 'TableRow' row_values(obj) row_values(obj) <- value ## S4 replacement method for signature 'TableRow' row_values(obj) <- value ## S4 replacement method for signature 'LabelRow' row_values(obj) <- value
obj_avar(obj) ## S4 method for signature 'TableRow' obj_avar(obj) ## S4 method for signature 'ElementaryTable' obj_avar(obj) row_cells(obj) ## S4 method for signature 'TableRow' row_cells(obj) row_cells(obj) <- value ## S4 replacement method for signature 'TableRow' row_cells(obj) <- value row_values(obj) ## S4 method for signature 'TableRow' row_values(obj) row_values(obj) <- value ## S4 replacement method for signature 'TableRow' row_values(obj) <- value ## S4 replacement method for signature 'LabelRow' row_values(obj) <- value
obj |
( |
value |
( |
Various return values depending on the accessor called.
formatters
packageSee the formatters
documentation for descriptions of these generics.
## S4 method for signature 'VNodeInfo' obj_name(obj) ## S4 method for signature 'Split' obj_name(obj) ## S4 replacement method for signature 'VNodeInfo' obj_name(obj) <- value ## S4 replacement method for signature 'Split' obj_name(obj) <- value ## S4 method for signature 'Split' obj_label(obj) ## S4 method for signature 'TableRow' obj_label(obj) ## S4 method for signature 'VTableTree' obj_label(obj) ## S4 method for signature 'ValueWrapper' obj_label(obj) ## S4 replacement method for signature 'Split' obj_label(obj) <- value ## S4 replacement method for signature 'TableRow' obj_label(obj) <- value ## S4 replacement method for signature 'ValueWrapper' obj_label(obj) <- value ## S4 replacement method for signature 'VTableTree' obj_label(obj) <- value ## S4 method for signature 'VTableNodeInfo' obj_format(obj) ## S4 method for signature 'CellValue' obj_format(obj) ## S4 method for signature 'Split' obj_format(obj) ## S4 replacement method for signature 'VTableNodeInfo' obj_format(obj) <- value ## S4 replacement method for signature 'Split' obj_format(obj) <- value ## S4 replacement method for signature 'CellValue' obj_format(obj) <- value ## S4 method for signature 'Split' obj_na_str(obj) ## S4 method for signature 'VTitleFooter' main_title(obj) ## S4 replacement method for signature 'VTitleFooter' main_title(obj) <- value ## S4 method for signature 'TableRow' main_title(obj) ## S4 method for signature 'VTitleFooter' subtitles(obj) ## S4 replacement method for signature 'VTitleFooter' subtitles(obj) <- value ## S4 method for signature 'TableRow' subtitles(obj) ## S4 method for signature 'VTitleFooter' main_footer(obj) ## S4 replacement method for signature 'VTitleFooter' main_footer(obj) <- value ## S4 method for signature 'TableRow' main_footer(obj) ## S4 method for signature 'VTitleFooter' prov_footer(obj) ## S4 replacement method for signature 'VTitleFooter' prov_footer(obj) <- value ## S4 method for signature 'TableRow' prov_footer(obj) ## S4 method for signature 'VTableNodeInfo' table_inset(obj) ## S4 method for signature 'PreDataTableLayouts' table_inset(obj) ## S4 replacement method for signature 'VTableNodeInfo' table_inset(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' table_inset(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' table_inset(obj) <- value ## S4 method for signature 'TableRow' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) ## S4 method for signature 'LabelRow' nlines( x, colwidths = NULL, max_width = NULL, fontspec = fontspec, col_gap = NULL ) ## S4 method for signature 'RefFootnote' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = NULL) ## S4 method for signature 'InstantiatedColumnInfo' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) ## S4 method for signature 'VTableTree' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = character(), incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = NULL, col_gap = 3 ) ## S4 method for signature 'TableRow' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = "root", incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = font_spec(), col_gap = 3 ) ## S4 method for signature 'LabelRow' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = "root", incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = font_spec(), col_gap = 3 )
## S4 method for signature 'VNodeInfo' obj_name(obj) ## S4 method for signature 'Split' obj_name(obj) ## S4 replacement method for signature 'VNodeInfo' obj_name(obj) <- value ## S4 replacement method for signature 'Split' obj_name(obj) <- value ## S4 method for signature 'Split' obj_label(obj) ## S4 method for signature 'TableRow' obj_label(obj) ## S4 method for signature 'VTableTree' obj_label(obj) ## S4 method for signature 'ValueWrapper' obj_label(obj) ## S4 replacement method for signature 'Split' obj_label(obj) <- value ## S4 replacement method for signature 'TableRow' obj_label(obj) <- value ## S4 replacement method for signature 'ValueWrapper' obj_label(obj) <- value ## S4 replacement method for signature 'VTableTree' obj_label(obj) <- value ## S4 method for signature 'VTableNodeInfo' obj_format(obj) ## S4 method for signature 'CellValue' obj_format(obj) ## S4 method for signature 'Split' obj_format(obj) ## S4 replacement method for signature 'VTableNodeInfo' obj_format(obj) <- value ## S4 replacement method for signature 'Split' obj_format(obj) <- value ## S4 replacement method for signature 'CellValue' obj_format(obj) <- value ## S4 method for signature 'Split' obj_na_str(obj) ## S4 method for signature 'VTitleFooter' main_title(obj) ## S4 replacement method for signature 'VTitleFooter' main_title(obj) <- value ## S4 method for signature 'TableRow' main_title(obj) ## S4 method for signature 'VTitleFooter' subtitles(obj) ## S4 replacement method for signature 'VTitleFooter' subtitles(obj) <- value ## S4 method for signature 'TableRow' subtitles(obj) ## S4 method for signature 'VTitleFooter' main_footer(obj) ## S4 replacement method for signature 'VTitleFooter' main_footer(obj) <- value ## S4 method for signature 'TableRow' main_footer(obj) ## S4 method for signature 'VTitleFooter' prov_footer(obj) ## S4 replacement method for signature 'VTitleFooter' prov_footer(obj) <- value ## S4 method for signature 'TableRow' prov_footer(obj) ## S4 method for signature 'VTableNodeInfo' table_inset(obj) ## S4 method for signature 'PreDataTableLayouts' table_inset(obj) ## S4 replacement method for signature 'VTableNodeInfo' table_inset(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' table_inset(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' table_inset(obj) <- value ## S4 method for signature 'TableRow' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) ## S4 method for signature 'LabelRow' nlines( x, colwidths = NULL, max_width = NULL, fontspec = fontspec, col_gap = NULL ) ## S4 method for signature 'RefFootnote' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = NULL) ## S4 method for signature 'InstantiatedColumnInfo' nlines(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) ## S4 method for signature 'VTableTree' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = character(), incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = NULL, col_gap = 3 ) ## S4 method for signature 'TableRow' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = "root", incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = font_spec(), col_gap = 3 ) ## S4 method for signature 'LabelRow' make_row_df( tt, colwidths = NULL, visible_only = TRUE, rownum = 0, indent = 0L, path = "root", incontent = FALSE, repr_ext = 0L, repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, max_width = NULL, fontspec = font_spec(), col_gap = 3 )
obj |
( |
value |
( |
x |
( |
colwidths |
( |
max_width |
( |
fontspec |
( |
col_gap |
( |
tt |
( |
visible_only |
( |
rownum |
( |
indent |
( |
path |
( |
incontent |
( |
repr_ext |
( |
repr_inds |
( |
sibpos |
( |
nsibs |
( |
When visible_only
is TRUE
(the default), methods should return a data.frame
with exactly one
row per visible row in the table-like object. This is useful when reasoning about how a table will
print, but does not reflect the full pathing space of the structure (though the paths which are given
will all work as is).
If supported, when visible_only
is FALSE
, every structural element of the table (in row-space)
will be reflected in the returned data.frame
, meaning the full pathing-space will be represented
but some rows in the layout summary will not represent printed rows in the table as it is displayed.
Most arguments beyond tt
and visible_only
are present so that make_row_df
methods can call
make_row_df
recursively and retain information, and should not be set during a top-level call.
Accessor functions return the current value of the component being accessed of obj
Setter functions return a modified copy of obj
with the new value.
The technically present root tree node is excluded from the summary returned by
both make_row_df
and make_col_df
(see relevant functions inrtables
), as it is the
row/column structure of tt
and thus not useful for pathing or pagination.
# Expected error with matrix_form. For real case examples consult {rtables} documentation mf <- basic_matrix_form(iris) # make_row_df(mf) # Use table obj instead
# Expected error with matrix_form. For real case examples consult {rtables} documentation mf <- basic_matrix_form(iris) # make_row_df(mf) # Use table obj instead
TableTree
Paginate an rtables
table in the vertical and/or horizontal direction, as required for the specified page size.
pag_tt_indices( tt, lpp = 15, min_siblings = 2, nosplitin = character(), colwidths = NULL, max_width = NULL, fontspec = NULL, col_gap = 3, verbose = FALSE ) paginate_table( tt, page_type = "letter", font_family = "Courier", font_size = 8, lineheight = 1, landscape = FALSE, pg_width = NULL, pg_height = NULL, margins = c(top = 0.5, bottom = 0.5, left = 0.75, right = 0.75), lpp = NA_integer_, cpp = NA_integer_, min_siblings = 2, nosplitin = character(), colwidths = NULL, tf_wrap = FALSE, max_width = NULL, fontspec = font_spec(font_family, font_size, lineheight), col_gap = 3, verbose = FALSE )
pag_tt_indices( tt, lpp = 15, min_siblings = 2, nosplitin = character(), colwidths = NULL, max_width = NULL, fontspec = NULL, col_gap = 3, verbose = FALSE ) paginate_table( tt, page_type = "letter", font_family = "Courier", font_size = 8, lineheight = 1, landscape = FALSE, pg_width = NULL, pg_height = NULL, margins = c(top = 0.5, bottom = 0.5, left = 0.75, right = 0.75), lpp = NA_integer_, cpp = NA_integer_, min_siblings = 2, nosplitin = character(), colwidths = NULL, tf_wrap = FALSE, max_width = NULL, fontspec = font_spec(font_family, font_size, lineheight), col_gap = 3, verbose = FALSE )
tt |
( |
lpp |
( |
min_siblings |
( |
nosplitin |
( |
colwidths |
( |
max_width |
( |
fontspec |
( |
col_gap |
( |
verbose |
( |
page_type |
( |
font_family |
( |
font_size |
( |
lineheight |
( |
landscape |
( |
pg_width |
( |
pg_height |
( |
margins |
( |
cpp |
( |
tf_wrap |
( |
rtables
pagination is context aware, meaning that label rows and row-group summaries (content rows) are repeated
after (vertical) pagination, as appropriate. This allows the reader to immediately understand where they are in the
table after turning to a new page, but does also mean that a rendered, paginated table will take up more lines of
text than rendering the table without pagination would.
Pagination also takes into account word-wrapping of title, footer, column-label, and formatted cell value content.
Vertical pagination information (pagination data.frame
) is created using (make_row_df
).
Horizontal pagination is performed by creating a pagination data frame for the columns, and then applying the same algorithm used for vertical pagination to it.
If physical page size and font information are specified, these are used to derive lines-per-page (lpp
) and
characters-per-page (cpp
) values.
The full multi-direction pagination algorithm then is as follows:
Adjust lpp
and cpp
to account for rendered elements that are not rows (columns):
titles/footers/column labels, and horizontal dividers in the vertical pagination case
row-labels, table_inset, and top-left materials in the horizontal case
Perform 'forced pagination' representing page-by row splits, generating 1 or more tables.
Perform vertical pagination separately on each table generated in (1).
Perform horizontal pagination on the entire table and apply the results to each table page generated in (1)-(2).
Return a list of subtables representing full bi-directional pagination.
Pagination in both directions is done using the Core Pagination Algorithm implemented in the formatters
package:
pag_tt_indices
returns a list of paginated-groups of row-indices of tt
.
paginate_table
returns the subtables defined by subsetting by the indices defined by pag_tt_indices
.
Pagination is performed independently in the vertical and horizontal directions based solely on a pagination data frame, which includes the following information for each row/column:
Number of lines/characters rendering the row will take after
word-wrapping (self_extent
)
The indices (reprint_inds
) and number of lines (par_extent
)
of the rows which act as context for the row
The row's number of siblings and position within its siblings
Given lpp
(cpp
) is already adjusted for rendered elements which
are not rows/columns and a data frame of pagination information,
pagination is performed via the following algorithm with start = 1
.
Core Pagination Algorithm:
Initial guess for pagination position is start + lpp
(start + cpp
)
While the guess is not a valid pagination position, and guess > start
,
decrement guess and repeat.
An error is thrown if all possible pagination positions between
start
and start + lpp
(start + cpp
) would be < start
after decrementing
Retain pagination index
If pagination point was less than NROW(tt)
(ncol(tt)
), set
start
to pos + 1
, and repeat steps (1) - (4).
Validating Pagination Position:
Given an (already adjusted) lpp
or cpp
value, a pagination is invalid if:
The rows/columns on the page would take more than (adjusted) lpp
lines/cpp
characters to render including:
word-wrapping
(vertical only) context repetition
(vertical only) footnote messages and/or section divider lines take up too many lines after rendering rows
(vertical only) row is a label or content (row-group summary) row
(vertical only) row at the pagination point has siblings, and
it has less than min_siblings
preceding or following siblings
pagination would occur within a sub-table listed in nosplitin
s_summary <- function(x) { if (is.numeric(x)) { in_rows( "n" = rcell(sum(!is.na(x)), format = "xx"), "Mean (sd)" = rcell(c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE)), format = "xx.xx (xx.xx)" ), "IQR" = rcell(IQR(x, na.rm = TRUE), format = "xx.xx"), "min - max" = rcell(range(x, na.rm = TRUE), format = "xx.xx - xx.xx") ) } else if (is.factor(x)) { vs <- as.list(table(x)) do.call(in_rows, lapply(vs, rcell, format = "xx")) } else { ( stop("type not supported") ) } } lyt <- basic_table() %>% split_cols_by(var = "ARM") %>% analyze(c("AGE", "SEX", "BEP01FL", "BMRKR1", "BMRKR2", "COUNTRY"), afun = s_summary) tbl <- build_table(lyt, ex_adsl) tbl nrow(tbl) row_paths_summary(tbl) tbls <- paginate_table(tbl, lpp = 15) mf <- matrix_form(tbl, indent_rownames = TRUE) w_tbls <- propose_column_widths(mf) # so that we have the same column widths tmp <- lapply(tbls, function(tbli) { cat(toString(tbli, widths = w_tbls)) cat("\n\n") cat("~~~~ PAGE BREAK ~~~~") cat("\n\n") })
s_summary <- function(x) { if (is.numeric(x)) { in_rows( "n" = rcell(sum(!is.na(x)), format = "xx"), "Mean (sd)" = rcell(c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE)), format = "xx.xx (xx.xx)" ), "IQR" = rcell(IQR(x, na.rm = TRUE), format = "xx.xx"), "min - max" = rcell(range(x, na.rm = TRUE), format = "xx.xx - xx.xx") ) } else if (is.factor(x)) { vs <- as.list(table(x)) do.call(in_rows, lapply(vs, rcell, format = "xx")) } else { ( stop("type not supported") ) } } lyt <- basic_table() %>% split_cols_by(var = "ARM") %>% analyze(c("AGE", "SEX", "BEP01FL", "BMRKR1", "BMRKR2", "COUNTRY"), afun = s_summary) tbl <- build_table(lyt, ex_adsl) tbl nrow(tbl) row_paths_summary(tbl) tbls <- paginate_table(tbl, lpp = 15) mf <- matrix_form(tbl, indent_rownames = TRUE) w_tbls <- propose_column_widths(mf) # so that we have the same column widths tmp <- lapply(tbls, function(tbli) { cat(toString(tbli, widths = w_tbls)) cat("\n\n") cat("~~~~ PAGE BREAK ~~~~") cat("\n\n") })
TableTree
Recursively prune a TableTree
prune_table( tt, prune_func = prune_empty_level, stop_depth = NA_real_, depth = 0 )
prune_table( tt, prune_func = prune_empty_level, stop_depth = NA_real_, depth = 0 )
tt |
( |
prune_func |
( |
stop_depth |
( |
depth |
( |
A TableTree
pruned via recursive application of prune_func
.
prune_empty_level()
for details on this and several other basic pruning functions included
in the rtables
package.
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") tbl_to_prune <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_prune %>% prune_table()
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") tbl_to_prune <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_prune %>% prune_table()
This function provides a convenience interface for generating generalizations of a 2-way frequency table. Row and column space can be facetted by variables, and an analysis function can be specified. The function then builds a layout with the specified layout and applies it to the data provided.
qtable_layout( data, row_vars = character(), col_vars = character(), avar = NULL, row_labels = NULL, afun = NULL, summarize_groups = FALSE, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = TRUE, drop_levels = TRUE, ..., .default_rlabel = NULL ) qtable( data, row_vars = character(), col_vars = character(), avar = NULL, row_labels = NULL, afun = NULL, summarize_groups = FALSE, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = TRUE, drop_levels = TRUE, ... )
qtable_layout( data, row_vars = character(), col_vars = character(), avar = NULL, row_labels = NULL, afun = NULL, summarize_groups = FALSE, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = TRUE, drop_levels = TRUE, ..., .default_rlabel = NULL ) qtable( data, row_vars = character(), col_vars = character(), avar = NULL, row_labels = NULL, afun = NULL, summarize_groups = FALSE, title = "", subtitles = character(), main_footer = character(), prov_footer = character(), show_colcounts = TRUE, drop_levels = TRUE, ... )
data |
( |
row_vars |
( |
col_vars |
( |
avar |
( |
row_labels |
( |
afun |
( |
summarize_groups |
( |
title |
( |
subtitles |
( |
main_footer |
( |
prov_footer |
( |
show_colcounts |
( |
drop_levels |
( |
... |
additional arguments passed to |
.default_rlabel |
( |
This function creates a table with a single top-level structure in both row and column dimensions involving faceting by 0 or more variables in each dimension.
The display of the table depends on certain details of the tabulation. In the case of an afun
which returns a
single cell's contents (either a scalar or a vector of 2 or 3 elements), the label rows for the deepest-nested row
facets will be hidden and the labels used there will be used as the analysis row labels. In the case of an afun
which returns a list (corresponding to multiple cells), the names of the list will be used as the analysis row
labels and the deepest-nested facet row labels will be visible.
The table will be annotated in the top-left area with an informative label displaying the analysis variable
(avar
), if set, and the function used (captured via substitute) where possible, or 'count' if not. One exception
where the user may directly modify the top-left area (via row_labels
) is the case of a table with row facets and
an afun
which returns a single row.
qtable
returns a built TableTree
object representing the desired table
qtable_layout
returns a PreDataTableLayouts
object declaring the structure of the desired table, suitable for
passing to build_table()
.
qtable(ex_adsl) qtable(ex_adsl, row_vars = "ARM") qtable(ex_adsl, col_vars = "ARM") qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM") qtable(ex_adsl, row_vars = c("COUNTRY", "SEX"), col_vars = c("ARM", "STRATA1")) qtable(ex_adsl, row_vars = c("COUNTRY", "SEX"), col_vars = c("ARM", "STRATA1"), avar = "AGE", afun = mean ) summary_list <- function(x, ...) as.list(summary(x)) qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM", avar = "AGE", afun = summary_list) suppressWarnings(qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM", avar = "AGE", afun = range ))
qtable(ex_adsl) qtable(ex_adsl, row_vars = "ARM") qtable(ex_adsl, col_vars = "ARM") qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM") qtable(ex_adsl, row_vars = c("COUNTRY", "SEX"), col_vars = c("ARM", "STRATA1")) qtable(ex_adsl, row_vars = c("COUNTRY", "SEX"), col_vars = c("ARM", "STRATA1"), avar = "AGE", afun = mean ) summary_list <- function(x, ...) as.list(summary(x)) qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM", avar = "AGE", afun = summary_list) suppressWarnings(qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM", avar = "AGE", afun = range ))
TableTree
and related objectsRow-bind TableTree
and related objects
rbindl_rtables( x, gap = lifecycle::deprecated(), check_headers = lifecycle::deprecated() ) ## S4 method for signature 'VTableNodeInfo' rbind(..., deparse.level = 1) ## S4 method for signature 'VTableNodeInfo,ANY' rbind2(x, y)
rbindl_rtables( x, gap = lifecycle::deprecated(), check_headers = lifecycle::deprecated() ) ## S4 method for signature 'VTableNodeInfo' rbind(..., deparse.level = 1) ## S4 method for signature 'VTableNodeInfo,ANY' rbind2(x, y)
x |
( |
gap |
|
check_headers |
|
... |
( |
deparse.level |
( |
y |
( |
A formal table object.
When objects are row-bound, titles and footer information is retained from the first object (if any exists) if all
other objects have no titles/footers or have identical titles/footers. Otherwise, all titles/footers are removed
and must be set for the bound table via the formatters::main_title()
, formatters::subtitles()
,
formatters::main_footer()
, and formatters::prov_footer()
functions.
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" ) )) rbind(mtbl, mtbl2) rbind(mtbl, rrow(), mtbl2) rbind(mtbl, rrow("aaa"), indent(mtbl2))
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" ) )) rbind(mtbl, mtbl2) rbind(mtbl, rrow(), mtbl2) rbind(mtbl, rrow("aaa"), indent(mtbl2))
Construct a cell value and associate formatting, labeling, indenting, and column spanning information with it.
rcell( x, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, footnotes = NULL, align = NULL, format_na_str = NULL ) non_ref_rcell( x, is_ref, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, refval = NULL, align = "center", format_na_str = NULL )
rcell( x, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, footnotes = NULL, align = NULL, format_na_str = NULL ) non_ref_rcell( x, is_ref, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, refval = NULL, align = "center", format_na_str = NULL )
x |
( |
format |
( |
colspan |
( |
label |
( |
indent_mod |
( |
footnotes |
( |
align |
( |
format_na_str |
( |
is_ref |
( |
refval |
( |
non_ref_rcell
provides the common blank for cells in the reference column, this value otherwise, and should
be passed the value of .in_ref_col
when it is used.
An object representing the value within a single cell within a populated table. The underlying structure of this object is an implementation detail and should not be relied upon beyond calling accessors for the class.
Currently column spanning is only supported for defining header structure.
Create a header
rheader(..., format = "xx", .lst = NULL)
rheader(..., format = "xx", .lst = NULL)
... |
row specifications, either as character vectors or the output from |
format |
( |
.lst |
( |
A InstantiatedColumnInfo
object.
Other compatibility:
rrow()
,
rrowl()
,
rtable()
h1 <- rheader(c("A", "B", "C")) h1 h2 <- rheader( rrow(NULL, rcell("group 1", colspan = 2), rcell("group 2", colspan = 2)), rrow(NULL, "A", "B", "A", "B") ) h2
h1 <- rheader(c("A", "B", "C")) h1 h2 <- rheader( rrow(NULL, rcell("group 1", colspan = 2), rcell("group 2", colspan = 2)), rrow(NULL, "A", "B", "A", "B") ) h2
Set all column counts at all levels of nesting to NA
rm_all_colcounts(obj) ## S4 method for signature 'VTableTree' rm_all_colcounts(obj) ## S4 method for signature 'InstantiatedColumnInfo' rm_all_colcounts(obj) ## S4 method for signature 'LayoutColTree' rm_all_colcounts(obj) ## S4 method for signature 'LayoutColLeaf' rm_all_colcounts(obj)
rm_all_colcounts(obj) ## S4 method for signature 'VTableTree' rm_all_colcounts(obj) ## S4 method for signature 'InstantiatedColumnInfo' rm_all_colcounts(obj) ## S4 method for signature 'LayoutColTree' rm_all_colcounts(obj) ## S4 method for signature 'LayoutColLeaf' rm_all_colcounts(obj)
obj |
( |
obj
with all column counts reset to missing
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) # before col_counts(tbl) tbl <- rm_all_colcounts(tbl) col_counts(tbl)
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) # before col_counts(tbl) tbl <- rm_all_colcounts(tbl) col_counts(tbl)
Access and set the referential footnotes aspects of a built table.
row_footnotes(obj) row_footnotes(obj) <- value cell_footnotes(obj) cell_footnotes(obj) <- value col_fnotes_here(obj) ## S4 method for signature 'ANY' col_fnotes_here(obj) col_fnotes_here(obj) <- value col_footnotes(obj) col_footnotes(obj) <- value ref_index(obj) ref_index(obj) <- value ref_symbol(obj) ref_symbol(obj) <- value ref_msg(obj) fnotes_at_path(obj, rowpath = NULL, colpath = NULL, reset_idx = TRUE) <- value
row_footnotes(obj) row_footnotes(obj) <- value cell_footnotes(obj) cell_footnotes(obj) <- value col_fnotes_here(obj) ## S4 method for signature 'ANY' col_fnotes_here(obj) col_fnotes_here(obj) <- value col_footnotes(obj) col_footnotes(obj) <- value ref_index(obj) ref_index(obj) <- value ref_symbol(obj) ref_symbol(obj) <- value ref_msg(obj) fnotes_at_path(obj, rowpath = NULL, colpath = NULL, reset_idx = TRUE) <- value
obj |
( |
value |
( |
rowpath |
( |
colpath |
( |
reset_idx |
( |
row_paths()
, col_paths()
, row_paths_summary()
, col_paths_summary()
# How to add referencial footnotes after having created a table lyt <- basic_table() %>% split_rows_by("SEX", page_by = TRUE) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl <- trim_rows(tbl) # Check the row and col structure to add precise references # row_paths(tbl) # col_paths(t) # row_paths_summary(tbl) # col_paths_summary(tbl) # Add the citation numbers on the table and relative references in the footnotes fnotes_at_path(tbl, rowpath = c("SEX", "F", "AGE", "Mean")) <- "Famous paper 1" fnotes_at_path(tbl, rowpath = c("SEX", "UNDIFFERENTIATED")) <- "Unfamous paper 2" # tbl
# How to add referencial footnotes after having created a table lyt <- basic_table() %>% split_rows_by("SEX", page_by = TRUE) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl <- trim_rows(tbl) # Check the row and col structure to add precise references # row_paths(tbl) # col_paths(t) # row_paths_summary(tbl) # col_paths_summary(tbl) # Add the citation numbers on the table and relative references in the footnotes fnotes_at_path(tbl, rowpath = c("SEX", "F", "AGE", "Mean")) <- "Famous paper 1" fnotes_at_path(tbl, rowpath = c("SEX", "UNDIFFERENTIATED")) <- "Unfamous paper 2" # tbl
Get a list of table row/column paths
row_paths(x) col_paths(x)
row_paths(x) col_paths(x)
x |
( |
A list of paths to each row/column within x
.
cell_values()
, fnotes_at_path<-
, row_paths_summary()
, col_paths_summary()
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("SEX", "AGE")) tbl <- build_table(lyt, ex_adsl) tbl row_paths(tbl) col_paths(tbl) cell_values(tbl, c("AGE", "Mean"), c("ARM", "B: Placebo"))
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("SEX", "AGE")) tbl <- build_table(lyt, ex_adsl) tbl row_paths(tbl) col_paths(tbl) cell_values(tbl, c("AGE", "Mean"), c("ARM", "B: Placebo"))
Print row/column paths summary
row_paths_summary(x) col_paths_summary(x)
row_paths_summary(x) col_paths_summary(x)
x |
( |
A data frame summarizing the row- or column-structure of x
.
ex_adsl_MF <- ex_adsl %>% dplyr::filter(SEX %in% c("M", "F")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX", split_fun = drop_split_levels) %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl_MF) tbl df <- row_paths_summary(tbl) df col_paths_summary(tbl) # manually constructed table tbl2 <- rtable( rheader( rrow( "row 1", rcell("a", colspan = 2), rcell("b", colspan = 2) ), rrow("h2", "a", "b", "c", "d") ), rrow("r1", 1, 2, 1, 2), rrow("r2", 3, 4, 2, 1) ) col_paths_summary(tbl2)
ex_adsl_MF <- ex_adsl %>% dplyr::filter(SEX %in% c("M", "F")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX", split_fun = drop_split_levels) %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl_MF) tbl df <- row_paths_summary(tbl) df col_paths_summary(tbl) # manually constructed table tbl2 <- rtable( rheader( rrow( "row 1", rcell("a", colspan = 2), rcell("b", colspan = 2) ), rrow("h2", "a", "b", "c", "d") ), rrow("r1", 1, 2, 1, 2), rrow("r2", 3, 4, 2, 1) ) col_paths_summary(tbl2)
rtable
rowCreate an rtable
row
rrow(row.name = "", ..., format = NULL, indent = 0, inset = 0L)
rrow(row.name = "", ..., format = NULL, indent = 0, inset = 0L)
row.name |
( |
... |
cell values. |
format |
( |
indent |
|
inset |
( |
A row object of the context-appropriate type (label or data).
Other compatibility:
rheader()
,
rrowl()
,
rtable()
rrow("ABC", c(1, 2), c(3, 2), format = "xx (xx.%)") rrow("")
rrow("ABC", c(1, 2), c(3, 2), format = "xx (xx.%)") rrow("")
rtable
row from a vector or list of valuesCreate an rtable
row from a vector or list of values
rrowl(row.name, ..., format = NULL, indent = 0, inset = 0L)
rrowl(row.name, ..., format = NULL, indent = 0, inset = 0L)
row.name |
( |
... |
values in vector/list form. |
format |
( |
indent |
|
inset |
( |
A row object of the context-appropriate type (label or data).
Other compatibility:
rheader()
,
rrow()
,
rtable()
rrowl("a", c(1, 2, 3), format = "xx") rrowl("a", c(1, 2, 3), c(4, 5, 6), format = "xx") rrowl("N", table(iris$Species)) rrowl("N", table(iris$Species), format = "xx") x <- tapply(iris$Sepal.Length, iris$Species, mean, simplify = FALSE) rrow(row.name = "row 1", x) rrow("ABC", 2, 3) rrowl(row.name = "row 1", c(1, 2), c(3, 4)) rrow(row.name = "row 2", c(1, 2), c(3, 4))
rrowl("a", c(1, 2, 3), format = "xx") rrowl("a", c(1, 2, 3), c(4, 5, 6), format = "xx") rrowl("N", table(iris$Species)) rrowl("N", table(iris$Species), format = "xx") x <- tapply(iris$Sepal.Length, iris$Species, mean, simplify = FALSE) rrow(row.name = "row 1", x) rrow("ABC", 2, 3) rrowl(row.name = "row 1", c(1, 2), c(3, 4)) rrow(row.name = "row 2", c(1, 2), c(3, 4))
Create a table
rtable(header, ..., format = NULL, hsep = default_hsep(), inset = 0L) rtablel(header, ..., format = NULL, hsep = default_hsep(), inset = 0L)
rtable(header, ..., format = NULL, hsep = default_hsep(), inset = 0L) rtablel(header, ..., format = NULL, hsep = default_hsep(), inset = 0L)
header |
( |
... |
rows to place in the table. |
format |
( |
hsep |
( |
inset |
( |
A formal table object of the appropriate type (ElementaryTable
or TableTree
).
Other compatibility:
rheader()
,
rrow()
,
rrowl()
rtable( header = LETTERS[1:3], rrow("one to three", 1, 2, 3), rrow("more stuff", rcell(pi, format = "xx.xx"), "test", "and more") ) # Table with multirow header sel <- iris$Species == "setosa" 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" ), rrow( row.name = "Setosa", mean(iris$Sepal.Length[sel]), median(iris$Sepal.Length[sel]), mean(iris$Petal.Length[sel]), median(iris$Petal.Length[sel]) ) ) mtbl names(mtbl) # always first row of header # Single row header tbl <- rtable( header = c("Treatement\nN=100", "Comparison\nN=300"), format = "xx (xx.xx%)", rrow("A", c(104, .2), c(100, .4)), rrow("B", c(23, .4), c(43, .5)), rrow(""), rrow("this is a very long section header"), rrow("estimate", rcell(55.23, "xx.xx", colspan = 2)), rrow("95% CI", indent = 1, rcell(c(44.8, 67.4), format = "(xx.x, xx.x)", colspan = 2)) ) tbl row.names(tbl) names(tbl) # Subsetting tbl[1, ] tbl[, 1] tbl[1, 2] tbl[2, 1] tbl[3, 2] tbl[5, 1] tbl[5, 2] # Data Structure methods dim(tbl) nrow(tbl) ncol(tbl) names(tbl) # Colspans tbl2 <- rtable( c("A", "B", "C", "D", "E"), format = "xx", rrow("r1", 1, 2, 3, 4, 5), rrow("r2", rcell("sp2", colspan = 2), "sp1", rcell("sp2-2", colspan = 2)) ) tbl2
rtable( header = LETTERS[1:3], rrow("one to three", 1, 2, 3), rrow("more stuff", rcell(pi, format = "xx.xx"), "test", "and more") ) # Table with multirow header sel <- iris$Species == "setosa" 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" ), rrow( row.name = "Setosa", mean(iris$Sepal.Length[sel]), median(iris$Sepal.Length[sel]), mean(iris$Petal.Length[sel]), median(iris$Petal.Length[sel]) ) ) mtbl names(mtbl) # always first row of header # Single row header tbl <- rtable( header = c("Treatement\nN=100", "Comparison\nN=300"), format = "xx (xx.xx%)", rrow("A", c(104, .2), c(100, .4)), rrow("B", c(23, .4), c(43, .5)), rrow(""), rrow("this is a very long section header"), rrow("estimate", rcell(55.23, "xx.xx", colspan = 2)), rrow("95% CI", indent = 1, rcell(c(44.8, 67.4), format = "(xx.x, xx.x)", colspan = 2)) ) tbl row.names(tbl) names(tbl) # Subsetting tbl[1, ] tbl[, 1] tbl[1, 2] tbl[2, 1] tbl[3, 2] tbl[5, 1] tbl[5, 2] # Data Structure methods dim(tbl) nrow(tbl) ncol(tbl) names(tbl) # Colspans tbl2 <- rtable( c("A", "B", "C", "D", "E"), format = "xx", rrow("r1", 1, 2, 3, 4, 5), rrow("r2", rcell("sp2", colspan = 2), "sp1", rcell("sp2-2", colspan = 2)) ) tbl2
Experimental function to correct structure of degenerate tables by adding messaging rows to empty sub-structures.
sanitize_table_struct(tt, empty_msg = "-- This Section Contains No Data --")
sanitize_table_struct(tt, empty_msg = "-- This Section Contains No Data --")
tt |
( |
empty_msg |
( |
This function locates degenerate portions of the table (including the table overall in the case of a table with no
data rows) and inserts a row which spans all columns with the message empty_msg
at each one, generating a table
guaranteed to be non-degenerate.
If tt
is already valid, it is returned unmodified. If tt
is degenerate, a modified, non-degenerate
version of the table is returned.
Other table structure validation functions:
find_degen_struct()
,
validate_table_struct()
sanitize_table_struct(rtable("cool beans")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() ## Degenerate because it doesn't have any analyze calls -> no data rows badtab <- build_table(lyt, DM) sanitize_table_struct(badtab)
sanitize_table_struct(rtable("cool beans")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() ## Degenerate because it doesn't have any analyze calls -> no data rows badtab <- build_table(lyt, DM) sanitize_table_struct(badtab)
section_div
can be used to set or get the section divider for a table object
produced by build_table()
. When assigned in post-processing (section_div<-
)
the table can have a section divider after every row, each assigned independently.
If assigning during layout creation, only split_rows_by()
(and its related row-wise
splits) and analyze()
have a section_div
parameter that will produce separators
between split sections and data subgroups, respectively.
section_div(obj) ## S4 method for signature 'VTableTree' section_div(obj) ## S4 method for signature 'list' section_div(obj) ## S4 method for signature 'TableRow' section_div(obj) section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'VTableTree' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'list' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'TableRow' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'LabelRow' section_div(obj, only_sep_sections = FALSE) <- value header_section_div(obj) ## S4 method for signature 'PreDataTableLayouts' header_section_div(obj) ## S4 method for signature 'VTableTree' header_section_div(obj) header_section_div(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' header_section_div(obj) <- value ## S4 replacement method for signature 'VTableTree' header_section_div(obj) <- value top_level_section_div(obj) ## S4 method for signature 'PreDataTableLayouts' top_level_section_div(obj) top_level_section_div(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' top_level_section_div(obj) <- value
section_div(obj) ## S4 method for signature 'VTableTree' section_div(obj) ## S4 method for signature 'list' section_div(obj) ## S4 method for signature 'TableRow' section_div(obj) section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'VTableTree' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'list' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'TableRow' section_div(obj, only_sep_sections = FALSE) <- value ## S4 replacement method for signature 'LabelRow' section_div(obj, only_sep_sections = FALSE) <- value header_section_div(obj) ## S4 method for signature 'PreDataTableLayouts' header_section_div(obj) ## S4 method for signature 'VTableTree' header_section_div(obj) header_section_div(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' header_section_div(obj) <- value ## S4 replacement method for signature 'VTableTree' header_section_div(obj) <- value top_level_section_div(obj) ## S4 method for signature 'PreDataTableLayouts' top_level_section_div(obj) top_level_section_div(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' top_level_section_div(obj) <- value
obj |
( |
only_sep_sections |
( |
value |
( |
Assigned value to section divider must be a character vector. If any value is NA_character_
the section divider will be absent for that row or section. When you want to only affect sections
or splits, please use only_sep_sections
or provide a shorter vector than the number of rows.
Ideally, the length of the vector should be less than the number of splits with, eventually, the
leaf-level, i.e. DataRow
where analyze results are. Note that if only one value is inserted,
only the first split will be affected.
If only_sep_sections = TRUE
, which is the default for section_div()
produced from the table
construction, the section divider will be set for all the splits and eventually analyses, but
not for the header or each row of the table. This can be set with header_section_div
in
basic_table()
or, eventually, with hsep
in build_table()
. If FALSE
, the section
divider will be set for all the rows of the table.
The section divider string. Each line that does not have a trailing separator
will have NA_character_
as section divider.
basic_table()
parameter header_section_div
and top_level_section_div
for global
section dividers.
# Data df <- data.frame( cat = c( "really long thing its so ", "long" ), value = c(6, 3, 10, 1) ) fast_afun <- function(x) list("m" = rcell(mean(x), format = "xx."), "m/2" = max(x) / 2) tbl <- basic_table() %>% split_rows_by("cat", section_div = "~") %>% analyze("value", afun = fast_afun, section_div = " ") %>% build_table(df) # Getter section_div(tbl) # Setter section_div(tbl) <- letters[seq_len(nrow(tbl))] tbl # last letter can appear if there is another table rbind(tbl, tbl) # header_section_div header_section_div(tbl) <- "+" tbl
# Data df <- data.frame( cat = c( "really long thing its so ", "long" ), value = c(6, 3, 10, 1) ) fast_afun <- function(x) list("m" = rcell(mean(x), format = "xx."), "m/2" = max(x) / 2) tbl <- basic_table() %>% split_rows_by("cat", section_div = "~") %>% analyze("value", afun = fast_afun, section_div = " ") %>% build_table(df) # Getter section_div(tbl) # Setter section_div(tbl) <- letters[seq_len(nrow(tbl))] tbl # last letter can appear if there is another table rbind(tbl, tbl) # header_section_div header_section_div(tbl) <- "+" tbl
Split function argument conventions
sf_args(trim, label, first)
sf_args(trim, label, first)
trim |
( |
label |
( |
first |
( |
No return value.
Other conventions:
compat_args()
,
constr_args()
,
gen_args()
,
lyt_args()
This function is used when analyze()
is invoked.
simple_analysis(x, ...) ## S4 method for signature 'numeric' simple_analysis(x, ...) ## S4 method for signature 'logical' simple_analysis(x, ...) ## S4 method for signature 'factor' simple_analysis(x, ...) ## S4 method for signature 'ANY' simple_analysis(x, ...)
simple_analysis(x, ...) ## S4 method for signature 'numeric' simple_analysis(x, ...) ## S4 method for signature 'logical' simple_analysis(x, ...) ## S4 method for signature 'factor' simple_analysis(x, ...) ## S4 method for signature 'ANY' simple_analysis(x, ...)
x |
( |
... |
additional parameters to pass on. |
This function has the following behavior given particular types of inputs:
The in_rows()
function is called on the resulting value(s). All other classes of input currently lead to an error.
A RowsVerticalSection
object (or NULL
). The details of this object should be considered an
internal implementation detail.
Gabriel Becker and Adrian Waddell
simple_analysis(1:3) simple_analysis(iris$Species) simple_analysis(iris$Species == "setosa")
simple_analysis(1:3) simple_analysis(iris$Species) simple_analysis(iris$Species == "setosa")
Main sorting function to order the sub-structure of a TableTree
at a particular path in the table tree.
sort_at_path( tt, path, scorefun, decreasing = NA, na.pos = c("omit", "last", "first"), .prev_path = character() )
sort_at_path( tt, path, scorefun, decreasing = NA, na.pos = c("omit", "last", "first"), .prev_path = character() )
tt |
( |
path |
( |
scorefun |
( |
decreasing |
( |
na.pos |
( |
.prev_path |
( |
sort_at_path
, given a path, locates the (sub)table(s) described by the path (see below for handling of the "*"
wildcard). For each such subtable, it then calls scorefun
on each direct child of the table, using the resulting
scores to determine their sorted order. tt
is then modified to reflect each of these one or more sorting
operations.
In path
, a leading "root"
element will be ignored, regardless of whether this matches the object name (and thus
actual root path name) of tt
. Including "root"
in paths where it does not match the name of tt
may mask deeper
misunderstandings of how valid paths within a TableTree
object correspond to the layout used to originally declare
it, which we encourage users to avoid.
path
can include the "wildcard" "*"
as a step, which translates roughly to any node/branching element and means
that each child at that step will be separately sorted based on scorefun
and the remaining path
entries. This
can occur multiple times in a path.
A list of valid (non-wildcard) paths can be seen in the path
column of the data.frame
created by
formatters::make_row_df()
with the visible_only
argument set to FALSE
. It can also be inferred from the
summary given by table_structure()
.
Note that sorting needs a deeper understanding of table structure in rtables
. Please consider reading the related
vignette
(Sorting and Pruning)
and explore table structure with useful functions like table_structure()
and row_paths_summary()
. It is also
very important to understand the difference between "content" rows and "data" rows. The first one analyzes and
describes the split variable generally and is generated with summarize_row_groups()
, while the second one is
commonly produced by calling one of the various analyze()
instances.
Built-in score functions are cont_n_allcols()
and cont_n_onecol()
. They are both working with content rows
(coming from summarize_row_groups()
) while a custom score function needs to be used on DataRow
s. Here, some
useful descriptor and accessor functions (coming from related vignette):
cell_values()
- Retrieves a named list of a TableRow
or TableTree
object's values.
formatters::obj_name()
- Retrieves the name of an object. Note this can differ from the label that is
displayed (if any is) when printing.
formatters::obj_label()
- Retrieves the display label of an object. Note this can differ from the name that
appears in the path.
content_table()
- Retrieves a TableTree
object's content table (which contains its summary rows).
tree_children()
- Retrieves a TableTree
object's direct children (either subtables, rows or possibly a mix
thereof, though that should not happen in practice).
A TableTree
with the same structure as tt
with the exception that the requested sorting has been done
at path
.
Score functions cont_n_allcols()
and cont_n_onecol()
.
formatters::make_row_df()
and table_structure()
for pathing information.
tt_at_path()
to select a table's (sub)structure at a given path.
# Creating a table to sort # Function that gives two statistics per table-tree "leaf" more_analysis_fnc <- function(x) { in_rows( "median" = median(x), "mean" = mean(x), .formats = "xx.x" ) } # Main layout of the table raw_lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by( "RACE", split_fun = drop_and_remove_levels("WHITE") # dropping WHITE levels ) %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE", afun = more_analysis_fnc) # Creating the table and pruning empty and NAs tbl <- build_table(raw_lyt, DM) %>% prune_table() # Peek at the table structure to understand how it is built table_structure(tbl) # Sorting only ASIAN sub-table, or, in other words, sorting STRATA elements for # the ASIAN group/row-split. This uses content_table() accessor function as it # is a "ContentRow". In this case, we also base our sorting only on the second column. sort_at_path(tbl, c("ASIAN", "STRATA1"), cont_n_onecol(2)) # Custom scoring function that is working on "DataRow"s scorefun <- function(tt) { # Here we could use browser() sum(unlist(row_values(tt))) # Different accessor function } # Sorting mean and median for all the AGE leaves! sort_at_path(tbl, c("RACE", "*", "STRATA1", "*", "AGE"), scorefun)
# Creating a table to sort # Function that gives two statistics per table-tree "leaf" more_analysis_fnc <- function(x) { in_rows( "median" = median(x), "mean" = mean(x), .formats = "xx.x" ) } # Main layout of the table raw_lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by( "RACE", split_fun = drop_and_remove_levels("WHITE") # dropping WHITE levels ) %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE", afun = more_analysis_fnc) # Creating the table and pruning empty and NAs tbl <- build_table(raw_lyt, DM) %>% prune_table() # Peek at the table structure to understand how it is built table_structure(tbl) # Sorting only ASIAN sub-table, or, in other words, sorting STRATA elements for # the ASIAN group/row-split. This uses content_table() accessor function as it # is a "ContentRow". In this case, we also base our sorting only on the second column. sort_at_path(tbl, c("ASIAN", "STRATA1"), cont_n_onecol(2)) # Custom scoring function that is working on "DataRow"s scorefun <- function(tt) { # Here we could use browser() sum(unlist(row_values(tt))) # Different accessor function } # Sorting mean and median for all the AGE leaves! sort_at_path(tbl, c("RACE", "*", "STRATA1", "*", "AGE"), scorefun)
.spl_context
is an optional parameter for any of rtables' special functions, i.e. afun
(analysis function
in analyze()
), cfun
(content or label function in summarize_row_groups()
), or split_fun
(e.g. for
split_rows_by()
).
The .spl_context
data.frame
gives information about the subsets of data corresponding to the splits within
which the current analyze
action is nested. Taken together, these correspond to the path that the resulting (set
of) rows the analysis function is creating, although the information is in a slightly different form. Each split
(which correspond to groups of rows in the resulting table), as well as the initial 'root' "split", is represented
via the following columns:
The name of the split (often the variable being split).
The string representation of the value at that split (split
).
A data.frame
containing the full data (i.e. across all columns) corresponding to the path
defined by the combination of split
and value
of this row and all rows above this row.
The number of observations corresponding to the row grouping (union of all columns).
These list columns
(named the same as names(col_exprs(tab))
) contain logical vectors corresponding to the subset of this row's
full_parent_df
corresponding to the column.
Identifier of the current column. This may be an internal name, constructed by pasting the column path together.
List column containing logical vectors indicating the subset of this row's full_parent_df
for the column currently being created by the analysis function.
List of current column expression. This may be used to filter .alt_df_row
, or any external
data, by column. Filtering .alt_df_row
by columns produces .alt_df
.
Integer column containing the observation counts for that split.
Current column split names. This is recovered from the current column path.
Current column split values. This is recovered from the current column path.
Within analysis functions that accept .spl_context
, the all_cols_n
and cur_col_n
columns of the data frame
will contain the 'true' observation counts corresponding to the row-group and row-group x column subsets of the
data. These numbers will not, and currently cannot, reflect alternate column observation counts provided by the
alt_counts_df
, col_counts
or col_total
arguments to build_table()
.
Translate spl_context to a path to display in error messages
spl_context_to_disp_path(ctx)
spl_context_to_disp_path(ctx)
ctx |
( |
A character string containing a description of the row path corresponding to ctx
.
This function is intended for use when writing custom splitting logic. In cases where the split is associated with
a single variable, the name of that variable will be returned. At time of writing this includes splits generated
via the split_rows_by()
, split_cols_by()
, split_rows_by_cuts()
, split_cols_by_cuts()
,
split_rows_by_cutfun()
, and split_cols_by_cutfun()
layout directives.
spl_variable(spl) ## S4 method for signature 'VarLevelSplit' spl_variable(spl) ## S4 method for signature 'VarDynCutSplit' spl_variable(spl) ## S4 method for signature 'VarStaticCutSplit' spl_variable(spl) ## S4 method for signature 'Split' spl_variable(spl)
spl_variable(spl) ## S4 method for signature 'VarLevelSplit' spl_variable(spl) ## S4 method for signature 'VarDynCutSplit' spl_variable(spl) ## S4 method for signature 'VarStaticCutSplit' spl_variable(spl) ## S4 method for signature 'Split' spl_variable(spl)
spl |
( |
For splits with a single variable associated with them, returns the split. Otherwise, an error is raised.
Will generate children for each subset of a categorical variable.
split_cols_by( lyt, var, labels_var = var, split_label = var, split_fun = NULL, format = NULL, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), ref_group = NULL, show_colcounts = FALSE, colcount_format = NULL )
split_cols_by( lyt, var, labels_var = var, split_label = var, split_fun = NULL, format = NULL, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), ref_group = NULL, show_colcounts = FALSE, colcount_format = NULL )
lyt |
( |
var |
( |
labels_var |
( |
split_label |
( |
split_fun |
( |
format |
( |
nested |
( |
child_labels |
( |
extra_args |
( |
ref_group |
( |
show_colcounts |
( |
colcount_format |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
User-defined custom split functions can perform any type of computation on the incoming data provided that they meet the requirements for generating "splits" of the incoming data based on the split object.
Split functions are functions that accept:
a data.frame
of incoming data to be split.
a Split object. This is largely an internal detail custom functions will not need to worry about,
but obj_name(spl)
, for example, will give the name of the split as it will appear in paths in the resulting
table.
any pre-calculated values. If given non-NULL
values, the values returned should match these.
Should be NULL
in most cases and can usually be ignored.
any pre-calculated value labels. Same as above for values
.
if TRUE
, resulting splits that are empty are removed.
a data.frame
describing previously performed splits which collectively
arrived at df
.
The function must then output a named list
with the following elements:
the vector of all values corresponding to the splits of df
.
a list of data.frame
s representing the groupings of the actual observations from df
.
a character vector giving a string label for each value listed in the values
element above.
if present, extra arguments are to be passed to summary and analysis functions
whenever they are executed on the corresponding element of datasplit
or a subset thereof.
One way to generate custom splitting functions is to wrap existing split functions and modify either the incoming data before they are called or their outputs.
Gabriel Becker
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) tbl # Let's look at the splits in more detail lyt1 <- basic_table() %>% split_cols_by("ARM") lyt1 # add an analysis (summary) lyt2 <- lyt1 %>% analyze(c("AGE", "COUNTRY"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt2 tbl2 <- build_table(lyt2, DM) tbl2 # By default sequentially adding layouts results in nesting library(dplyr) DM_MF <- DM %>% filter(SEX %in% c("M", "F")) %>% mutate(SEX = droplevels(SEX)) lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% analyze(c("AGE", "COUNTRY"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt3 tbl3 <- build_table(lyt3, DM_MF) tbl3 # nested=TRUE vs not lyt4 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE") lyt4 tbl4 <- build_table(lyt4, DM) tbl4 lyt5 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% analyze("AGE") %>% split_rows_by("RACE", nested = FALSE, split_fun = drop_split_levels) %>% analyze("AGE") lyt5 tbl5 <- build_table(lyt5, DM) tbl5
lyt <- basic_table() %>% split_cols_by("ARM") %>% analyze(c("AGE", "BMRKR2")) tbl <- build_table(lyt, ex_adsl) tbl # Let's look at the splits in more detail lyt1 <- basic_table() %>% split_cols_by("ARM") lyt1 # add an analysis (summary) lyt2 <- lyt1 %>% analyze(c("AGE", "COUNTRY"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt2 tbl2 <- build_table(lyt2, DM) tbl2 # By default sequentially adding layouts results in nesting library(dplyr) DM_MF <- DM %>% filter(SEX %in% c("M", "F")) %>% mutate(SEX = droplevels(SEX)) lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% analyze(c("AGE", "COUNTRY"), afun = list_wrap_x(summary), format = "xx.xx" ) lyt3 tbl3 <- build_table(lyt3, DM_MF) tbl3 # nested=TRUE vs not lyt4 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE") lyt4 tbl4 <- build_table(lyt4, DM) tbl4 lyt5 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% analyze("AGE") %>% split_rows_by("RACE", nested = FALSE, split_fun = drop_split_levels) %>% analyze("AGE") lyt5 tbl5 <- build_table(lyt5, DM) tbl5
Create columns (or row splits) based on values (such as quartiles) of var
.
split_cols_by_cuts( lyt, var, cuts, cutlabels = NULL, split_label = var, nested = TRUE, cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_rows_by_cuts( lyt, var, cuts, cutlabels = NULL, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, cumulative = FALSE, label_pos = "hidden", section_div = NA_character_ ) split_cols_by_cutfun( lyt, var, cutfun = qtile_cuts, cutlabelfun = function(x) NULL, split_label = var, nested = TRUE, extra_args = list(), cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_cols_by_quartiles( lyt, var, split_label = var, nested = TRUE, extra_args = list(), cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_rows_by_quartiles( lyt, var, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, label_pos = "hidden", section_div = NA_character_ ) split_rows_by_cutfun( lyt, var, cutfun = qtile_cuts, cutlabelfun = function(x) NULL, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, label_pos = "hidden", section_div = NA_character_ )
split_cols_by_cuts( lyt, var, cuts, cutlabels = NULL, split_label = var, nested = TRUE, cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_rows_by_cuts( lyt, var, cuts, cutlabels = NULL, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, cumulative = FALSE, label_pos = "hidden", section_div = NA_character_ ) split_cols_by_cutfun( lyt, var, cutfun = qtile_cuts, cutlabelfun = function(x) NULL, split_label = var, nested = TRUE, extra_args = list(), cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_cols_by_quartiles( lyt, var, split_label = var, nested = TRUE, extra_args = list(), cumulative = FALSE, show_colcounts = FALSE, colcount_format = NULL ) split_rows_by_quartiles( lyt, var, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, label_pos = "hidden", section_div = NA_character_ ) split_rows_by_cutfun( lyt, var, cutfun = qtile_cuts, cutlabelfun = function(x) NULL, split_label = var, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, label_pos = "hidden", section_div = NA_character_ )
lyt |
( |
var |
( |
cuts |
( |
cutlabels |
( |
split_label |
( |
nested |
( |
cumulative |
( |
show_colcounts |
( |
colcount_format |
( |
format |
( |
na_str |
( |
label_pos |
( |
section_div |
( |
cutfun |
( |
cutlabelfun |
( |
extra_args |
( |
child_labels |
( |
indent_mod |
( |
For dynamic cuts, the cut is transformed into a static cut by build_table()
based on the full dataset,
before proceeding. Thus even when nested within another split in column/row space, the resulting split will reflect
the overall values (e.g., quartiles) in the dataset, NOT the values for subset it is nested under.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
library(dplyr) # split_cols_by_cuts lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_cuts("AGE", split_label = "Age", cuts = c(0, 25, 35, 1000), cutlabels = c("young", "medium", "old") ) %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl <- build_table(lyt, ex_adsl) tbl # split_rows_by_cuts lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by_cuts("AGE", split_label = "Age", cuts = c(0, 25, 35, 1000), cutlabels = c("young", "medium", "old") ) %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl2 <- build_table(lyt2, ex_adsl) tbl2 # split_cols_by_quartiles lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_quartiles("AGE", split_label = "Age") %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl3 <- build_table(lyt3, ex_adsl) tbl3 # split_rows_by_quartiles lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% split_rows_by_quartiles("AGE", split_label = "Age") %>% analyze("BMRKR2") %>% append_topleft(c("Age Quartiles", " Counts BMRKR2")) tbl4 <- build_table(lyt4, ex_adsl) tbl4 # split_cols_by_cutfun cutfun <- function(x) { cutpoints <- c( min(x), mean(x), max(x) ) names(cutpoints) <- c("", "Younger", "Older") cutpoints } lyt5 <- basic_table() %>% split_cols_by_cutfun("AGE", cutfun = cutfun) %>% analyze("SEX") tbl5 <- build_table(lyt5, ex_adsl) tbl5 # split_rows_by_cutfun lyt6 <- basic_table() %>% split_cols_by("SEX") %>% split_rows_by_cutfun("AGE", cutfun = cutfun) %>% analyze("BMRKR2") tbl6 <- build_table(lyt6, ex_adsl) tbl6
library(dplyr) # split_cols_by_cuts lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_cuts("AGE", split_label = "Age", cuts = c(0, 25, 35, 1000), cutlabels = c("young", "medium", "old") ) %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl <- build_table(lyt, ex_adsl) tbl # split_rows_by_cuts lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by_cuts("AGE", split_label = "Age", cuts = c(0, 25, 35, 1000), cutlabels = c("young", "medium", "old") ) %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl2 <- build_table(lyt2, ex_adsl) tbl2 # split_cols_by_quartiles lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_quartiles("AGE", split_label = "Age") %>% analyze(c("BMRKR2", "STRATA2")) %>% append_topleft("counts") tbl3 <- build_table(lyt3, ex_adsl) tbl3 # split_rows_by_quartiles lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% split_rows_by_quartiles("AGE", split_label = "Age") %>% analyze("BMRKR2") %>% append_topleft(c("Age Quartiles", " Counts BMRKR2")) tbl4 <- build_table(lyt4, ex_adsl) tbl4 # split_cols_by_cutfun cutfun <- function(x) { cutpoints <- c( min(x), mean(x), max(x) ) names(cutpoints) <- c("", "Younger", "Older") cutpoints } lyt5 <- basic_table() %>% split_cols_by_cutfun("AGE", cutfun = cutfun) %>% analyze("SEX") tbl5 <- build_table(lyt5, ex_adsl) tbl5 # split_rows_by_cutfun lyt6 <- basic_table() %>% split_cols_by("SEX") %>% split_rows_by_cutfun("AGE", cutfun = cutfun) %>% analyze("BMRKR2") tbl6 <- build_table(lyt6, ex_adsl) tbl6
In some cases, the variable to be ultimately analyzed is most naturally defined on a column, not a row, basis.
When we need columns to reflect different variables entirely, rather than different levels of a single
variable, we use split_cols_by_multivar
.
split_cols_by_multivar( lyt, vars, split_fun = NULL, varlabels = vars, varnames = NULL, nested = TRUE, extra_args = list(), show_colcounts = FALSE, colcount_format = NULL )
split_cols_by_multivar( lyt, vars, split_fun = NULL, varlabels = vars, varnames = NULL, nested = TRUE, extra_args = list(), show_colcounts = FALSE, colcount_format = NULL )
lyt |
( |
vars |
( |
split_fun |
( |
varlabels |
( |
varnames |
( |
nested |
( |
extra_args |
( |
show_colcounts |
( |
colcount_format |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
library(dplyr) ANL <- DM %>% mutate(value = rnorm(n()), pctdiff = runif(n())) ## toy example where we take the mean of the first variable and the ## count of >.5 for the second. colfuns <- list( function(x) in_rows(mean = mean(x), .formats = "xx.x"), function(x) in_rows("# x > 5" = sum(x > .5), .formats = "xx") ) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff")) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = colfuns) lyt tbl <- build_table(lyt, ANL) tbl
library(dplyr) ANL <- DM %>% mutate(value = rnorm(n()), pctdiff = runif(n())) ## toy example where we take the mean of the first variable and the ## count of >.5 for the second. colfuns <- list( function(x) in_rows(mean = mean(x), .formats = "xx.x"), function(x) in_rows("# x > 5" = sum(x > .5), .formats = "xx") ) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by_multivar(c("value", "pctdiff")) %>% split_rows_by("RACE", split_label = "ethnicity", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = colfuns) lyt tbl <- build_table(lyt, ANL) tbl
This is a collection of useful, default split function that can help you in dividing the data, hence the
table rows or columns, into different parts or groups (splits). You can also create your own split function if you
need to create a custom division as specific as you need. Please consider reading custom_split_funs if
this is the case. Beyond this list of functions, you can also use add_overall_level()
and add_combo_levels()
for adding or modifying levels and trim_levels_to_map()
to provide possible level combinations to filter the split
with.
keep_split_levels(only, reorder = TRUE) remove_split_levels(excl) drop_split_levels(df, spl, vals = NULL, labels = NULL, trim = FALSE) drop_and_remove_levels(excl) reorder_split_levels(neworder, newlabels = neworder, drlevels = TRUE) trim_levels_in_group(innervar, drop_outlevs = TRUE)
keep_split_levels(only, reorder = TRUE) remove_split_levels(excl) drop_split_levels(df, spl, vals = NULL, labels = NULL, trim = FALSE) drop_and_remove_levels(excl) reorder_split_levels(neworder, newlabels = neworder, drlevels = TRUE) trim_levels_in_group(innervar, drop_outlevs = TRUE)
only |
( |
reorder |
( |
excl |
( |
df |
( |
spl |
( |
vals |
( |
labels |
( |
trim |
( |
neworder |
( |
newlabels |
( |
drlevels |
( |
innervar |
( |
drop_outlevs |
( |
A function that can be used to split the data accordingly. The actual function signature is similar to the one you can define when creating a fully custom one. For more details see custom_split_funs.
keep_split_levels()
: keeps only specified levels (only
) in the split variable. If any of the specified
levels is not present, an error is returned. reorder = TRUE
(the default) orders the split levels
according to the order of only
.
remove_split_levels()
: Removes specified levels (excl
) from the split variable. Nothing done if not in data.
drop_split_levels()
: Drops levels that have no representation in the data.
drop_and_remove_levels()
: Removes specified levels excl
and drops all levels that are
not in the data.
reorder_split_levels()
: Reorders split levels following neworder
, which needs to be of
same size as the levels in data.
trim_levels_in_group()
: Takes the split groups and removes levels of innervar
if not present in
those split groups. If you want to specify a filter of possible combinations, please
consider using trim_levels_to_map()
.
The following parameters are also documented here but they are only the default
signature of a split function: df
(data to be split), spl
(split object), and vals = NULL
,
labels = NULL
, trim = FALSE
(last three only for internal use). See custom_split_funs for more details
and make_split_fun()
for a more advanced API.
custom_split_funs, add_overall_level()
, add_combo_levels()
, and trim_levels_to_map()
.
# keep_split_levels keeps specified levels (reorder = TRUE by default) lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("USA", "CAN", "BRA")) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # remove_split_levels removes specified split levels lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = remove_split_levels(c( "USA", "CAN", "CHE", "BRA" )) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # drop_split_levels drops levels that are not present in the data lyt <- basic_table() %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Removing "M" and "U" directly, then "UNDIFFERENTIATED" because not in data lyt <- basic_table() %>% split_rows_by("SEX", split_fun = drop_and_remove_levels(c("M", "U"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Reordering levels in split variable lyt <- basic_table() %>% split_rows_by( "SEX", split_fun = reorder_split_levels( neworder = c("U", "F"), newlabels = c(U = "Uu", `F` = "Female") ) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Reordering levels in split variable but keeping all the levels lyt <- basic_table() %>% split_rows_by( "SEX", split_fun = reorder_split_levels( neworder = c("U", "F"), newlabels = c("Uu", "Female"), drlevels = FALSE ) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # trim_levels_in_group() trims levels within each group defined by the split variable dat <- data.frame( col1 = factor(c("A", "B", "C"), levels = c("A", "B", "C", "N")), col2 = factor(c("a", "b", "c"), levels = c("a", "b", "c", "x")) ) # N is removed if drop_outlevs = TRUE, x is removed always tbl <- basic_table() %>% split_rows_by("col1", split_fun = trim_levels_in_group("col2")) %>% analyze("col2") %>% build_table(dat) tbl
# keep_split_levels keeps specified levels (reorder = TRUE by default) lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = keep_split_levels(c("USA", "CAN", "BRA")) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # remove_split_levels removes specified split levels lyt <- basic_table() %>% split_rows_by("COUNTRY", split_fun = remove_split_levels(c( "USA", "CAN", "CHE", "BRA" )) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # drop_split_levels drops levels that are not present in the data lyt <- basic_table() %>% split_rows_by("SEX", split_fun = drop_split_levels) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Removing "M" and "U" directly, then "UNDIFFERENTIATED" because not in data lyt <- basic_table() %>% split_rows_by("SEX", split_fun = drop_and_remove_levels(c("M", "U"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Reordering levels in split variable lyt <- basic_table() %>% split_rows_by( "SEX", split_fun = reorder_split_levels( neworder = c("U", "F"), newlabels = c(U = "Uu", `F` = "Female") ) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # Reordering levels in split variable but keeping all the levels lyt <- basic_table() %>% split_rows_by( "SEX", split_fun = reorder_split_levels( neworder = c("U", "F"), newlabels = c("Uu", "Female"), drlevels = FALSE ) ) %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl # trim_levels_in_group() trims levels within each group defined by the split variable dat <- data.frame( col1 = factor(c("A", "B", "C"), levels = c("A", "B", "C", "N")), col2 = factor(c("a", "b", "c"), levels = c("a", "b", "c", "x")) ) # N is removed if drop_outlevs = TRUE, x is removed always tbl <- basic_table() %>% split_rows_by("col1", split_fun = trim_levels_in_group("col2")) %>% analyze("col2") %>% build_table(dat) tbl
Add rows according to levels of a variable
split_rows_by( lyt, var, labels_var = var, split_label = var, split_fun = NULL, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), label_pos = "hidden", indent_mod = 0L, page_by = FALSE, page_prefix = split_label, section_div = NA_character_ )
split_rows_by( lyt, var, labels_var = var, split_label = var, split_fun = NULL, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), label_pos = "hidden", indent_mod = 0L, page_by = FALSE, page_prefix = split_label, section_div = NA_character_ )
lyt |
( |
var |
( |
labels_var |
( |
split_label |
( |
split_fun |
( |
format |
( |
na_str |
( |
nested |
( |
child_labels |
( |
label_pos |
( |
indent_mod |
( |
page_by |
( |
page_prefix |
( |
section_div |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
User-defined custom split functions can perform any type of computation on the incoming data provided that they meet the requirements for generating "splits" of the incoming data based on the split object.
Split functions are functions that accept:
a data.frame
of incoming data to be split.
a Split object. This is largely an internal detail custom functions will not need to worry about,
but obj_name(spl)
, for example, will give the name of the split as it will appear in paths in the resulting
table.
any pre-calculated values. If given non-NULL
values, the values returned should match these.
Should be NULL
in most cases and can usually be ignored.
any pre-calculated value labels. Same as above for values
.
if TRUE
, resulting splits that are empty are removed.
a data.frame
describing previously performed splits which collectively
arrived at df
.
The function must then output a named list
with the following elements:
the vector of all values corresponding to the splits of df
.
a list of data.frame
s representing the groupings of the actual observations from df
.
a character vector giving a string label for each value listed in the values
element above.
if present, extra arguments are to be passed to summary and analysis functions
whenever they are executed on the corresponding element of datasplit
or a subset thereof.
One way to generate custom splitting functions is to wrap existing split functions and modify either the incoming data before they are called or their outputs.
If var
is a factor with empty unobserved levels and labels_var
is specified, it must also be a factor
with the same number of levels as var
. Currently the error that occurs when this is not the case is not very
informative, but that will change in the future.
Gabriel Becker
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE", mean, var_labels = "Age", format = "xx.xx") tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE") %>% analyze("AGE", mean, var_labels = "Age", format = "xx.xx") tbl2 <- build_table(lyt2, DM) tbl2 lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% summarize_row_groups(label_fstr = "Overall (N)") %>% split_rows_by("RACE", split_label = "Ethnicity", labels_var = "ethn_lab", split_fun = drop_split_levels ) %>% summarize_row_groups("RACE", label_fstr = "%s (n)") %>% analyze("AGE", var_labels = "Age", afun = mean, format = "xx.xx") lyt3 library(dplyr) DM2 <- DM %>% filter(SEX %in% c("M", "F")) %>% mutate( SEX = droplevels(SEX), gender_lab = c( "F" = "Female", "M" = "Male", "U" = "Unknown", "UNDIFFERENTIATED" = "Undifferentiated" )[SEX], ethn_lab = c( "ASIAN" = "Asian", "BLACK OR AFRICAN AMERICAN" = "Black or African American", "WHITE" = "White", "AMERICAN INDIAN OR ALASKA NATIVE" = "American Indian or Alaska Native", "MULTIPLE" = "Multiple", "NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER" = "Native Hawaiian or Other Pacific Islander", "OTHER" = "Other", "UNKNOWN" = "Unknown" )[RACE] ) tbl3 <- build_table(lyt3, DM2) tbl3
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze("AGE", mean, var_labels = "Age", format = "xx.xx") tbl <- build_table(lyt, DM) tbl lyt2 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE") %>% analyze("AGE", mean, var_labels = "Age", format = "xx.xx") tbl2 <- build_table(lyt2, DM) tbl2 lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% summarize_row_groups(label_fstr = "Overall (N)") %>% split_rows_by("RACE", split_label = "Ethnicity", labels_var = "ethn_lab", split_fun = drop_split_levels ) %>% summarize_row_groups("RACE", label_fstr = "%s (n)") %>% analyze("AGE", var_labels = "Age", afun = mean, format = "xx.xx") lyt3 library(dplyr) DM2 <- DM %>% filter(SEX %in% c("M", "F")) %>% mutate( SEX = droplevels(SEX), gender_lab = c( "F" = "Female", "M" = "Male", "U" = "Unknown", "UNDIFFERENTIATED" = "Undifferentiated" )[SEX], ethn_lab = c( "ASIAN" = "Asian", "BLACK OR AFRICAN AMERICAN" = "Black or African American", "WHITE" = "White", "AMERICAN INDIAN OR ALASKA NATIVE" = "American Indian or Alaska Native", "MULTIPLE" = "Multiple", "NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER" = "Native Hawaiian or Other Pacific Islander", "OTHER" = "Other", "UNKNOWN" = "Unknown" )[RACE] ) tbl3 <- build_table(lyt3, DM2) tbl3
When we need rows to reflect different variables rather than different
levels of a single variable, we use split_rows_by_multivar
.
split_rows_by_multivar( lyt, vars, split_fun = NULL, split_label = "", varlabels = vars, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), indent_mod = 0L, section_div = NA_character_, extra_args = list() )
split_rows_by_multivar( lyt, vars, split_fun = NULL, split_label = "", varlabels = vars, format = NULL, na_str = NA_character_, nested = TRUE, child_labels = c("default", "visible", "hidden"), indent_mod = 0L, section_div = NA_character_, extra_args = list() )
lyt |
( |
vars |
( |
split_fun |
( |
split_label |
( |
varlabels |
( |
format |
( |
na_str |
( |
nested |
( |
child_labels |
( |
indent_mod |
( |
section_div |
( |
extra_args |
( |
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
split_rows_by()
for typical row splitting, and split_cols_by_multivar()
to perform the same type of
split on a column basis.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by_multivar(c("SEX", "STRATA1")) %>% summarize_row_groups() %>% analyze(c("AGE", "SEX")) tbl <- build_table(lyt, DM) tbl
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by_multivar(c("SEX", "STRATA1")) %>% summarize_row_groups() %>% analyze(c("AGE", "SEX")) tbl <- build_table(lyt, DM) tbl
Add a content row of summary counts
summarize_row_groups( lyt, var = "", label_fstr = "%s", format = "xx (xx.x%)", na_str = "-", cfun = NULL, indent_mod = 0L, extra_args = list() )
summarize_row_groups( lyt, var = "", label_fstr = "%s", format = "xx (xx.x%)", na_str = "-", cfun = NULL, indent_mod = 0L, extra_args = list() )
lyt |
( |
var |
( |
label_fstr |
( |
format |
( |
na_str |
( |
cfun |
( |
indent_mod |
( |
extra_args |
( |
If format
expects 1 value (i.e. it is specified as a format string and xx
appears for two values
(i.e. xx
appears twice in the format string) or is specified as a function, then both raw and percent of
column total counts are calculated. If format
is a format string where xx
appears only one time, only
raw counts are used.
cfun
must accept x
or df
as its first argument. For the df
argument cfun
will receive the subset
data.frame
corresponding with the row- and column-splitting for the cell being calculated. Must accept
labelstr
as the second parameter, which accepts the label
of the level of the parent split currently
being summarized. Can additionally take any optional argument supported by analysis functions. (see analyze()
).
In addition, if complex custom functions are needed, we suggest checking the available additional_fun_params
that can be used in cfun
.
A PreDataTableLayouts
object suitable for passing to further layouting functions, and to build_table()
.
Gabriel Becker
DM2 <- subset(DM, COUNTRY %in% c("USA", "CAN", "CHN")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("COUNTRY", split_fun = drop_split_levels) %>% summarize_row_groups(label_fstr = "%s (n)") %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") lyt tbl <- build_table(lyt, DM2) tbl row_paths_summary(tbl) # summary count is a content table ## use a cfun and extra_args to customize summarization ## behavior sfun <- function(x, labelstr, trim) { in_rows( c(mean(x, trim = trim), trim), .formats = "xx.x (xx.x%)", .labels = sprintf( "%s (Trimmed mean and trim %%)", labelstr ) ) } lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% split_rows_by("COUNTRY", split_fun = drop_split_levels) %>% summarize_row_groups("AGE", cfun = sfun, extra_args = list(trim = .2) ) %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") %>% append_topleft(c("Country", " Age")) tbl2 <- build_table(lyt2, DM2) tbl2
DM2 <- subset(DM, COUNTRY %in% c("USA", "CAN", "CHN")) lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("COUNTRY", split_fun = drop_split_levels) %>% summarize_row_groups(label_fstr = "%s (n)") %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") lyt tbl <- build_table(lyt, DM2) tbl row_paths_summary(tbl) # summary count is a content table ## use a cfun and extra_args to customize summarization ## behavior sfun <- function(x, labelstr, trim) { in_rows( c(mean(x, trim = trim), trim), .formats = "xx.x (xx.x%)", .labels = sprintf( "%s (Trimmed mean and trim %%)", labelstr ) ) } lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM") %>% split_rows_by("COUNTRY", split_fun = drop_split_levels) %>% summarize_row_groups("AGE", cfun = sfun, extra_args = list(trim = .2) ) %>% analyze("AGE", afun = list_wrap_x(summary), format = "xx.xx") %>% append_topleft(c("Country", " Age")) tbl2 <- build_table(lyt2, DM2) tbl2
A table shell is a rendering of the table which maintains the structure, but does not display the values, rather displaying the formatting instructions for each cell.
table_shell( tt, widths = NULL, col_gap = 3, hsep = default_hsep(), tf_wrap = FALSE, max_width = NULL ) table_shell_str( tt, widths = NULL, col_gap = 3, hsep = default_hsep(), tf_wrap = FALSE, max_width = NULL )
table_shell( tt, widths = NULL, col_gap = 3, hsep = default_hsep(), tf_wrap = FALSE, max_width = NULL ) table_shell_str( tt, widths = NULL, col_gap = 3, hsep = default_hsep(), tf_wrap = FALSE, max_width = NULL )
tt |
( |
widths |
( |
col_gap |
( |
hsep |
( |
tf_wrap |
( |
max_width |
( |
table_shell
returns NULL
, as the function is called for the side effect of printing the shell to the console.
table_shell_str
returns the string representing the table shell.
value_formats()
for a matrix of formats for each cell in a table.
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") tbl <- build_table(lyt, iris2) table_shell(tbl)
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") tbl <- build_table(lyt, iris2) table_shell(tbl)
Summarize table
table_structure(x, detail = c("subtable", "row"))
table_structure(x, detail = c("subtable", "row"))
x |
( |
detail |
( |
No return value. Called for the side-effect of printing a row- or subtable-structure summary of x
.
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx" ) tbl <- build_table(lyt, iris2) tbl row_paths(tbl) table_structure(tbl) table_structure(tbl, detail = "row")
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx" ) tbl <- build_table(lyt, iris2) tbl row_paths(tbl) table_structure(tbl) table_structure(tbl, detail = "row")
A TableTree
object can have top left material which is a sequence of strings which are printed in the
area of the table between the column header display and the label of the first row. These functions access
and modify that material.
top_left(obj) ## S4 method for signature 'VTableTree' top_left(obj) ## S4 method for signature 'InstantiatedColumnInfo' top_left(obj) ## S4 method for signature 'PreDataTableLayouts' top_left(obj) top_left(obj) <- value ## S4 replacement method for signature 'VTableTree' top_left(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' top_left(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' top_left(obj) <- value
top_left(obj) ## S4 method for signature 'VTableTree' top_left(obj) ## S4 method for signature 'InstantiatedColumnInfo' top_left(obj) ## S4 method for signature 'PreDataTableLayouts' top_left(obj) top_left(obj) <- value ## S4 replacement method for signature 'VTableTree' top_left(obj) <- value ## S4 replacement method for signature 'InstantiatedColumnInfo' top_left(obj) <- value ## S4 replacement method for signature 'PreDataTableLayouts' top_left(obj) <- value
obj |
( |
value |
( |
A character vector representing the top-left material of obj
(or obj
after modification, in the
case of the setter).
rtable
object to a stringTransform a complex object into a string representation ready to be printed or written to a plain-text file.
All objects that are printed to console pass via toString
. This function allows
fundamental formatting specifications to be applied to final output, like column widths
and relative wrapping (width
), title and footer wrapping (tf_wrap = TRUE
and
max_width
), and horizontal separator character (e.g. hsep = "+"
).
## S4 method for signature 'VTableTree' toString( x, widths = NULL, col_gap = 3, hsep = horizontal_sep(x), indent_size = 2, tf_wrap = FALSE, max_width = NULL, fontspec = font_spec(), ttype_ok = FALSE )
## S4 method for signature 'VTableTree' toString( x, widths = NULL, col_gap = 3, hsep = horizontal_sep(x), indent_size = 2, tf_wrap = FALSE, max_width = NULL, fontspec = font_spec(), ttype_ok = FALSE )
x |
( |
widths |
( |
col_gap |
( |
hsep |
( |
indent_size |
( |
tf_wrap |
( |
max_width |
( |
fontspec |
( |
ttype_ok |
( |
Manual insertion of newlines is not supported when tf_wrap = TRUE
and will result in a warning and
undefined wrapping behavior. Passing vectors of already split strings remains supported, however in this
case each string is word-wrapped separately with the behavior described above.
A string representation of x
as it appears when printed.
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") tbl <- build_table(lyt, iris2) cat(toString(tbl, col_gap = 3))
library(dplyr) iris2 <- iris %>% group_by(Species) %>% mutate(group = as.factor(rep_len(c("a", "b"), length.out = n()))) %>% ungroup() lyt <- basic_table() %>% split_cols_by("Species") %>% split_cols_by("group") %>% analyze(c("Sepal.Length", "Petal.Width"), afun = list_wrap_x(summary), format = "xx.xx") tbl <- build_table(lyt, iris2) cat(toString(tbl, col_gap = 3))
Retrieve or set the direct children of a tree-style object
tree_children(x) tree_children(x) <- value
tree_children(x) tree_children(x) <- value
x |
( |
value |
( |
A list of direct children of x
.
Trim levels of another variable from each facet (post-processing split step)
trim_levels_in_facets(innervar)
trim_levels_in_facets(innervar)
innervar |
( |
A function suitable for use in the pre
(list) argument of make_split_fun
.
Other make_custom_split:
add_combo_facet()
,
drop_facet_levels()
,
make_split_fun()
,
make_split_result()
This split function constructor creates a split function which trims levels of a variable to reflect restrictions on the possible combinations of two or more variables which the data is split by (along the same axis) within a layout.
trim_levels_to_map(map = NULL)
trim_levels_to_map(map = NULL)
map |
data.frame. A data.frame defining allowed combinations of variables. Any combination at the level of this split not present in the map will be removed from the data, both for the variable being split and those present in the data but not associated with this split or any parents of it. |
When splitting occurs, the map is subset to the values of all previously performed splits. The levels of the variable being split are then pruned to only those still present within this subset of the map representing the current hierarchical splitting context.
Splitting is then performed via the keep_split_levels()
split function.
Each resulting element of the partition is then further trimmed by pruning values of any remaining variables specified in the map to those values allowed under the combination of the previous and current split.
A function that can be used as a split function.
map <- data.frame( LBCAT = c("CHEMISTRY", "CHEMISTRY", "CHEMISTRY", "IMMUNOLOGY"), PARAMCD = c("ALT", "CRP", "CRP", "IGA"), ANRIND = c("LOW", "LOW", "HIGH", "HIGH"), stringsAsFactors = FALSE ) lyt <- basic_table() %>% split_rows_by("LBCAT") %>% split_rows_by("PARAMCD", split_fun = trim_levels_to_map(map = map)) %>% analyze("ANRIND") tbl <- build_table(lyt, ex_adlb)
map <- data.frame( LBCAT = c("CHEMISTRY", "CHEMISTRY", "CHEMISTRY", "IMMUNOLOGY"), PARAMCD = c("ALT", "CRP", "CRP", "IGA"), ANRIND = c("LOW", "LOW", "HIGH", "HIGH"), stringsAsFactors = FALSE ) lyt <- basic_table() %>% split_rows_by("LBCAT") %>% split_rows_by("PARAMCD", split_fun = trim_levels_to_map(map = map)) %>% analyze("ANRIND") tbl <- build_table(lyt, ex_adlb)
Trim rows from a populated table without regard for table structure
trim_rows(tt, criteria = all_zero_or_na)
trim_rows(tt, criteria = all_zero_or_na)
tt |
( |
criteria |
( |
This function will be deprecated in the future in favor of the more elegant and versatile prune_table()
function which can perform the same function as trim_rows()
but is more powerful as it takes table structure
into account.
The table with rows that have only NA
or 0 cell values removed.
Visible LabelRow
s are including in this trimming, which can lead to either all label rows being trimmed or
label rows remaining when all data rows have been trimmed, depending on what criteria
returns when called on
a LabelRow
object. To avoid this, use the structurally-aware prune_table()
machinery instead.
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") tbl_to_trim <- basic_table() %>% analyze("BMRKR1") %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_trim %>% trim_rows() tbl_to_trim %>% trim_rows(all_zero)
adsl <- ex_adsl levels(adsl$SEX) <- c(levels(ex_adsl$SEX), "OTHER") tbl_to_trim <- basic_table() %>% analyze("BMRKR1") %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% summarize_row_groups() %>% split_rows_by("STRATA1") %>% summarize_row_groups() %>% analyze("AGE") %>% build_table(adsl) tbl_to_trim %>% trim_rows() tbl_to_trim %>% trim_rows(all_zero)
Access or set table elements at specified path
tt_at_path(tt, path, ...) tt_at_path(tt, path, ...) <- value
tt_at_path(tt, path, ...) tt_at_path(tt, path, ...) <- value
tt |
( |
path |
( |
... |
unused. |
value |
( |
Setting NULL
at a defined path removes the corresponding sub-table.
# Accessing sub table. lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% split_rows_by("BMRKR2") %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) %>% prune_table() sub_tbl <- tt_at_path(tbl, path = c("SEX", "F", "BMRKR2")) # Removing sub table. tbl2 <- tbl tt_at_path(tbl2, path = c("SEX", "F")) <- NULL tbl2 # Setting sub table. lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze("BMRKR2") tbl3 <- build_table(lyt3, ex_adsl) %>% prune_table() tt_at_path(tbl3, path = c("SEX", "F", "BMRKR2")) <- sub_tbl tbl3
# Accessing sub table. lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% split_rows_by("BMRKR2") %>% analyze("AGE") tbl <- build_table(lyt, ex_adsl) %>% prune_table() sub_tbl <- tt_at_path(tbl, path = c("SEX", "F", "BMRKR2")) # Removing sub table. tbl2 <- tbl tt_at_path(tbl2, path = c("SEX", "F")) <- NULL tbl2 # Setting sub table. lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze("BMRKR2") tbl3 <- build_table(lyt3, ex_adsl) %>% prune_table() tt_at_path(tbl3, path = c("SEX", "F", "BMRKR2")) <- sub_tbl tbl3
flextable
from an rtables
tablePrincipally used for export (export_as_docx()
), this function produces a flextable
from an rtables
table. If theme = NULL
, rtables
-like style will be used. Otherwise,
theme_docx_default()
will produce a .docx
-friendly table.
tt_to_flextable( tt, theme = theme_docx_default(), border = flextable::fp_border_default(width = 0.5), indent_size = NULL, titles_as_header = TRUE, bold_titles = TRUE, footers_as_text = FALSE, counts_in_newline = FALSE, paginate = FALSE, fontspec = NULL, lpp = NULL, cpp = NULL, ..., colwidths = NULL, tf_wrap = !is.null(cpp), max_width = cpp, total_page_height = 10, total_page_width = 10, autofit_to_page = TRUE ) theme_docx_default( font = "Arial", font_size = 9, cell_margins = c(word_mm_to_pt(1.9), word_mm_to_pt(1.9), 0, 0), bold = c("header", "content_rows", "label_rows", "top_left"), bold_manual = NULL, border = flextable::fp_border_default(width = 0.5) ) theme_html_default( font = "Courier", font_size = 9, cell_margins = 0.2, remove_internal_borders = "label_rows", border = flextable::fp_border_default(width = 1, color = "black") ) word_mm_to_pt(mm)
tt_to_flextable( tt, theme = theme_docx_default(), border = flextable::fp_border_default(width = 0.5), indent_size = NULL, titles_as_header = TRUE, bold_titles = TRUE, footers_as_text = FALSE, counts_in_newline = FALSE, paginate = FALSE, fontspec = NULL, lpp = NULL, cpp = NULL, ..., colwidths = NULL, tf_wrap = !is.null(cpp), max_width = cpp, total_page_height = 10, total_page_width = 10, autofit_to_page = TRUE ) theme_docx_default( font = "Arial", font_size = 9, cell_margins = c(word_mm_to_pt(1.9), word_mm_to_pt(1.9), 0, 0), bold = c("header", "content_rows", "label_rows", "top_left"), bold_manual = NULL, border = flextable::fp_border_default(width = 0.5) ) theme_html_default( font = "Courier", font_size = 9, cell_margins = 0.2, remove_internal_borders = "label_rows", border = flextable::fp_border_default(width = 1, color = "black") ) word_mm_to_pt(mm)
tt |
( |
theme |
( |
border |
( |
indent_size |
( |
titles_as_header |
( |
bold_titles |
( |
footers_as_text |
( |
counts_in_newline |
( |
paginate |
( |
fontspec |
( |
lpp |
( |
cpp |
( |
... |
( |
colwidths |
( |
tf_wrap |
( |
max_width |
( |
total_page_height |
( |
total_page_width |
( |
autofit_to_page |
( |
font |
( |
font_size |
( |
cell_margins |
( |
bold |
( |
bold_manual |
(named |
remove_internal_borders |
( |
mm |
( |
Themes can also be extended when you need only a minor change from a default style. You can either
add your own theme to the theme call (e.g. c(theme_docx_default(), my_theme)
) or create a new
theme like shown in the examples. Please pay attention to the parameters' inputs as they are relevant
for this to work properly.
Indeed, it is possible to use some hidden values for building your own theme (hence the need of ...
).
In particular, tt_to_flextable
sends in the following variable: tbl_row_class = make_row_df(tt)$node_class
.
This is ignored if not used in the theme. See theme_docx_default
for an example on own to retrieve
these values and how to use them.
A flextable
object.
theme_docx_default()
: Main theme function for export_as_docx()
.
theme_html_default()
: Theme function for html outputs.
word_mm_to_pt()
: Padding helper functions to transform mm to pt.
Currently cpp
, tf_wrap
, and max_width
are only used in pagination and do not yet have a
clear cooperation with colwidths
and autofit_to_page
. at the moment it is suggested to use the cpp
parameter family cautiously. If issues arise, please communicate with the maintainers or raise an issue.
analysisfun <- function(x, ...) { in_rows( row1 = 5, row2 = c(1, 2), .row_footnotes = list(row1 = "row 1 - row footnote"), .cell_footnotes = list(row2 = "row 2 - cell footnote") ) } lyt <- basic_table( title = "Title says Whaaaat", subtitles = "Oh, ok.", main_footer = "ha HA! Footer!" ) %>% split_cols_by("ARM") %>% analyze("AGE", afun = analysisfun) tbl <- build_table(lyt, ex_adsl) library(flextable) # example code # rtables style tt_to_flextable(tbl, theme = NULL) tt_to_flextable(tbl, theme = theme_docx_default(font_size = 6)) # Example with multiple themes (only extending the docx default!) my_theme <- function(x, ...) { border_inner(x, part = "body", border = flextable::fp_border_default(width = 0.5)) } flx <- tt_to_flextable(tbl, theme = c(theme_docx_default(), my_theme)) library(flextable) # Custom theme special_bold <- list( "header" = list("i" = 1, "j" = c(1, 3)), "body" = list("i" = c(1, 2), "j" = 1) ) custom_theme <- theme_docx_default( font_size = 10, font = "Brush Script MT", border = flextable::fp_border_default(color = "pink", width = 2), bold = NULL, bold_manual = special_bold ) tt_to_flextable(tbl, border = flextable::fp_border_default(color = "pink", width = 2), theme = custom_theme ) # Extending themes my_theme <- function(font_size = 6) { # here can pass additional arguments for default theme function(flx, ...) { # First apply theme_docx_default flx <- theme_docx_default(font_size = font_size)(flx, ...) # Then apply additional styling flx <- border_inner(flx, part = "body", border = flextable::fp_border_default(width = 0.5)) return(flx) } } flx <- tt_to_flextable(tbl, theme = my_theme())
analysisfun <- function(x, ...) { in_rows( row1 = 5, row2 = c(1, 2), .row_footnotes = list(row1 = "row 1 - row footnote"), .cell_footnotes = list(row2 = "row 2 - cell footnote") ) } lyt <- basic_table( title = "Title says Whaaaat", subtitles = "Oh, ok.", main_footer = "ha HA! Footer!" ) %>% split_cols_by("ARM") %>% analyze("AGE", afun = analysisfun) tbl <- build_table(lyt, ex_adsl) library(flextable) # example code # rtables style tt_to_flextable(tbl, theme = NULL) tt_to_flextable(tbl, theme = theme_docx_default(font_size = 6)) # Example with multiple themes (only extending the docx default!) my_theme <- function(x, ...) { border_inner(x, part = "body", border = flextable::fp_border_default(width = 0.5)) } flx <- tt_to_flextable(tbl, theme = c(theme_docx_default(), my_theme)) library(flextable) # Custom theme special_bold <- list( "header" = list("i" = 1, "j" = c(1, 3)), "body" = list("i" = c(1, 2), "j" = 1) ) custom_theme <- theme_docx_default( font_size = 10, font = "Brush Script MT", border = flextable::fp_border_default(color = "pink", width = 2), bold = NULL, bold_manual = special_bold ) tt_to_flextable(tbl, border = flextable::fp_border_default(color = "pink", width = 2), theme = custom_theme ) # Extending themes my_theme <- function(font_size = 6) { # here can pass additional arguments for default theme function(flx, ...) { # First apply theme_docx_default flx <- theme_docx_default(font_size = font_size)(flx, ...) # Then apply additional styling flx <- border_inner(flx, part = "body", border = flextable::fp_border_default(width = 0.5)) return(flx) } } flx <- tt_to_flextable(tbl, theme = my_theme())
Re-indexes footnotes within a built table.
update_ref_indexing(tt)
update_ref_indexing(tt)
tt |
( |
After adding or removing referential footnotes manually, or after subsetting a table, the reference indexes (i.e. the number associated with specific footnotes) may be incorrect. This function recalculates these based on the full table.
In the future this should not generally need to be called manually.
A TableTree
(rtables
-built table) is considered degenerate if:
It contains no subtables or data rows (content rows do not count).
It contains a subtable which is degenerate by the criterion above.
validate_table_struct
assesses whether tt
has a valid (non-degenerate) structure.
assert_valid_table
asserts a table must have a valid structure, and throws an informative error (the default) or
warning (if warn_only
is TRUE
) if the table is degenerate (has invalid structure or contains one or more
invalid substructures.
validate_table_struct(tt) assert_valid_table(tt, warn_only = FALSE)
validate_table_struct(tt) assert_valid_table(tt, warn_only = FALSE)
tt |
( |
warn_only |
( |
validate_table_struct
returns a logical value indicating valid structure.
assert_valid_table
is called for its side-effect of throwing an error or warning for degenerate tables.
This function is experimental and the exact text of the warning/error is subject to change in future releases.
Other table structure validation functions:
find_degen_struct()
,
sanitize_table_struct()
validate_table_struct(rtable("hahaha")) ## Not run: assert_valid_table(rtable("oops")) ## End(Not run)
validate_table_struct(rtable("hahaha")) ## Not run: assert_valid_table(rtable("oops")) ## End(Not run)
Returns a matrix of formats for the cells in a table.
value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'ANY' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'TableRow' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'LabelRow' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'VTableTree' value_formats(obj, default = obj_format(obj))
value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'ANY' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'TableRow' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'LabelRow' value_formats(obj, default = obj_format(obj)) ## S4 method for signature 'VTableTree' value_formats(obj, default = obj_format(obj))
obj |
( |
default |
( |
Matrix (storage mode list) containing the effective format for each cell position in the table
(including 'virtual' cells implied by label rows, whose formats are always NULL
).
table_shell()
and table_shell_str()
for information on the table format structure.
lyt <- basic_table() %>% split_rows_by("RACE", split_fun = keep_split_levels(c("ASIAN", "WHITE"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) value_formats(tbl)
lyt <- basic_table() %>% split_rows_by("RACE", split_fun = keep_split_levels(c("ASIAN", "WHITE"))) %>% analyze("AGE") tbl <- build_table(lyt, DM) value_formats(tbl)
Split on levels within a variable
VarLevelSplit( var, split_label, labels_var = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_fun = NULL, split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, label_pos = c("topleft", "hidden", "visible"), cindent_mod = 0L, cvar = "", cextra_args = list(), page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL ) VarLevWBaselineSplit( var, ref_group, labels_var = var, split_label, split_fun = NULL, label_fstr = "%s - %s", cfun = NULL, cformat = NULL, cna_str = NA_character_, cvar = "", split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, extra_args = list(), show_colcounts = FALSE, colcount_format = NULL )
VarLevelSplit( var, split_label, labels_var = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_fun = NULL, split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, label_pos = c("topleft", "hidden", "visible"), cindent_mod = 0L, cvar = "", cextra_args = list(), page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL ) VarLevWBaselineSplit( var, ref_group, labels_var = var, split_label, split_fun = NULL, label_fstr = "%s - %s", cfun = NULL, cformat = NULL, cna_str = NA_character_, cvar = "", split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, extra_args = list(), show_colcounts = FALSE, colcount_format = NULL )
var |
( |
split_label |
( |
labels_var |
( |
cfun |
( |
cformat |
( |
cna_str |
( |
split_fun |
( |
split_format |
( |
split_na_str |
( |
valorder |
( |
split_name |
( |
child_labels |
( |
extra_args |
( |
indent_mod |
( |
label_pos |
( |
cindent_mod |
( |
cvar |
( |
cextra_args |
( |
page_prefix |
( |
section_div |
( |
show_colcounts |
( |
colcount_format |
( |
ref_group |
( |
label_fstr |
( |
a VarLevelSplit
object.
Gabriel Becker
List variables required by a pre-data table layout
vars_in_layout(lyt) ## S4 method for signature 'PreDataTableLayouts' vars_in_layout(lyt) ## S4 method for signature 'PreDataAxisLayout' vars_in_layout(lyt) ## S4 method for signature 'SplitVector' vars_in_layout(lyt) ## S4 method for signature 'Split' vars_in_layout(lyt) ## S4 method for signature 'CompoundSplit' vars_in_layout(lyt) ## S4 method for signature 'ManualSplit' vars_in_layout(lyt)
vars_in_layout(lyt) ## S4 method for signature 'PreDataTableLayouts' vars_in_layout(lyt) ## S4 method for signature 'PreDataAxisLayout' vars_in_layout(lyt) ## S4 method for signature 'SplitVector' vars_in_layout(lyt) ## S4 method for signature 'Split' vars_in_layout(lyt) ## S4 method for signature 'CompoundSplit' vars_in_layout(lyt) ## S4 method for signature 'ManualSplit' vars_in_layout(lyt)
lyt |
( |
This will walk the layout declaration and return a vector of the names of the unique variables that are used in any of the following ways:
Variable being split on (directly or via cuts)
Element of a Multi-variable column split
Content variable
Value-label variable
A character vector containing the unique variables explicitly used in the layout (see the notes below).
This function will not detect dependencies implicit in analysis or summary functions which accept x
or df
and then rely on the existence of particular variables not being split on/analyzed.
The order these variable names appear within the return vector is undefined and should not be relied upon.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% summarize_row_groups(label_fstr = "Overall (N)") %>% split_rows_by("RACE", split_label = "Ethnicity", labels_var = "ethn_lab", split_fun = drop_split_levels ) %>% summarize_row_groups("RACE", label_fstr = "%s (n)") %>% analyze("AGE", var_labels = "Age", afun = mean, format = "xx.xx") vars_in_layout(lyt)
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_cols_by("SEX") %>% summarize_row_groups(label_fstr = "Overall (N)") %>% split_rows_by("RACE", split_label = "Ethnicity", labels_var = "ethn_lab", split_fun = drop_split_levels ) %>% summarize_row_groups("RACE", label_fstr = "%s (n)") %>% analyze("AGE", var_labels = "Age", afun = mean, format = "xx.xx") vars_in_layout(lyt)
Splits for cutting by values of a numeric variable
Create static cut or static cumulative cut split
make_static_cut_split( var, split_label, cuts, cutlabels = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", cumulative = FALSE, page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL ) VarDynCutSplit( var, split_label, cutfun, cutlabelfun = function(x) NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL )
make_static_cut_split( var, split_label, cuts, cutlabels = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", cumulative = FALSE, page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL ) VarDynCutSplit( var, split_label, cutfun, cutlabelfun = function(x) NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = var, child_labels = c("default", "visible", "hidden"), extra_args = list(), cumulative = FALSE, indent_mod = 0L, cindent_mod = 0L, cvar = "", cextra_args = list(), label_pos = "visible", page_prefix = NA_character_, section_div = NA_character_, show_colcounts = FALSE, colcount_format = NULL )
var |
( |
split_label |
( |
cuts |
( |
cutlabels |
( |
cfun |
( |
cformat |
( |
cna_str |
( |
split_format |
( |
split_na_str |
( |
split_name |
( |
child_labels |
( |
extra_args |
( |
indent_mod |
( |
cindent_mod |
( |
cvar |
( |
cextra_args |
( |
label_pos |
( |
cumulative |
( |
page_prefix |
( |
section_div |
( |
show_colcounts |
( |
colcount_format |
( |
cutfun |
( |
cutlabelfun |
( |
A VarStaticCutSplit
, CumulativeCutSplit
object for make_static_cut_split
, or a VarDynCutSplit
object for VarDynCutSplit()
.
rtable
object in the Viewer pane in RStudio or in a browserThe table will be displayed using bootstrap styling.
Viewer(x, y = NULL, ...)
Viewer(x, y = NULL, ...)
x |
( |
y |
( |
... |
arguments passed to |
Not meaningful. Called for the side effect of opening a browser or viewer pane.
if (interactive()) { sl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE), labels = c("S.L > 5", "S.L <= 5") ) df <- cbind(iris, sl5 = sl5) lyt <- basic_table() %>% split_cols_by("sl5") %>% analyze("Sepal.Length") tbl <- build_table(lyt, df) Viewer(tbl) Viewer(tbl, tbl) tbl2 <- htmltools::tags$div( class = "table-responsive", as_html(tbl, class_table = "table") ) Viewer(tbl, tbl2) }
if (interactive()) { sl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE), labels = c("S.L > 5", "S.L <= 5") ) df <- cbind(iris, sl5 = sl5) lyt <- basic_table() %>% split_cols_by("sl5") %>% analyze("Sepal.Length") tbl <- build_table(lyt, df) Viewer(tbl) Viewer(tbl, tbl) tbl2 <- htmltools::tags$div( class = "table-responsive", as_html(tbl, class_table = "table") ) Viewer(tbl, tbl2) }