suggested_dependent_pkgs <- c("dplyr") knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = all(vapply( suggested_dependent_pkgs, requireNamespace, logical(1), quietly = TRUE )) )
knitr::opts_chunk$set(comment = "#")
```{css, echo=FALSE} .reveal .r code { white-space: pre; }
## Customizing Appearance In this vignette, we describe the various ways we can modify and customize the appearance of `rtables`. Loading the package: ```r library(rtables) library(dplyr)
It is possible to align the content by assigning "left"
, "center"
(default), and "right"
to .aligns
and align
arguments in
in_rows()
and rcell()
, respectively. It is also possible to use decimal
,
dec_right
, and dec_left
for decimal alignments. The first takes all numerical
values and aligns the decimal character .
in every value of the column that has
align = "decimal"
. Also numeric without decimal values are aligned according to
an imaginary .
if specified as such. dec_left
and dec_right
behave similarly,
with the difference that if the column present empty spaces at left or right, it
pushes values towards left or right taking the one value that has most decimal
characters, if right, or non-decimal values if left. For more details, please read
the related documentation page help("decimal_align")
.
Please consider using ?in_rows
and ?rcell
for further
clarifications on the two arguments, and use formatters::list_valid_aligns()
to see
all available alignment options.
In the following we show two simplified examples that use align
and
.aligns
, respectively.
# In rcell we use align. lyt <- basic_table() %>% analyze("AGE", function(x) { in_rows( left = rcell("l", align = "left"), right = rcell("r", align = "right"), center = rcell("c", align = "center") ) }) tbl <- build_table(lyt, DM) tbl
# In in_rows, we use .aligns. This can either set the general value or the # single values (see NB). lyt2 <- basic_table() %>% analyze("AGE", function(x) { in_rows( left = rcell("l"), right = rcell("r"), center = rcell("c"), .aligns = c("right") ) # NB: .aligns = c("right", "left", "center") }) tbl2 <- build_table(lyt2, DM) tbl2
These concepts can be well applied to any clinical table as shown in the following, more complex, example.
lyt3 <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze(c("AGE", "STRATA1"), function(x) { if (is.numeric(x)) { in_rows( "mean" = rcell(mean(x)), "sd" = rcell(sd(x)), .formats = c("xx.x"), .aligns = "left" ) } else if (is.factor(x)) { rcell(length(unique(x)), align = "right") } else { stop("Unsupported type") } }, show_labels = "visible", na_str = "NE") tbl3 <- build_table(lyt3, ex_adsl) tbl3
The sequence of strings printed in the area between the column header
display and the first row label \emph{top left material} can be
modified during pre-processing using label position argument in row
splits split_rows_by
, with the append_topleft
function, and during
post-processing using the top_left()
function. Note: Indenting is
automatically added \emph{only in the case of} label_pos =
"topleft"
.
Within the layout initializer:
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("STRATA1") %>% analyze("AGE") %>% append_topleft("New top_left material here") build_table(lyt, DM)
Specify label position using the split_rows
function. Notice the
position of STRATA1
and SEX
.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("STRATA1", label_pos = "topleft") %>% split_rows_by("SEX", label_pos = "topleft") %>% analyze("AGE") build_table(lyt, DM)
Post-processing using the top_left()
function:
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% analyze(c("AGE", "STRATA1"), function(x) { if (is.numeric(x)) { in_rows( "mean" = rcell(mean(x)), "sd" = rcell(sd(x)), .formats = c("xx.x"), .aligns = "left" ) } else if (is.factor(x)) { rcell(length(unique(x)), align = "right") } else { stop("Unsupported type") } }, show_labels = "visible", na_str = "NE") %>% build_table(ex_adsl) # Adding top-left material top_left(lyt) <- "New top-left material here" lyt
Table title, table body, referential footnotes and and main footers
can be inset from the left alignment of the titles and provenance
footer materials. This can be modified within the layout initializer
basic_table()
using the inset
argument or during post-processing
with table_inset()
.
Using the layout initializer:
lyt <- basic_table(inset = 5) %>% analyze("AGE") build_table(lyt, DM)
Using the post-processing function:
Without inset -
lyt <- basic_table() %>% analyze("AGE") tbl <- build_table(lyt, DM) tbl
With an inset of 5 characters -
table_inset(tbl) <- 5 tbl
Below is an example with a table produced for clinical data. Compare the inset of the table and main footer between the two tables.
Without inset -
analysisfun <- function(x, ...) { in_rows( row1 = 5, row2 = c(1, 2), .row_footnotes = list(row1 = "row 1 rfn"), .cell_footnotes = list(row2 = "row 2 cfn") ) } lyt <- basic_table( title = "Title says Whaaaat", subtitles = "Oh, ok.", main_footer = "ha HA! Footer!", prov_footer = "provenaaaaance" ) %>% split_cols_by("ARM") %>% analyze("AGE", afun = analysisfun) result <- build_table(lyt, ex_adsl) result
With inset -
Notice, the inset does not apply to any title materials (main title, subtitles, page titles), or provenance footer materials. Inset settings is applied to top-left materials, referential footnotes main footer materials and any horizontal dividers.
table_inset(result) <- 5 result
A character value can be specified to modify the horizontal separation between column headers and the table. Horizontal separation applies when:
Below, we replace the default line with "=".
tbl <- basic_table() %>% split_cols_by("Species") %>% add_colcounts() %>% analyze(c("Sepal.Length", "Petal.Width"), function(x) { in_rows( mean_sd = c(mean(x), sd(x)), var = var(x), min_max = range(x), .formats = c("xx.xx (xx.xx)", "xx.xxx", "xx.x - xx.x"), .labels = c("Mean (sd)", "Variance", "Min - Max") ) }) %>% build_table(iris, hsep = "=") tbl
A character value can be specified as a section divider which succeed every group defined by a split instruction. Note, a trailing divider at the end of the table is never printed.
Below, a "+" is repeated and used as a section divider.
lyt <- 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") ) }, section_div = "+") build_table(lyt, iris)
Section dividers can be set to " " to create a blank line.
lyt <- 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") ) }, section_div = " ") build_table(lyt, iris)
Separation characters can be specified for different row splits. However, only one will be printed if they "pile up" next to each other.
lyt <- basic_table() %>% split_cols_by("ARM") %>% split_rows_by("RACE", section_div = "=") %>% split_rows_by("STRATA1", section_div = "~") %>% analyze("AGE", mean, var_labels = "Age", format = "xx.xx") build_table(lyt, DM)
Tables by default have indenting at each level of splitting. A custom
indent value can be supplied with the indent_mod
argument within a
split function to modify this default. Compare the indenting of the
tables below:
Default Indent -
basic_table( title = "Study XXXXXXXX", subtitles = c("subtitle YYYYYYYYYY", "subtitle2 ZZZZZZZZZ"), main_footer = "Analysis was done using cool methods that are correct", prov_footer = "file: /path/to/stuff/that/lives/there HASH:1ac41b242a" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX") %>% split_rows_by("STRATA1") %>% analyze("AGE", mean, format = "xx.x") %>% build_table(DM)
Modified indent -
basic_table( title = "Study XXXXXXXX", subtitles = c("subtitle YYYYYYYYYY", "subtitle2 ZZZZZZZZZ"), main_footer = "Analysis was done using cool methods that are correct", prov_footer = "file: /path/to/stuff/that/lives/there HASH:1ac41b242a" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX", indent_mod = 3) %>% split_rows_by("STRATA1", indent_mod = 5) %>% analyze("AGE", mean, format = "xx.x") %>% build_table(DM)
With split instructions, visibility of the label for the variable
being split can be modified to visible
, hidden
and topleft
with
the show_labels
argument, label_pos
argument, and child_labels
argument where applicable. Note: this is NOT the name of the levels
contained in the variable. For analyze calls, \code{default} indicates
that the variable should be visible only if multiple variables are
analyzed at the same level of nesting.
Visibility of labels for the groups generated by a split can also be
modified using the child_label
argument with a split call. The
child_label
argument can force labels to be visible in addition to
content rows but we cannot hide or move the content rows.
Notice the placement of the "AGE" label in this example:
lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = "ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels, child_labels = "visible") %>% split_rows_by("STRATA1") %>% analyze("AGE", mean, show_labels = "default") build_table(lyt, DM)
When set to default, the label AGE
is not repeated since there is
only one variable being analyzed at the same level of
nesting. Override this by setting the show_labels
argument as
"visible".
lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = "ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels, child_labels = "hidden") %>% split_rows_by("STRATA1") %>% analyze("AGE", mean, show_labels = "visible") build_table(lyt2, DM)
Below is an example using the label_pos
argument for modifying label
visibility:
Label order will mirror the order of split_rows_by
calls. If the
labels of any subgroups should be hidden, the label_pos
argument
should be set to hidden.
"SEX" label position is hidden -
basic_table( title = "Study XXXXXXXX", subtitles = c("subtitle YYYYYYYYYY", "subtitle2 ZZZZZZZZZ"), main_footer = "Analysis was done using cool methods that are correct", prov_footer = "file: /path/to/stuff/that/lives/there HASH:1ac41b242a" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels, label_pos = "visible") %>% split_rows_by("STRATA1", label_pos = "hidden") %>% analyze("AGE", mean, format = "xx.x") %>% build_table(DM)
"SEX" label position is with the top-left materials -
basic_table( title = "Study XXXXXXXX", subtitles = c("subtitle YYYYYYYYYY", "subtitle2 ZZZZZZZZZ"), main_footer = "Analysis was done using cool methods that are correct", prov_footer = "file: /path/to/stuff/that/lives/there HASH:1ac41b242a" ) %>% split_cols_by("ARM") %>% split_rows_by("SEX", split_fun = drop_split_levels, label_pos = "topleft") %>% split_rows_by("STRATA1", label_pos = "hidden") %>% analyze("AGE", mean, format = "xx.x") %>% build_table(DM)
An rtable
can be rendered with a customized width by setting custom
rendering widths for cell contents, row labels, and titles/footers.
This is demonstrated using the sample data and table below. In this section we aim to render this table with a reduced width since the table has very wide contents in several cells, labels, and titles/footers.
trimmed_data <- ex_adsl %>% filter(SEX %in% c("M", "F")) %>% filter(RACE %in% levels(RACE)[1:2]) levels(trimmed_data$ARM)[1] <- "Incredibly long column name to be wrapped" levels(trimmed_data$ARM)[2] <- "This_column_name_should_be_split_somewhere" wide_tbl <- basic_table( title = "Title that is too long and also needs to be wrapped to a smaller width", subtitles = "Subtitle that is also long and also needs to be wrapped to a smaller width", main_footer = "Footnote that is wider than expected for this table.", prov_footer = "Provenance footer material that is also wider than expected for this table." ) %>% split_cols_by("ARM") %>% split_rows_by("RACE", split_fun = drop_split_levels) %>% analyze( c("AGE", "EOSDY"), na_str = "Very long cell contents to_be_wrapped_and_splitted", inclNAs = TRUE ) %>% build_table(trimmed_data) wide_tbl
In the following sections we will use the toString()
function to
render the table in string form. This resulting string representation
is ready to be printed or written to a plain text file, but we will
use the strsplit()
function in combination with the matrix()
function to preview the rendered wrapped table in matrix form within
this vignette.
The width of a rendered table can be customized by wrapping column
widths. This is done by setting custom width values via the widths
argument of the toString()
function. The length of the vector passed
to the widths
argument must be equal to the total number of columns
in the table, including the row labels column, with each value of the
vector corresponding to the maximum width (in characters) allowed in
each column, from left to right.
Similarly, wrapping can be applied when exporting a table via one of
the four export_as_*
functions and when implementing pagination via
the paginate_table()
function from the rtables
package. In these
cases, the rendered column widths are set using the colwidths
argument which takes input in the same format as the widths
argument
of toString()
.
For example, wide_tbl
has four columns (1 row label column and 3
content columns) which we will set the widths of below to use in the
rendered table. We set the width of the row label column to 10
characters and the widths of each of the 3 content columns to 8
characters. Any words longer than the specified width are broken and
continued on the following line. By default there are 3 spaces
separating each of the columns in the rendered table but this can be
customized via the col_gap
argument to toString()
if further width
customization is desired.
result_wrap_cells <- toString(wide_tbl, widths = c(10, 8, 8, 8)) matrix_wrap_cells <- matrix(strsplit(result_wrap_cells, "\n")[[1]], ncol = 1) matrix_wrap_cells
In the resulting output we can see that the table has been correctly rendered using wrapping with a total width of 43 characters, but that the titles and footers remain wider than the rendered table.
In addition to wrapping column widths, titles and footers can be
wrapped by setting tf_wrap = TRUE
in toString()
and setting the
max_width
argument of toString()
to the maximum width (in
characters) allowed for titles/footers. The four export_as_*
functions and paginate_table()
can also wrap titles/footers by
setting the same two arguments. In the following code, we set
max_width = 43
so that the rendered table and all of its annotations
have a maximum width of 43 characters.
result_wrap_cells_tf <- toString( wide_tbl, widths = c(10, 8, 8, 8), tf_wrap = TRUE, max_width = 43 ) matrix_wrap_cells_tf <- matrix(strsplit(result_wrap_cells_tf, "\n")[[1]], ncol = 1) matrix_wrap_cells_tf
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.