| Title: | A Grammar of Clinical Summary Tables |
| Version: | 0.2.0 |
| Description: | Implements a grammar of summary data for clinical reports. Clinical summary tables are decomposed into modular layers, each representing an independent summary block. Supports count, descriptive statistics, and shift layer types with a declarative spec-based API built on data.table for performance. |
| License: | MIT + file LICENSE |
| URL: | https://github.com/atorus-research/tplyr2, https://atorus-research.github.io/tplyr2/ |
| BugReports: | https://github.com/atorus-research/tplyr2/issues |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.1.0) |
| Imports: | data.table, jsonlite, purrr, rlang, stringr |
| Suggests: | testthat (≥ 3.0.0), withr, yaml, knitr, rmarkdown |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| LazyData: | true |
| Config/roxygen2/version: | 8.0.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-08-02 14:12:57 UTC; mstackhouse |
| Author: | Mike Stackhouse [aut, cre] |
| Maintainer: | Mike Stackhouse <mike.stackhouse@atorusresearch.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-08-08 12:00:02 UTC |
tplyr2: A Grammar of Clinical Summary Tables
Description
Implements a grammar of summary data for clinical reports. Clinical summary tables are decomposed into modular layers, each representing an independent summary block. Supports count, descriptive statistics, and shift layer types with a declarative spec-based API built on data.table for performance.
Author(s)
Maintainer: Mike Stackhouse mike.stackhouse@atorusresearch.com
Authors:
Mike Stackhouse mike.stackhouse@atorusresearch.com
See Also
Useful links:
Report bugs at https://github.com/atorus-research/tplyr2/issues
Attach single-proportion CI columns to a long count table
Description
Adds ci_lower/ci_upper (from n/total) and, when the distinct columns
are present, distinct_ci_lower/distinct_ci_upper (from
distinct_n/distinct_total). Bounds are stored on the percentage scale
(proportion times 100) to match the pct/distinct_pct statistics.
Usage
add_count_ci(dt, settings)
Arguments
dt |
A long count data.table (or NULL, a no-op) |
settings |
A tplyr_layer_settings object supplying |
Value
dt, modified in place
Conditional reformatting of a pre-populated string of numbers
Description
This function allows you to conditionally re-format a string of numbers based on a numeric value within the string itself. By selecting a "format group", which is targeting a specific number within the string, a user can establish a condition upon which a provided replacement string can be used. Either the entire replacement can be used to replace the entire string, or the replacement text can refill the "format group" while preserving the original width and alignment of the target string.
Usage
apply_conditional_format(
string,
format_group,
condition,
replacement,
full_string = FALSE
)
Arguments
string |
Target character vector where text may be replaced |
format_group |
An integer representing the targeted numeric field within the string, numbered from left to right |
condition |
An expression, using the variable name |
replacement |
A string to use as the replacement value |
full_string |
TRUE if the full string should be replaced, FALSE if the replacement should be done within the format group |
Value
A character vector
Examples
string <- c(" 0 (0.0%)", " 8 (9.3%)", "78 (90.7%)")
apply_conditional_format(string, 2, x == 0, " 0 ", full_string = TRUE)
apply_conditional_format(string, 2, x < 1, "(<1%)")
Apply count format(s) to a long counts table
Description
Single-format mode (unnamed) writes a single formatted column.
Stat-columns mode (named formats) writes one formatted_<i> column per
format; cast_to_wide()
spreads these into separate res columns per column group. All count
statistics are already present on dt (including the special total and
missing row tables), so every format can be applied to every row.
Usage
apply_count_formats(
dt,
fmts,
pct_lt = NULL,
pct_gt = NULL,
zero_count_display = "full"
)
Arguments
dt |
data.table with computed count statistics (or NULL, a no-op) |
fmts |
List of f_str objects from |
pct_lt |
Optional numeric less-than threshold for percents (see |
pct_gt |
Optional numeric greater-than threshold for percents |
zero_count_display |
One of "full" (default, unchanged), "count_only" (zero cells show just the count field, e.g. " 0"), or "blank" (zero cells render as "") |
Details
The pct_lt/pct_gt and zero_count_display arguments implement the
regulatory display conventions from issue #14. pct_lt/pct_gt retarget
the percent statistic (the pct/distinct_pct format group) to the
"<1"/">99" tokens. zero_count_display rewrites cells whose count is zero.
Apply custom column groups to data
Description
Duplicates rows matching source levels with the column variable set to the custom group name.
Usage
apply_custom_groups(dt, custom_groups)
Arguments
dt |
data.table |
custom_groups |
List of tplyr_custom_group objects (or NULL) |
Value
Modified data.table
Drop missing-counted rows from the denominator source
Description
Implements missing_count$denom_exclude: the rows folded into the
Missing row leave the denominator, so the layer's percentages are of the
non-missing population rather than of everyone.
Usage
apply_denom_exclude(denom_dt, tv, missing_count, layer_index = NULL)
Arguments
denom_dt |
data.table used as the denominator source |
tv |
Character string, target variable name |
missing_count |
The layer's |
layer_index |
Integer layer index, used in the warning message |
Value
denom_dt, filtered when denom_exclude is TRUE
Apply format strings to numeric values
Description
Vectorized formatting function. Takes an f_str object and numeric vectors, returns a character vector of formatted strings.
Usage
apply_formats(
fmt,
...,
precision = NULL,
lt = NULL,
gt = NULL,
lt_gt_group = NULL,
na = NULL,
width = NULL,
pad = c("right", "left")
)
Arguments
fmt |
An |
... |
Numeric vectors, one per variable in the f_str (positional matching) |
precision |
Optional list of resolved precision per group (for auto-precision) |
lt |
Optional numeric less-than threshold applied to the group named by
|
gt |
Optional numeric greater-than threshold applied to the group named by
|
lt_gt_group |
Optional integer index of the format group to which |
na |
Optional string substituted for cells whose format-group inputs are
all NA, used instead of the default blank-width fill. |
width |
Optional integer total width to pad each formatted token to,
using |
pad |
Side to pad on when |
Value
Character vector of formatted values
See Also
f_str() for the format-string grammar.
Examples
# Vectorized: one formatted string per element
apply_formats(f_str("xx (xx.x%)", "n", "pct"),
n = c(5, 12, 103), pct = c(4.5, 33.333, 99.9))
# `na` replaces the default blank-width fill
apply_formats(f_str("xx.x", "mean"), mean = c(1.2, NA))
apply_formats(f_str("xx.x", "mean"), mean = c(1.2, NA), na = "NE")
apply_formats(f_str("xx.x", "mean"), mean = c(1.2, NA), na = "")
# lt/gt thresholds, targeting the percent group (index 2)
apply_formats(f_str("xx (xx.x%)", "n", "pct"), n = c(1, 199), pct = c(0.4, 99.7),
lt = 1, gt = 99, lt_gt_group = 2)
# Pad to a fixed width for row-binding against other output
apply_formats(f_str("xx.x", "mean"), mean = c(1.2, 10.75), width = 10)
apply_formats(f_str("xx.x", "mean"), mean = c(1.2, 10.75), width = 10, pad = "left")
Apply build-time overrides to a spec
Description
Merges override parameters into a copy of the spec. Handles special cases:
-
where: If character, parsed to an expression viarlang::parse_expr(). -
pop_data: If a list, sub-fields are merged into the existing pop_data config rather than replacing it entirely.
Usage
apply_overrides(spec, overrides)
Arguments
spec |
A tplyr_spec object |
overrides |
Named list of override values |
Value
Modified spec (shallow copy)
Apply precision caps
Description
Applies layer-level cap first, falls back to global option.
Usage
apply_precision_cap(prec, precision_cap = NULL)
Arguments
prec |
data.table with max_int and max_dec columns |
precision_cap |
Named numeric vector c(int=, dec=) or NULL |
Value
Modified data.table
Apply row masks to blank repeated row labels
Description
Walks each rowlabel* column top-to-bottom and blanks values that
are identical to the previous row, respecting layer boundaries
(ord_layer_index).
Usage
apply_row_masks(result, row_breaks = FALSE)
Arguments
result |
A data.frame produced by |
row_breaks |
Logical. If TRUE, insert a blank row between layers. |
Value
A data.frame with repeated labels blanked
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1", by = "SEX"))
)
built <- tplyr_build(spec, tplyr_adsl)
# SEX repeats on every age-group row
built[, c("rowlabel1", "rowlabel2")]
# Masked: each value prints once, at the top of its block
apply_row_masks(built)[, c("rowlabel1", "rowlabel2")]
# row_breaks inserts a blank row between layers
two <- tplyr_build(
tplyr_spec(cols = "TRT01P",
layers = tplyr_layers(group_count("SEX"), group_count("AGEGR1"))),
tplyr_adsl
)
apply_row_masks(two, row_breaks = TRUE)[, c("rowlabel1", "res1")]
Apply total groups to data
Description
Duplicates all rows with the column variable set to the total group label, creating a synthetic "Total" column level.
Usage
apply_total_groups(dt, total_groups)
Arguments
dt |
data.table |
total_groups |
List of tplyr_total_group objects (or NULL) |
Value
Modified data.table
Extract a display-ready frame from a build result
Description
Returns just the display content of a tplyr_build result,
dropping the internal ordering helpers (ord_layer_index,
ord_layer_*) and the row_id metadata column, and giving a frame
ready to hand to a table-rendering package (clinify, flextable, gt, ...). The
build output is already ordered, so no re-sorting is performed.
Usage
as_display(x, labels = FALSE)
Arguments
x |
A data.frame produced by |
labels |
Logical. When |
Details
Everything that is not an internal helper is kept. That is normally the
rowlabel*, res*, rdiff*, and pval* columns, but a
stats_as_columns desc layer with no by variable names its result
columns after the statistics themselves, and those are retained too.
Value
A data.frame of display columns.
Examples
spec <- tplyr_spec(cols = "TRT", layers = tplyr_layers(group_count("SEX")))
b <- tplyr_build(spec, data.frame(TRT = rep(c("A", "B"), 3),
SEX = rep(c("F", "M"), 3)))
as_display(b)
Association-test column(s) for count, shift, and desc layers
Description
Configures an association test and lands its formatted result beside the
n(\
comparisons is supplied:
Usage
assoc_test(
fn,
format = f_str("x.xxx", "p"),
label = NULL,
reference = NULL,
comparisons = NULL,
total_row = TRUE
)
Arguments
fn |
A function of one argument. In omnibus mode it is called with the
source-data subset (a data.frame) for a single |
format |
An |
label |
Character string used as the result column's header label. In
pairwise mode it may be a vector with one entry per comparison (or a single
value recycled across comparisons); |
reference |
Pairwise mode only. Character(1) naming the reference arm
level of the first |
comparisons |
Pairwise mode only. A character vector (or list of single
levels) of arm levels, each compared to |
total_row |
Pairwise mode only. Logical(1); when the layer also emits a
total row ( |
Details
Omnibus mode (comparisons = NULL, the default). Runs
fn once per by group, across the treatment columns, and
lands its result as a single trailing column. The supplied function receives
the raw source-data subset for the by group (all cols levels
and all target/row levels), so a caller can tabulate and test naturally
(e.g. fisher.test(table(.data$TRT, .data$RESP)) or
coin::cmh_test(...)). When the layer has no by variable the
test runs once over the whole layer; otherwise once per by group, with
the value placed on that group's first output row. This mode works on
count, shift, and desc layers – on a desc layer
it is the natural home for a continuous-variable comparison across arms
(ANOVA / Kruskal-Wallis / t-test), e.g.
fn = function(.data) anova(lm(AGE ~ TRT, .data))[["Pr(>F)"]][1], with
one p-value per by group on that group's first statistic row.
Pairwise / per-level mode (comparisons non-NULL).
Count layers only. Emits one pval column per comparison, each
comparing an arm level of the first cols variable to reference,
with a value on every target-level row (like risk_diff's
rdiff columns). On a nested count layer it emits a value on
every row of every level – each inner (e.g. preferred-term) row and each
outer (e.g. system-organ-class subtotal) row, each row's 2x2 built from that
row's own counts – and, when the layer has a total row, on the grand-total
row too (see total_row). Here fn receives, for one (row,
comparison) pair, a 2x2 contingency matrix
matrix(c(n_ref, n_cmp, N_ref - n_ref, N_cmp - n_cmp), nrow = 2) –
rows are (reference, comparison) arm, columns are (event, no event) – where
n is the cell count and N the population denominator for that
arm. When the layer sets distinct_by, the distinct counts/denominators
are used. fn returns either a numeric value (a scalar, or a vector of
several statistics matching a multi-variable format) or a verbatim
character display string (NA renders a blank).
Attach it to a layer via
layer_settings(assoc_test = assoc_test(...)).
Value
A tplyr_assoc_test object.
Examples
# Omnibus
at <- assoc_test(
fn = function(.data) fisher.test(table(.data$TRT, .data$RESP))$p.value,
format = f_str("x.xxx", "p"),
label = "p-value [1]"
)
# Pairwise per-level (count layer): Fisher on an incidence 2x2
at2 <- assoc_test(
fn = function(m) fisher.test(m)$p.value,
reference = "Placebo",
comparisons = c("Low", "High"),
format = f_str("x.xxx", "p")
)
# Omnibus on a desc layer: continuous comparison across arms (ANOVA)
at3 <- assoc_test(
fn = function(.data) anova(lm(AGE ~ TRT, .data))[["Pr(>F)"]][1],
format = f_str("x.xxx", "p")
)
# Multiple statistics in one cell: odds ratio with a confidence interval
at4 <- assoc_test(
fn = function(m) {
ft <- fisher.test(m)
c(ft$estimate, ft$conf.int[1], ft$conf.int[2])
},
reference = "Placebo",
comparisons = c("Low", "High"),
format = f_str("xx.xx (xx.xx, xx.xx)", "or", "lo", "hi"),
label = "OR (95% CI)"
)
Process a custom analysis layer
Description
Calls the user-provided analyze_fn for each group combination
and formats results using either format_strings or a pre-formatted
column.
Usage
build_analyze_layer(dt, layer, cols, layer_index, col_n = NULL, pop_dt = NULL)
Arguments
dt |
data.table with the (filtered) input data |
layer |
A tplyr_analyze_layer object |
cols |
Character vector of column variable names from the spec |
layer_index |
Integer index of this layer |
col_n |
data.table with column counts (or NULL) |
pop_dt |
data.table with population data (or NULL) |
Value
A data.table with rowlabel*, res*, and ord* columns
Build cell-level metadata for the full output table
Description
For each output row x result column combination, constructs filter expressions that describe the source data subset for that cell. The expressions can be evaluated at query time against the original data.
Usage
build_cell_metadata(output, spec, col_names, pop_col_map = NULL)
Arguments
output |
data.frame output from tplyr_build (with rowlabel/res/ord cols) |
spec |
tplyr_spec object |
col_names |
Character vector of original data column names |
Value
Named list of tplyr_meta objects, keyed by "row_id||column"
Build column labels with header N suffix
Description
Takes raw dcast column names and a col_n data.table, returns labels
with "(N=<n>)" suffix. For shift layers where the label includes both
spec-level cols and the shift column variable, only the spec-level portion
is used for the N lookup.
Usage
build_col_labels(raw_labels, col_n)
Arguments
raw_labels |
Character vector of raw column labels from dcast |
col_n |
data.table with spec-level column variables and .n column, or NULL (in which case labels are returned unchanged) |
Value
Character vector of labels with N suffix
Process a count layer
Description
Process a count layer
Usage
build_count_layer(
dt,
layer,
cols,
layer_index,
col_n = NULL,
pop_dt = NULL,
col_levels = NULL
)
Arguments
dt |
data.table with the (filtered) input data |
layer |
A tplyr_count_layer object |
cols |
Character vector of column variable names from the spec |
layer_index |
Integer index of this layer |
Value
A data.table with rowlabel*, res*, and ord* columns
Process a nested (multi-variable) count layer
Description
Process a nested (multi-variable) count layer
Usage
build_count_layer_nested(
dt,
target_vars,
cols,
by_data_vars,
by_labels,
settings,
layer_index,
col_n = NULL,
pop_dt = NULL,
col_levels = NULL
)
Process a single-variable count layer
Description
Process a single-variable count layer
Usage
build_count_layer_single(
dt,
tv,
cols,
by_data_vars,
by_labels,
settings,
layer_index,
col_n = NULL,
pop_dt = NULL,
col_levels = NULL
)
Process a descriptive statistics layer
Description
Process a descriptive statistics layer
Usage
build_desc_layer(
dt,
layer,
cols,
layer_index,
col_n = NULL,
pop_dt = NULL,
col_levels = NULL
)
Arguments
dt |
data.table with the (filtered) input data |
layer |
A tplyr_desc_layer object |
cols |
Character vector of column variable names from the spec |
layer_index |
Integer index of this layer |
Value
A data.table with rowlabel*, res*, and ord* columns
Build a multi-target desc layer
Description
Build a multi-target desc layer
Usage
build_desc_multi(
dt,
target_vars,
cols,
by_data_vars,
by_labels,
settings,
layer_index,
col_n,
pop_dt = NULL,
col_levels = NULL
)
Build a single-target desc layer
Description
Build a single-target desc layer
Usage
build_desc_single(
dt,
tv,
cols,
by_data_vars,
by_labels,
settings,
layer_index,
col_n,
var_label = NULL,
var_index = NULL,
pop_dt = NULL,
col_levels = NULL
)
Build row labels for a nested count level
Description
Build row labels for a nested count level
Usage
build_nested_row_labels(
counts,
by_labels,
by_data_vars,
target_vars,
level,
n_levels
)
Build row labels for special rows (total/missing) in nested context
Description
Build row labels for special rows (total/missing) in nested context
Usage
build_nested_row_labels_special(
dt,
by_labels,
by_data_vars,
target_vars,
tv,
n_label_cols
)
Write rowlabel columns onto a long-format layer table
Description
Writes one rowlabel<n> column per by label constant, then one per by
data variable (as character), then the value variable (as character).
Usage
build_row_labels_long(dt, by_labels, by_data_vars, value_var)
Arguments
dt |
data.table to modify by reference |
by_labels |
Character vector of constant |
by_data_vars |
Character vector of |
value_var |
Name of the variable supplying the final rowlabel column. ARD reconstruction can encounter a target variable absent from the stats table; the column is skipped (but still named) in that case. |
Value
Character vector of the rowlabel column names
Build row labels for special rows (missing, total) in single-variable context
Description
Build row labels for special rows (missing, total) in single-variable context
Usage
build_row_labels_special(dt, by_labels, by_data_vars, tv, existing_label_cols)
Process a shift layer
Description
Builds a cross-tabulation (row variable × column variable) within each treatment arm. The shift column variable becomes an additional column dimension, producing result columns like "Placebo_H", "Placebo_N", etc.
Usage
build_shift_layer(
dt,
layer,
cols,
layer_index,
col_n = NULL,
pop_dt = NULL,
col_levels = NULL
)
Arguments
dt |
data.table with the (filtered) input data |
layer |
A tplyr_shift_layer object |
cols |
Character vector of column variable names from the spec |
layer_index |
Integer index of this layer |
Value
A data.table with rowlabel*, res*, and ord* columns
Map data variable names to their rowlabel columns
Description
Map data variable names to their rowlabel columns
Usage
build_var_to_rowlabel_map(layer, by_data_vars, by_labels)
Arguments
layer |
A tplyr_layer object |
by_data_vars |
Character vector of by-variable data column names |
by_labels |
Character vector of by-variable label strings |
Value
Named list where names are data variables and values are rowlabel
column names (e.g., list(SEX = "rowlabel1"))
Call analyze_fn for each group combination
Description
Call analyze_fn for each group combination
Usage
call_analyze_fn_by_group(dt, analyze_fn, target_var, group_vars)
Cast long data to wide output format
Description
When stat_labels is provided (stat_columns mode), the long data carries
one formatted_<i> column per statistic and each column group spreads
into one res column per statistic, interleaved column-group-major. Column
labels follow the pattern "<column group> (N=n) | <stat label>" so
renderers can span the column group over its stat sub-columns.
Usage
cast_to_wide(
dt,
row_label_cols,
cols,
layer_index,
col_n = NULL,
stat_labels = NULL,
col_levels = NULL,
row_order_col = NULL
)
Arguments
stat_labels |
Character vector of stat column labels (the names of
the |
col_levels |
Named list mapping factor column variables to their level
order (from |
row_order_col |
Name of a numeric column in |
Warn when a nonzero count has a missing or zero denominator
Description
Denominators are attached with a left join, so a group present in the
analysis data but absent from the denominator source comes back with
total = NA and renders a blank-width percent (" 5 ( )") —
and total == 0 with n > 0 used to display an affirmatively
wrong 0.0%. Both mean the denominator setup is wrong, so say so.
Usage
check_denominator_integrity(counts, n_col, total_col, group_cols, layer_index)
Arguments
counts |
data.table holding the counts and their denominators |
n_col |
Name of the count column ( |
total_col |
Name of the denominator column |
group_cols |
Columns identifying a row, used to name offending groups |
layer_index |
Integer layer index |
Value
Invisible TRUE
Classify by values into data variables and labels
Description
Classify by values into data variables and labels
Usage
classify_by(by, col_names)
Collapse row labels into a single column
Description
This is a generalized post processing function that allows you to take groups of by variables and collapse them into a single column. Repeating values are split into separate rows, and for each level of nesting, a specified indentation level can be applied.
Usage
collapse_row_labels(
x,
...,
indent = " ",
target_col = "row_label",
nest = FALSE
)
Arguments
x |
Input data frame |
... |
Column names (as character strings) to be collapsed, must be 2 or more |
indent |
Indentation string to be used, which is multiplied at each indentation level |
target_col |
Character string naming the output column containing collapsed row labels |
nest |
Logical. If TRUE, collapse row labels in-place without inserting stub rows for repeating values. Allows a single column to be passed. Default is FALSE. |
Value
data.frame with row labels collapsed into a single column
Examples
x <- data.frame(
row_label1 = c("A", "A", "A", "B", "B"),
row_label2 = c("C", "C", "D", "E", "F"),
var1 = 1:5,
stringsAsFactors = FALSE
)
collapse_row_labels(x, "row_label1", "row_label2")
collapse_row_labels(x, "row_label1", "row_label2", indent = " ",
target_col = "rl")
Collect precision from data
Description
Scans a numeric variable to determine the maximum integer width and maximum decimal precision present in the data, optionally grouped.
Usage
collect_precision(
dt,
precision_on,
precision_by = character(0),
precision_data = NULL,
precision_cap = NULL
)
Arguments
dt |
data.table with the data |
precision_on |
Character string naming the variable to scan |
precision_by |
Character vector of grouping variables (can be empty) |
precision_data |
Optional external data.frame with pre-computed precision |
precision_cap |
Named numeric vector c(int=, dec=) for capping |
Value
data.table with precision_by columns + "max_int" and "max_dec" columns
Compute max integer width and max decimal precision from a numeric vector
Description
Compute max integer width and max decimal precision from a numeric vector
Usage
collect_precision_values(vals)
Arguments
vals |
Numeric vector (should already have NA/Inf removed) |
Value
list(max_int, max_dec)
Evaluate a build, reporting any user-code failures as one warning
Description
Evaluate a build, reporting any user-code failures as one warning
Usage
collect_user_fn_errors(expr)
Arguments
expr |
Expression to evaluate |
Value
The value of expr
Complete count data to ensure all combinations exist
Description
Complete count data to ensure all combinations exist
Usage
complete_counts(
counts,
dt,
cols,
by_data_vars,
tv,
limit_data_by = NULL,
denom_group = NULL,
col_levels = NULL
)
Complete counts for a nested level
Description
Complete counts for a nested level
Usage
complete_nested_level(
counts,
dt,
cols,
by_data_vars,
level_tvs,
limit_data_by = NULL,
denom_group = NULL,
col_levels = NULL
)
Complete shift count data
Description
Ensures all row_var × col_var × cols combinations exist, filling missing combinations with zero counts.
Usage
complete_shift_counts(
counts,
dt,
all_cols,
by_data_vars,
row_var,
denom_group = NULL,
col_levels = NULL
)
Compute the association-test result per by-group
Description
Runs config$fn once per by group over the source-data subset
for that group, and returns the display string keyed by the by variables.
Usage
compute_assoc_test(source_dt, by_data_vars, config)
Arguments
source_dt |
data.table of source rows for the layer (after any layer
|
by_data_vars |
Character vector of by data-variable names (may be empty). |
config |
A |
Value
A data.table with the by_data_vars columns (as character) plus
a formatted character column .assoc_p. When by_data_vars is
empty, a single-row table with only .assoc_p.
Compute sort keys for count layer rows
Description
Computes ordering columns for a count layer's rows, based on the by-variable and target variable values.
Usage
compute_count_sort_keys(counts, dt, cols, by_data_vars, tv, settings)
Arguments
counts |
data.table with count data (long format, before cast) |
dt |
data.table with the original input data (for VARN lookup) |
cols |
Character vector of spec column variables |
by_data_vars |
Character vector of by data variable names |
tv |
Character string, target variable name |
settings |
Layer settings object |
Value
The counts data.table with .ord_by_* and .ord_tv columns added
Compute missing count row
Description
Compute missing count row
Usage
compute_missing_counts(
dt,
counts,
cols,
by_data_vars,
tv,
group_vars,
denom_group,
denom_dt,
distinct_by,
missing_count
)
Compute missing subjects row
Description
Counts subjects present in pop_data but absent from target data.
Uses distinct_by to identify subjects; if NULL, uses row-level counts.
Usage
compute_missing_subjects(
dt,
pop_dt,
cols,
by_data_vars,
tv,
distinct_by,
missing_label,
denom_group,
denom_dt,
fmt
)
Arguments
dt |
data.table with target data (after layer where filter) |
pop_dt |
data.table with population data |
cols |
Character vector of spec column variable names |
by_data_vars |
Character vector of by-variable names from data |
tv |
Character string, target variable name |
distinct_by |
Character string naming the subject identifier (or NULL) |
missing_label |
Character string for the row label |
denom_group |
Character vector of denominator grouping variables |
denom_dt |
data.table for denominator computation |
fmt |
f_str object for formatting |
Value
A data.table for the missing subjects row, or NULL
Compute pairwise per-level association-test p-values from a counts table
Description
For each comparison arm and each target-variable level, builds a 2x2
contingency matrix from the assembled cell counts and population
denominators and calls config$fn to obtain a scalar p-value. This
mirrors compute_risk_diff() in placement (a value per target level per
comparison) but delegates the test to the caller-supplied function.
Usage
compute_pairwise_assoc(
counts_long,
cols,
tv,
by_data_vars,
distinct_by,
config,
reference
)
Arguments
counts_long |
data.table (pre-formatting) with the column variable, any
|
cols |
Character vector of column variable names from the spec. |
tv |
Character string naming the target variable. |
by_data_vars |
Character vector of by-variable names. |
distinct_by |
Distinct-by variable name (or NULL); selects the distinct counts/denominators when non-NULL. |
config |
A |
reference |
Character(1) resolved reference arm level. |
Value
A data.table with one row per target level per comparison, holding
the row variables, .comp_idx, and the formatted display string
.disp. Numeric fn returns are formatted with
config$format; a character fn return is passed through
verbatim (issue #47); NA and a zero denominator render a blank.
Compute pairwise per-level association-test p-values for a nested layer
Description
Like compute_pairwise_assoc() but keyed directly by the assembled
rowlabel* columns rather than a single target variable, so it works at
every nesting level at once: each inner (e.g. preferred-term) row and each
outer (e.g. system-organ-class subtotal) row is one rowlabel tuple,
and its 2x2 is built from that row's own reference/comparison counts and
population denominators. The same helper computes the grand-total row's
p-value when passed the total-row table (a single rowlabel tuple).
Usage
compute_pairwise_assoc_nested(
long,
cols,
row_label_cols,
distinct_by,
config,
reference,
arm_n = NULL
)
Arguments
long |
data.table holding the column variable, the assembled
|
cols |
Character vector of column variable names from the spec. |
row_label_cols |
Character vector of the |
distinct_by |
Distinct-by variable name (or NULL); selects the distinct counts/denominators when non-NULL. |
config |
A |
reference |
Character(1) resolved reference arm level. |
arm_n |
Named numeric of population arm sizes (arm level -> N), used to
back-fill the 2x2 denominator for an arm that has no events on a row (or no
events at all). Without it, a zero-event reference or comparison arm would
have a missing denominator and blank the test; with it, an empty arm still
yields a valid |
Value
A data.table with the row_label_cols (as character),
.comp_idx, and the display string .disp; one row per output
row per comparison.
Compute risk differences for count layer data
Description
For each comparison pair and each target variable level, computes the
difference in proportions with a confidence interval using
stats::prop.test().
Usage
compute_risk_diff(counts_long, cols, tv, by_data_vars, risk_diff_config)
Arguments
counts_long |
data.table in long format with n, total columns |
cols |
Character vector of column variable names from the spec |
tv |
Character string naming the target variable |
by_data_vars |
Character vector of by-variable names |
risk_diff_config |
List with comparisons, ci, and format |
Value
A data.table with one row per target_var level per comparison, containing rdiff, lower, upper, p_value columns
Compute total row
Description
Compute total row
Usage
compute_total_row(
counts,
dt,
cols,
by_data_vars,
tv,
total_label,
total_missings,
distinct_by,
missing_count,
denom_group,
denom_dt
)
Compute sort key for a variable
Description
Returns an integer or numeric vector of sort keys for the values of a variable.
Priority: factor levels > VARN companion column > alphabetical.
The method parameter can override this auto-detection.
Usage
compute_var_order(
values,
var_name = NULL,
data_dt = NULL,
method = NULL,
count_values = NULL
)
Arguments
values |
Character or factor vector of values to sort |
var_name |
Character string naming the variable (for VARN lookup) |
data_dt |
data.table with the raw data (for VARN lookup) |
method |
Character: NULL (auto), "byfactor", "byvarn", "bycount", "alphabetical" |
count_values |
Numeric vector of counts per row (for bycount method) |
Value
Numeric vector of sort keys (lower = earlier)
Create a custom column group configuration
Description
Combines existing column levels into a custom group. Rows matching any of the source levels are duplicated with the column variable set to the group name.
Usage
custom_group(col_var, ...)
Arguments
col_var |
Character string naming the column variable |
... |
Named arguments where names are group labels and values are
character vectors of source levels to combine.
Example: |
Value
A tplyr_custom_group object
Examples
# Pool the two dose arms into one "Xanomeline (All)" column, kept alongside
# the arms it is built from
spec <- tplyr_spec(
cols = "TRT01P",
custom_groups = list(custom_group(
"TRT01P",
"Xanomeline (All)" = c("Xanomeline High Dose", "Xanomeline Low Dose")
)),
layers = tplyr_layers(group_count("AGEGR1"))
)
tplyr_build(spec, tplyr_adsl)
# Several groups at once
custom_group(
"TRT01P",
"Active" = c("Xanomeline High Dose", "Xanomeline Low Dose"),
"Control" = "Placebo"
)
Deserialize by parameter
Description
Deserialize by parameter
Usage
deserialize_by(raw)
Deserialize expression
Description
Deserialize expression
Usage
deserialize_expr(raw)
Deserialize f_str from raw list
Description
Deserialize f_str from raw list
Usage
deserialize_f_str(raw)
Deserialize a function
Description
Deserialize a function
Usage
deserialize_function(raw)
Deserialize a layer from raw list
Description
Deserialize a layer from raw list
Usage
deserialize_layer(raw_layer, layer_id = NULL)
Arguments
raw_layer |
Named list of parsed layer fields |
layer_id |
Human-readable layer identifier used in warning messages |
Deserialize layer settings
Description
Deserialize layer settings
Usage
deserialize_settings(raw, layer_id = NULL)
Arguments
raw |
Named list of parsed settings |
layer_id |
Human-readable layer identifier used in warning messages |
Drop target-variable levels that were folded into the Missing row
Description
Values named in missing_count$missing_values are counted in the Missing
row. Without removing them here they would also keep their own category row,
counting the same records twice and pushing the column past 100%.
Usage
drop_missing_value_levels(counts, tv, missing_count)
Arguments
counts |
data.table of category counts |
tv |
Character string, target variable name |
missing_count |
The layer's |
Value
counts with the folded-in levels removed
Create a format string object
Description
Create a format string object
Usage
f_str(format_string, ..., empty = NULL)
Arguments
format_string |
Character string defining the display template |
... |
Character strings naming the variables that populate the template |
empty |
Value to display when data is NA/missing. Supplied as
|
Details
Each run of x characters is one format group, and each group is filled by
the correspondingly-positioned variable in .... The count of xs sets the
field width, so "xx.x" renders two integer digits and one decimal. Literal
text between groups is preserved verbatim. a (and A) request
auto-precision, where the decimal count comes from the data.
Value
A tplyr_f_str object
See Also
apply_formats() to render values outside a build.
Examples
# Two format groups filled by n and pct
fmt <- f_str("xx (xx.x%)", "n", "pct")
fmt
apply_formats(fmt, n = c(5, 12), pct = c(4.5, 33.33))
# Width is set by the number of x's
apply_formats(f_str("xxx", "n"), n = 7)
apply_formats(f_str("x", "n"), n = 7)
# `empty` fills each NA group in place, preserving alignment
apply_formats(f_str("xx (xxx)", "n", "pct", empty = "NA"),
n = NA, pct = NA)
# `.overall` replaces the whole cell, but only when every group is NA
both_na <- f_str("xx (xxx)", "n", "pct", empty = c(.overall = "Not est."))
apply_formats(both_na, n = NA, pct = NA)
# Used in a layer
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1", settings = layer_settings(
format_strings = list(n_counts = f_str("xx (xx.x%)", "n", "pct"))))
)
)
tplyr_build(spec, tplyr_adsl)
Zero-fill synthetic markers after a bind
Description
rbindlist(fill = TRUE) sets a marker absent from one side to NA; those rows
are originals for that variable.
Usage
fill_synth_markers(dt)
Arguments
dt |
data.table |
Value
dt
Format analyze results using format_strings
Description
For each group combination, takes the first row of numeric values from the analyze_fn output and creates one formatted row per format_string entry.
Usage
format_analyze_results(fn_combined, format_strings, group_vars)
Arguments
fn_combined |
data.table of raw analyze_fn results |
format_strings |
Named list of f_str objects |
group_vars |
Character vector of grouping column names |
Value
data.table with row_label and formatted columns
Render an assoc_test fn return value for display
Description
Turns the value returned by a caller-supplied fn into the string shown
in the pval cell:
a numeric (or logical) vector whose length equals the number of variables in
formatis mapped positionally onto the format and rendered into one cell – a scalar with a one-variable format (a p-value), or several values with a multi-variable format (issue #60), e.g. an odds ratio with a confidence interval. An all-NAreturn (or an arity that does not match the format) renders a blank; a singleNAfield within a longer return blanks just that field via thef_strgrammar;a length-1 character is passed through verbatim (issue #47), so a caller computing an arbitrary test can also supply the finished display (significance flags,
">.99"/"<.0001"ceilings,"NE"sentinels, trailing-space alignment);NA_character_renders a blank;anything else (non-atomic, mismatched length) renders a blank.
Usage
format_assoc_return(raw, format, label = "assoc_test fn", group = NULL)
Arguments
raw |
The raw value returned by |
format |
An |
label |
What produced |
group |
Optional group identifier used when reporting a shape mismatch. |
Value
A length-1 character display string.
Render a data.table .BY group as a human-readable label
Description
Render a data.table .BY group as a human-readable label
Usage
format_group_label(by_values)
Arguments
by_values |
Named list of group values (data.table's |
Value
Single string, or NULL when there is no grouping
Format a numeric vector to a fixed-width field
Description
Format a numeric vector to a fixed-width field
Usage
format_number_vec(values, group, precision = NULL, lt = NULL, gt = NULL)
Arguments
values |
Numeric vector to format |
group |
A parsed format group (from |
precision |
Optional resolved precision (int_width/dec_width) overriding the group's static widths |
lt |
Optional numeric less-than threshold. Values strictly greater than 0
whose rounded display would fall below |
gt |
Optional numeric greater-than threshold. Values strictly less than
100 whose rounded display would exceed |
Format risk difference values
Description
Applies an f_str format to the computed risk difference data.
Usage
format_risk_diff(rd_data, fmt)
Arguments
rd_data |
data.table with rdiff, lower, upper, p_value columns |
fmt |
An f_str object for formatting |
Value
Character vector of formatted risk difference strings
Format values with auto-precision
Description
Handles the split-apply-combine when precision_by creates multiple precision groups. For a single precision group (no precision_by), resolves precision once and formats all rows.
Usage
format_with_precision(
fmt,
var_names,
stats,
group_vars,
precision_table,
precision_by
)
Arguments
fmt |
An f_str object |
var_names |
Character vector of variable names to format |
stats |
data.table with computed statistics |
group_vars |
Character vector of grouping column names |
precision_table |
data.table from collect_precision() |
precision_by |
Character vector of precision grouping variables (or NULL) |
Value
Character vector of formatted values
Generate unique row IDs for output rows
Description
Creates a character ID for each row by combining the layer index and
row label values. These IDs can be used with tplyr_meta_result()
and tplyr_meta_subset() to look up cell metadata.
Usage
generate_row_ids(result)
Arguments
result |
A data.frame produced by |
Value
Character vector of row IDs (same length as nrow(result))
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1"))
)
built <- tplyr_build(spec, tplyr_adsl)
generate_row_ids(built)
# IDs are derived from the row labels, so generate them from an unmodified
# build. tplyr_build(metadata = TRUE) attaches a row_id column that survives
# post-processing, which is the safer route.
with_meta <- tplyr_build(spec, tplyr_adsl, metadata = TRUE)
with_meta$row_id
Ordered factor levels for the column variable(s)
Description
Returns a named list mapping each cols variable that is a factor in
source_dt to its level order. Non-factor column variables are omitted.
Used to preserve the column variable's factor-level order through the
dcast() in cast_to_wide() (issue #13), so count/shift/desc layers all
order their res* columns by factor levels rather than alphabetically.
Usage
get_col_levels(source_dt, cols, complete = FALSE)
Arguments
source_dt |
data.table with the original (factor-typed) input data |
cols |
Character vector of column variable names |
Get the count format string, falling back to defaults
Description
Get the count format string, falling back to defaults
Usage
get_count_format(settings)
Resolve the list of count formats for a layer
Description
Returns the stat_columns list when set (one result column per format
per column group); otherwise a single-element unnamed list wrapping the
single default format so callers can treat both modes uniformly. The
presence of names on the result signals stat-columns mode downstream.
Usage
get_count_formats(settings)
Extract variable labels from a data.frame
Description
Returns a named character vector of variable labels. Labels are extracted
from the "label" attribute of each column (standard for Haven-imported
CDISC data).
Usage
get_data_labels(data)
Arguments
data |
A data.frame |
Value
Named character vector where names are column names and values are
labels. Columns without labels return NA_character_.
Examples
# CDISC data imported via haven carries a "label" attribute per column
labs <- get_data_labels(tplyr_adsl)
head(labs)
# Columns with no label attribute come back NA
get_data_labels(data.frame(a = 1, b = 2))
Get descriptive format strings, falling back to defaults
Description
Get descriptive format strings, falling back to defaults
Usage
get_desc_formats(settings)
Resolve denominator group for a nesting level
Description
Resolve denominator group for a nesting level
Usage
get_nested_denom_group(denoms_by, level, cols)
Create a custom analysis layer
Description
Allows a user-defined function to compute summary statistics. The function receives a data subset and the target variable name for each group combination, and returns a data.frame of results.
Usage
group_analyze(
target_var,
by = NULL,
where = NULL,
analyze_fn,
settings = layer_settings()
)
Arguments
target_var |
Character string naming the target variable(s) |
by |
Character string or vector for row grouping |
where |
Expression for filtering data for this layer |
analyze_fn |
A function with signature |
settings |
A layer_settings object |
Details
The analyze_fn is called once per group combination (defined by
cols and by data variables). It receives:
-
.data: A data.frame subset for the current group -
.target_var: Character string with the target variable name(s)
If format_strings are provided in settings, analyze_fn should
return a single-row data.frame of named numeric values. Each format string
entry becomes one output row, with its name used as the row label.
If no format_strings are provided, analyze_fn must return a
data.frame with row_label and formatted columns.
Note that analyze_fn is called once per cols x by
combination, so it only ever sees a single treatment column at a time — it
cannot compute a statistic across the treatment columns. For an
omnibus association test that spans the columns (e.g. Fisher's exact or CMH
on a count/shift layer), see assoc_test.
Value
A tplyr_analyze_layer object
See Also
assoc_test for cross-column association tests.
Examples
# format_strings mode: the function returns one row of named numbers, and
# each format string becomes an output row.
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_analyze("AGE",
analyze_fn = function(.data, .target_var) {
v <- .data[[.target_var]]
data.frame(gmean = exp(mean(log(v))), rng = diff(range(v)))
},
settings = layer_settings(format_strings = list(
"Geometric mean" = f_str("xx.xx", "gmean"),
"Range" = f_str("xx", "rng")
)))
)
)
tplyr_build(spec, tplyr_adsl)
# Pre-formatted mode: the function supplies row_label and formatted itself.
pre <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_analyze("AGE",
analyze_fn = function(.data, .target_var) {
v <- .data[[.target_var]]
data.frame(
row_label = "Median [IQR]",
formatted = sprintf("%.1f [%.1f]", median(v), IQR(v))
)
})
)
)
tplyr_build(pre, tplyr_adsl)
Create a count layer
Description
Create a count layer
Usage
group_count(target_var, by = NULL, where = NULL, settings = layer_settings())
Arguments
target_var |
Character string or vector naming the target variable(s). Multiple variables create nested/hierarchical counts. |
by |
Character string or vector for row grouping. Strings that don't
match column names are treated as text labels. Use |
where |
Expression for filtering data for this layer |
settings |
A layer_settings object |
Value
A tplyr_count_layer object
See Also
layer_settings() for denominators, sorting, and special rows.
Examples
# Counts of a categorical variable within each column group
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1"))
)
tplyr_build(spec, tplyr_adsl)
# Distinct subject counts with a total row, filtered to serious events
ae <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(
group_count("AEBODSYS",
where = AESER == "Y",
settings = layer_settings(distinct_by = "USUBJID", total_row = TRUE))
)
)
head(tplyr_build(ae, tplyr_adae))
# Two target variables nest: preferred term within body system
nested <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(group_count(c("AEBODSYS", "AEDECOD")))
)
head(tplyr_build(nested, tplyr_adae))
Create a descriptive statistics layer
Description
Create a descriptive statistics layer
Usage
group_desc(target_var, by = NULL, where = NULL, settings = layer_settings())
Arguments
target_var |
Character string or vector naming the target variable(s) |
by |
Character string or vector for row grouping |
where |
Expression for filtering data for this layer |
settings |
A layer_settings object |
Value
A tplyr_desc_layer object
See Also
layer_settings() for auto-precision and custom summaries.
Examples
# Default summary: n, Mean (SD), Median, Q1/Q3, Min/Max, Missing
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_desc("AGE"))
)
tplyr_build(spec, tplyr_adsl)
# Choose the statistics and their formats
custom <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_desc("AGE", settings = layer_settings(
format_strings = list(
"n" = f_str("xx", "n"),
"Mean (SD)" = f_str("xx.x (xx.xx)", "mean", "sd")
)))
)
)
tplyr_build(custom, tplyr_adsl)
# Several target variables in one layer, grouped by visit
multi <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(group_desc(c("AVAL", "CHG"), by = "AVISIT"))
)
head(tplyr_build(multi, tplyr_adlb))
Create a shift layer
Description
Create a shift layer
Usage
group_shift(target_var, by = NULL, where = NULL, settings = layer_settings())
Arguments
target_var |
Named character vector with |
by |
Character string or vector for row grouping |
where |
Expression for filtering data for this layer |
settings |
A layer_settings object |
Value
A tplyr_shift_layer object
Examples
# Baseline (rows) against post-baseline (columns) reference ranges
spec <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(
group_shift(c(row = "BNRIND", column = "ANRIND"))
)
)
head(tplyr_build(spec, tplyr_adlb))
# Percentages relative to each baseline (column) group rather than the arm
by_col <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(
group_shift(c(row = "BNRIND", column = "ANRIND"),
settings = layer_settings(shift_denom = "column"))
)
)
head(tplyr_build(by_col, tplyr_adlb))
Harmonize column sets across layers and row-bind
Description
Harmonize column sets across layers and row-bind
Usage
harmonize_and_bind(layer_results)
Apply parenthesis hugging to a format group
Description
Shifts leading spaces from the formatted number to after the trailing
literal, so that characters like ( hug the number.
Usage
hug_format_group(prefix, num_part, trailing_literal)
Arguments
prefix |
Character vector of accumulated result so far |
num_part |
Character vector of formatted numbers (with leading spaces) |
trailing_literal |
Character string of the literal after this group |
Value
Character vector with hugged result
Check if an object is a tplyr_label
Description
Check if an object is a tplyr_label
Usage
is_label(x)
Arguments
x |
Object to check |
Value
Logical
Which values a missing_count setting treats as missing
Description
NA, plus anything named in missing_values. Shared by the
Missing-row computation and denom_exclude so the two cannot disagree
about what "missing" means.
Usage
is_missing_value(x, missing_count)
Arguments
x |
Vector of target-variable values |
missing_count |
The layer's |
Value
Logical vector the same length as x
Check if an object is a tplyr_pop_data
Description
Check if an object is a tplyr_pop_data
Usage
is_pop_data(x)
Arguments
x |
An object to check |
Value
Logical
Examples
is_pop_data(pop_data(cols = "TRT01P"))
is_pop_data("TRT01P")
Check if an object is a tplyr_layer
Description
Check if an object is a tplyr_layer
Usage
is_tplyr_layer(x)
Arguments
x |
Object to check |
Value
Logical
Examples
is_tplyr_layer(group_count("SEX"))
is_tplyr_layer(group_desc("AGE"))
is_tplyr_layer("SEX")
Check if an object is a tplyr_spec
Description
Check if an object is a tplyr_spec
Usage
is_tplyr_spec(x)
Arguments
x |
An object to check |
Value
Logical
Examples
spec <- tplyr_spec(cols = "TRT01P", layers = tplyr_layers(group_count("SEX")))
is_tplyr_spec(spec)
is_tplyr_spec(mtcars)
Create a text label for use in by parameters
Description
Explicitly marks a string as a text label (not a data variable name). Useful when a label string might coincidentally match a column name.
Usage
label(x)
Arguments
x |
Character string to use as a label |
Value
A tplyr_label object
Examples
# A `by` string that matches no column is already treated as a label, but
# label() is explicit -- and necessary when the text matches a column name.
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1", by = label("Age Group (y)"))
)
)
head(tplyr_build(spec, tplyr_adsl))
Create layer settings
Description
Configuration object for all layer options. Unused parameters default to NULL and are ignored during build. Type-specific validation happens at build time.
Usage
layer_settings(
format_strings = NULL,
stat_columns = NULL,
denoms_by = NULL,
shift_denom = "total",
denom_row = FALSE,
denom_row_label = "n",
denom_row_format = NULL,
denom_where = NULL,
denom_ignore = NULL,
distinct_by = NULL,
total_row = FALSE,
total_row_label = "Total",
total_row_count_missings = TRUE,
missing_count = NULL,
missing_subjects = FALSE,
missing_subjects_label = "Missing",
keep_levels = NULL,
limit_data_by = NULL,
custom_summaries = NULL,
stats_as_columns = FALSE,
precision_by = NULL,
precision_on = NULL,
precision_data = NULL,
precision_cap = NULL,
order_count_method = NULL,
ordering_cols = NULL,
result_order_var = NULL,
outer_sort_position = NULL,
risk_diff = NULL,
ci_method = c("clopper_pearson", "wilson", "wald", "agresti_coull", "jeffreys"),
ci_level = 0.95,
assoc_test = NULL,
pct_lt = NULL,
pct_gt = NULL,
zero_count_display = "full",
name = NULL
)
Arguments
format_strings |
Named list of f_str objects |
stat_columns |
Named list of f_str objects for count layers. Each
entry produces its own result column per column group (e.g. one
"n (\
name used as the column sub-label. Column label attributes follow the
pattern |
denoms_by |
Character vector of variable names for denominator grouping.
This replaces (does not augment) the default denominator grouping,
which is the column ( |
shift_denom |
Denominator basis for shift layers. |
denom_row |
Logical, shift layers only. When |
denom_row_label |
Character string, the row label for the |
denom_row_format |
An |
denom_where |
Expression for separate denominator filter |
denom_ignore |
Character vector of values to exclude from denominators |
distinct_by |
Character string naming the variable for distinct counting |
total_row |
Logical, whether to add a total row |
total_row_label |
Character string for total row label |
total_row_count_missings |
Logical, include missing in total |
missing_count |
List configuring the Missing row. Recognized keys:
Any other key is an error. |
missing_subjects |
Logical, add missing subjects row |
missing_subjects_label |
Character string for missing subjects label |
keep_levels |
Character vector of levels to keep |
limit_data_by |
Character vector for data limiting |
custom_summaries |
Named list of expressions for custom summaries |
stats_as_columns |
Logical, transpose stats to columns |
precision_by |
Character vector for precision grouping |
precision_on |
Character string for precision variable |
precision_data |
Data frame with external precision values |
precision_cap |
Named numeric vector c(int=, dec=) |
order_count_method |
Character, ordering method |
ordering_cols |
Character, which column drives ordering |
result_order_var |
Character, which result variable for ordering |
outer_sort_position |
Character, outer sort direction |
risk_diff |
List with risk difference configuration |
ci_method |
Method for the single-proportion confidence interval exposed
through the |
ci_level |
Numeric coverage probability for the single-proportion
confidence interval keywords. Defaults to |
assoc_test |
A |
pct_lt |
Numeric less-than threshold for count-layer percents. A cell
with a nonzero count whose percent would display below this value renders
the percent as |
pct_gt |
Numeric greater-than threshold for count-layer percents. A cell
whose percent is below 100 but would display above this value renders the
percent as |
zero_count_display |
How to display count-layer cells whose count is
zero: |
name |
Character string, layer name for identification |
Value
A tplyr_layer_settings object
Settings by Layer Type
Not all settings apply to every layer type. The table below shows which settings are applicable for each of the four layer types:
| Setting | Count | Desc | Shift | Analyze |
format_strings | X | X | X | X |
stat_columns | X | |||
denoms_by | X | X | X | |
shift_denom | X | |||
denom_row | X | |||
denom_row_label | X | |||
denom_row_format | X | |||
denom_where | X | X | X | |
denom_ignore | X | X | ||
distinct_by | X | X | ||
total_row | X | |||
total_row_label | X | |||
total_row_count_missings | X | |||
missing_count | X | |||
missing_subjects | X | |||
missing_subjects_label | X | |||
keep_levels | X | |||
limit_data_by | X | |||
custom_summaries | X | |||
stats_as_columns | X | |||
precision_by | X | |||
precision_on | X | |||
precision_data | X | |||
precision_cap | X | |||
order_count_method | X | |||
ordering_cols | X | |||
result_order_var | X | |||
outer_sort_position | X | |||
risk_diff | X | |||
ci_method | X | |||
ci_level | X | |||
assoc_test | X | X | X | |
pct_lt | X | |||
pct_gt | X | |||
zero_count_display | X | |||
name | X | X | X | X |
Settings provided for an inapplicable layer type are silently ignored.
Examples
# Formats and special rows on a count layer
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1", settings = layer_settings(
format_strings = list(n_counts = f_str("xx (xx.x%)", "n", "pct")),
total_row = TRUE,
total_row_label = "Total subjects"
))
)
)
tplyr_build(spec, tplyr_adsl)
# Denominators: percentages within each arm-by-sex cell rather than the arm
denom <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1", by = "SEX",
settings = layer_settings(denoms_by = c("TRT01P", "SEX")))
)
)
tplyr_build(denom, tplyr_adsl)
# Auto-precision on a desc layer: 'a' takes decimals from the data, and
# precision_cap bounds them.
prec <- tplyr_spec(
cols = "TRTA",
layers = tplyr_layers(
group_desc("AVAL", by = "PARAMCD", settings = layer_settings(
format_strings = list("Mean (SD)" = f_str("a.a+1 (a.a+2)", "mean", "sd")),
precision_by = "PARAMCD",
precision_cap = c(int = 3, dec = 2)
))
)
)
head(tplyr_build(prec, tplyr_adlb))
# A custom summary adds a statistic the built-ins do not provide
cv <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_desc("AGE", settings = layer_settings(
custom_summaries = list(cv = quote(sd(.var) / mean(.var) * 100)),
format_strings = list("CV%" = f_str("xx.x", "cv"))
))
)
)
tplyr_build(cv, tplyr_adsl)
Does a count layer's formats reference any CI keyword?
Description
Scans the layer's format_strings and stat_columns f_str $vars for one
of the four confidence-interval keywords, so the (comparatively expensive)
CI computation can be skipped entirely for layers that don't display one.
Usage
layer_uses_ci(settings)
Arguments
settings |
A tplyr_layer_settings object |
Value
Logical scalar
Build an equality filter expression
Description
Build an equality filter expression
Usage
make_eq_filter(var_name, value)
Build an inclusion filter expression
Description
Build an inclusion filter expression
Usage
make_in_filter(var_name, values)
Arguments
var_name |
Character name of the variable |
values |
Vector of values for the inclusion set |
Build an is.na() filter expression
Description
Build an is.na() filter expression
Usage
make_is_na_filter(var_name)
Build a filter for missing values (is.na OR %in% missing_values)
Description
Build a filter for missing values (is.na OR %in% missing_values)
Usage
make_missing_filter(var_name, missing_values = character(0))
Build an exclusion filter expression
Description
Build an exclusion filter expression
Usage
make_not_in_filter(var_name, values)
Arguments
var_name |
Character name of the variable |
values |
Vector of values to exclude |
Build a !is.na() filter expression
Description
Build a !is.na() filter expression
Usage
make_not_na_filter(var_name)
Attach an association-test result column to a wide layer result
Description
Adds a pval1 column carrying the formatted per-by-group result, placed
on the first output row of each by group (blank elsewhere), with
config$label as its label attribute.
Usage
merge_assoc_column(wide, assoc, by_rl_cols, by_data_vars, config)
Arguments
wide |
data.table layer result (with rowlabel/res/ord columns). |
assoc |
data.table from |
by_rl_cols |
Character vector of the rowlabel columns holding the by variable values (in by-variable order); empty when the layer has no by. |
by_data_vars |
Character vector of by data-variable names. |
config |
A |
Combine pinned column levels with a layer's own
Description
The spec-level level set (every column-variable value in the table's data)
takes precedence so a layer whose where empties a column group still emits
that column. Any additional variable the layer knows about (a shift layer's
own column variable) is carried through.
Usage
merge_col_levels(pinned, layer_levels)
Arguments
pinned |
Named list of levels captured before layer filtering (or NULL) |
layer_levels |
Named list from |
Value
Named list of levels
Attach pairwise per-level association-test columns to a wide layer result
Description
Appends one pval<k> column per comparison to the wide-format output,
each carrying the formatted p-value on every target-level row (blank
for special rows such as Total/Missing), with the per-comparison label as the
column's label attribute.
Usage
merge_pairwise_assoc(
wide,
assoc_data,
config,
tv,
by_data_vars,
by_labels,
reference
)
Arguments
wide |
data.table in wide format (after |
assoc_data |
data.table from |
config |
A |
tv |
Character string naming the target variable. |
by_data_vars |
Character vector of by-variable names. |
by_labels |
Character vector of by string-labels (non-data by entries). |
reference |
Character(1) resolved reference arm level. |
Attach pairwise association-test columns to a nested wide layer result
Description
Places each comparison's display string on every matching output row by an
exact join on the rowlabel* columns (which uniquely identify a wide
row across all nesting levels). Rows with no computed value – special rows
such as Missing, or a Total row when total_row = FALSE – stay blank.
Usage
merge_pairwise_assoc_nested(
wide,
assoc_data,
config,
row_label_cols,
reference
)
Arguments
wide |
data.table in wide format (after |
assoc_data |
data.table from |
config |
A |
row_label_cols |
Character vector of the |
reference |
Character(1) resolved reference arm level. |
Merge risk difference columns onto wide result
Description
Appends formatted risk difference columns to the wide-format output from
cast_to_wide().
Usage
merge_risk_diff_columns(
wide,
rd_data,
risk_diff_config,
row_label_cols,
tv,
by_data_vars,
by_labels = character(0)
)
Arguments
wide |
data.table in wide format (after cast_to_wide) |
rd_data |
data.table with computed risk differences |
risk_diff_config |
List with comparisons, ci, format |
row_label_cols |
Character vector of row label column names |
tv |
Character string naming the target variable |
by_data_vars |
Character vector of by-variable names |
by_labels |
Character vector of constant |
Value
Modified wide data.table with rdiff columns appended
Grouping columns of a numeric-data snapshot
Description
Layer builders tag each snapshot with the columns that identify a row (see
tag_numeric_group_cols()). Snapshots from an older build lack the
attribute, so fall back to treating the non-numeric columns as grouping.
Usage
numeric_data_group_cols(nd)
Arguments
nd |
A numeric-data snapshot data.frame |
Value
Character vector of grouping column names
Build one pairwise 2x2 and render its display string
Description
Shared by the single-level and nested pairwise paths. Builds
matrix(c(n_ref, n_cmp, N_ref - n_ref, N_cmp - n_cmp), nrow = 2), calls
config$fn on it, and renders the return via
format_assoc_return. A missing count/denominator or a zero
denominator renders a blank (no test).
Usage
pairwise_cell_disp(n_ref, n_cmp, N_ref, N_cmp, config, group = NULL)
Arguments
n_ref, n_cmp |
Event counts for the reference and comparison arm. |
N_ref, N_cmp |
Population denominators for the reference and comparison arm. |
config |
A |
group |
Optional group identifier used when reporting a failure of
|
Value
A length-1 character display string.
Parse a single format group
Description
Parse a single format group
Usage
parse_format_group(group_str)
Parse one side (int or dec) of a format group
Description
Parse one side (int or dec) of a format group
Usage
parse_format_part(part)
Parse a format string into groups and literals
Description
Parse a format string into groups and literals
Usage
parse_format_string(fmt)
Create a population data configuration
Description
Configuration object specifying how population data maps to the spec.
The actual population data.frame is provided at build time via
tplyr_build(spec, data, pop_data = ...).
Usage
pop_data(cols, where = NULL)
Arguments
cols |
Character vector of column variable names in the population data.
If named, names are the spec column names and values are the pop_data column
names (e.g., |
where |
Expression for filtering the population data (optional) |
Value
A tplyr_pop_data object
Examples
# The AE data's TRTA maps to the subject-level TRT01P. Denominators and the
# header N then come from the population, not from the AE records.
spec <- tplyr_spec(
cols = "TRTA",
pop_data = pop_data(cols = c("TRTA" = "TRT01P")),
layers = tplyr_layers(
group_count("AEBODSYS",
settings = layer_settings(distinct_by = "USUBJID"))
)
)
built <- tplyr_build(spec, tplyr_adae, pop_data = tplyr_adsl)
head(built)
# Header N reflects the 254 enrolled subjects, not the 200 AE records
tplyr_header_n(built)
# Restrict the population to the safety set
saf <- pop_data(cols = c("TRTA" = "TRT01P"), where = SAFFL == "Y")
saf
Prepare the dcast column variable, respecting factor-level order
Description
For a single column variable, converts it to a factor ordered by
col_levels so dcast() spreads columns in level order. For multiple
column variables, builds the " | "-joined interaction column and, when
any component is a factor, orders it by the cross-product of each
variable's level order (outermost variable varies slowest). When no
component is a factor the interaction is left as a character vector, so
dcast() falls back to alphabetical order exactly as before.
Usage
prepare_cast_column(dt, cols, col_levels = NULL)
Arguments
dt |
Long data.table about to be cast (mutated in place) |
cols |
Character vector of column variable names |
col_levels |
Named list from |
Value
The name of the variable to use on the RHS of the dcast formula
Confidence interval for a single binomial proportion
Description
Vectorized computation of a two-sided confidence interval for a single
proportion x / n, using one of five standard methods. All methods are
computed in closed form or via qbeta (no per-row
binom.test loop), so the function scales to a full count table's worth
of cells at once.
Usage
proportion_ci(
x,
n,
method = c("clopper_pearson", "wilson", "wald", "agresti_coull", "jeffreys"),
level = 0.95
)
Arguments
x |
Numeric vector of event counts (numerators). |
n |
Numeric vector of trial counts (denominators). Recycled against
|
method |
One of |
level |
Numeric coverage probability (default |
Details
The methods and their references:
clopper_pearsonExact interval based on the beta distribution. Matches
stats::binom.test()and SASPROC FREQ ... EXACT(the clinical convention). This is the default.wilsonWilson score interval without continuity correction. Matches
stats::prop.test(correct = FALSE).waldNormal-approximation ("simple asymptotic") interval, clamped to
[0, 1].agresti_coullAdds
z^2/2pseudo-successes and pseudo-failures, then applies the Wald interval to the adjusted counts.jeffreysBayesian interval using the Jeffreys
Beta(0.5, 0.5)prior, with the standard boundary adjustments atx == 0andx == n.
Edge cases: when n == 0 (or either input is NA) both bounds are
NA; when x == 0 the lower bound is exactly 0; when
x == n the upper bound is exactly 1.
Value
A data.table with two columns, lower and upper,
giving the interval bounds as proportions in [0, 1] (multiply by 100
for the percentage scale).
Examples
proportion_ci(c(0, 12, 40), c(40, 40, 40), method = "clopper_pearson")
Reconstruct a single layer from ARD data
Description
Reconstruct a single layer from ARD data
Usage
reconstruct_layer_from_ard(layer_ard, layer, cols, layer_index)
Record a failure in user-supplied code
Description
A no-op outside collect_user_fn_errors(), so helpers stay callable
from tests and from code paths that run outside a build.
Usage
record_user_fn_error(label, cond, group = NULL)
Arguments
label |
What failed, e.g. |
cond |
The condition caught |
group |
Optional human-readable group identifier, e.g.
|
Value
Invisible NULL, called for its side effect
Re-attach total/distinct_total denominators after grid completion
Description
Grid completion introduces rows whose denominators come out NA from the
left join; refill them from the pre-completion counts, keyed by
denom_group (or fallback_cols when no denom_group applies).
Usage
refill_denom_totals(result, counts, denom_group, fallback_cols)
Rename ordering columns to the public output names
Description
Renames ordindx to ord_layer_index and ord1/ord2/... to
ord_layer_1/ord_layer_2/...
Usage
rename_ord_columns(result)
Arguments
result |
data.table |
Value
Modified data.table (by reference)
Replace leading whitespace with a specified string
Description
Useful for HTML rendering where leading spaces are collapsed.
Usage
replace_leading_whitespace(x, replace_with = " ")
Arguments
x |
Character vector |
replace_with |
Replacement string for each leading space |
Value
Character vector with leading spaces replaced
Examples
# Indentation survives an HTML renderer that would collapse plain spaces
terms <- c("CARDIAC DISORDERS", " ATRIAL FIBRILLATION")
out <- replace_leading_whitespace(terms)
out
# One replacement per leading space; interior spacing is untouched
replace_leading_whitespace(" A B", replace_with = "-")
Record an assoc_test return-shape mismatch
Description
Record an assoc_test return-shape mismatch
Usage
report_assoc_shape_mismatch(label, group, detail)
Resolve the reference arm level for a pairwise association test
Description
Returns config$reference when supplied, otherwise the first level of
the first cols variable at build time (factor level order when the
variable is a factor, else first value in appearance order).
Usage
resolve_assoc_reference(config, dt, cols)
Arguments
config |
A |
dt |
data.table of source rows for the layer. |
cols |
Character vector of column variable names from the spec. |
Value
Character(1) reference level.
Resolve per-comparison p-value column labels
Description
Default is "<reference> vs <comparison>"; a single configured label recycles
across comparisons; a vector is used as-is.
Usage
resolve_pairwise_labels(config, reference)
Resolve population data column mapping
Description
Maps population data columns to spec columns. Handles named (explicit) and unnamed (positional) mapping.
Usage
resolve_pop_cols(pop_config, spec_cols)
Arguments
pop_config |
A tplyr_pop_data object or NULL |
spec_cols |
Character vector of spec-level column names |
Value
Character vector of column names to use in the population data
Resolve auto-precision widths for a format group
Description
Given a parsed format group and collected precision values, returns the effective integer width and decimal width.
Usage
resolve_precision(group, max_int, max_dec)
Arguments
group |
A parsed format group (from parse_format_group) |
max_int |
Integer, the collected max integer width |
max_dec |
Integer, the collected max decimal precision |
Value
list(int_width, dec_width)
Locate the rowlabel columns to join summary statistics back on
Description
The inverse of build_row_labels_long()'s layout: one rowlabel column
per constant by label first, then one per by data variable, then the
target variable last. Callers that merge per-group statistics (risk
difference, pairwise p-values) onto the assembled table need the data
variable columns; assuming they start at rowlabel1 keys the join against a
constant-label column whenever by leads with a string label, matching
nothing and leaving every cell blank.
Usage
resolve_rowlabel_join_cols(wide, by_labels, by_data_vars)
Arguments
wide |
Assembled wide table |
by_labels |
Character vector of constant |
by_data_vars |
Character vector of |
Value
List with tv_col (the target variable's rowlabel column) and
by_cols (the by data variables' rowlabel columns, in by_data_vars
order), or NULL when wide has no rowlabel columns
Restore the atomic type of the plain settings fields
Description
Restore the atomic type of the plain settings fields
Usage
restore_settings_field_types(raw)
Arguments
raw |
Named list of parsed settings |
Value
raw with each plain field coerced to its declared vector type
Percentage of a count against its denominator
Description
The one 0-vs-NA convention for the whole package. Without a usable
denominator the percentage is undefined, so it is NA and renders blank —
count and shift layers used to render 0, which for n > 0 is an
affirmatively wrong number, while desc layers already used NA.
Usage
safe_pct(n, total)
Arguments
n |
Numeric vector of counts |
total |
Numeric vector of denominators |
Value
Numeric vector of percentages, NA where the denominator is NA or 0
Convert raw parsed JSON/YAML back to tplyr_spec
Description
Convert raw parsed JSON/YAML back to tplyr_spec
Usage
serializable_to_spec(raw)
Serialize the by parameter
Description
Serialize the by parameter
Usage
serialize_by(by)
Serialize an expression (or NULL)
Description
rlang::expr_deparse() wraps at 60 characters by default, which any
realistic multi-condition filter exceeds. parse_expr() on read needs a
single string, so the deparsed pieces are always collapsed to one.
Usage
serialize_expr(expr)
Serialize an f_str object
Description
Serialize an f_str object
Usage
serialize_f_str(fmt)
Serialize a function
Description
Serialize a function
Usage
serialize_function(fn)
Serialize a single layer
Description
Serialize a single layer
Usage
serialize_layer(layer)
Serialize layer settings
Description
Serialize layer settings
Usage
serialize_settings(settings)
Atomic-vector types of the plain layer settings
Description
JSON arrays always parse back as lists (simplifyVector = FALSE), which
erases both the vector type and the distinction between a length-1 vector and
a list. This table is the single source of truth used by
deserialize_settings() to restore each plain setting to the vector type
the build code expects. test-serialize.R asserts that every
layer_settings() formal appears here or in
serialize_special_fields, so a newly added setting cannot silently
round-trip as a list.
Usage
settings_field_types()
Value
Named list of character vectors, keyed by storage mode
Sort column names by their numeric suffix
Description
Lexicographic sorting places "res10" before "res2"; ordering by the numeric suffix keeps columns in build order once a family has more than 9 members.
Usage
sort_by_numeric_suffix(x)
Sort bound layer results by layer index, then within-layer ord columns
Description
Sorts by reference. other_ord sorts lexicographically, which is stable
for the single-digit ord counts layers produce today.
Usage
sort_by_ord_columns(result)
Sort nested count data for correct interleaving
Description
Sort nested count data for correct interleaving
Usage
sort_nested(
combined,
target_vars,
by_data_vars,
dt = NULL,
outer_sort_position = NULL,
order_count_method = NULL,
result_order_var = "n",
ordering_cols = NULL,
cols = character(0)
)
Convert spec to a plain list suitable for JSON/YAML
Description
Convert spec to a plain list suitable for JSON/YAML
Usage
spec_to_serializable(spec)
Extract numeric values from formatted strings
Description
Extracts the Nth numeric value from a formatted tplyr2 string.
Usage
str_extract_num(x, index = 1L)
Arguments
x |
Character vector of formatted strings |
index |
Integer, which numeric value to extract (1-based) |
Value
Numeric vector
Examples
cells <- c(" 22 (25.6%)", " 9 (10.5%)", " 55 (64.0%)")
# Default pulls the count; index = 2 pulls the percent
str_extract_num(cells)
str_extract_num(cells, index = 2)
# Asking past the available numbers gives NA, as does an NA input
str_extract_num(c(" 5", NA), index = 2)
# Recover a sort key from an already-formatted table
built <- tplyr_build(
tplyr_spec(cols = "TRT01P", layers = tplyr_layers(group_count("AGEGR1"))),
tplyr_adsl
)
built[order(-str_extract_num(built$res1)), c("rowlabel1", "res1")]
Wrap strings to a specific width with hyphenation while preserving indentation
Description
Leverages stringr::str_wrap() under the hood, but takes extra steps
to preserve any indentation that has been applied to a character element,
and use hyphenated wrapping of single words that run longer than the
allotted wrapping width.
Usage
str_indent_wrap(x, width = 10, tab_width = 5)
Arguments
x |
An input character vector |
width |
The desired width of elements within the output character vector |
tab_width |
The number of spaces to which tabs should be converted |
Details
stringr::str_wrap() is highly efficient, but in the context of table
creation there are two features missing — hyphenation for long running
strings that overflow width, and respect for pre-indentation of a character
element. For example, in an adverse event table, you may have body system
rows as an un-indented column, and preferred terms as indented columns. These
strings may run long and require wrapping to not surpass the column width.
Furthermore, for crowded tables a single word may be longer than the column
width itself.
This function resolves these two issues, while minimizing additional overhead required to apply the wrapping of strings.
Note: This function automatically converts tabs to spaces. Tab width varies
depending on font, so width cannot automatically be determined within a data
frame. Users can specify the width via tab_width.
Value
A character vector with string wrapping applied
Examples
ex_text1 <- c("RENAL AND URINARY DISORDERS", " NEPHROLITHIASIS")
ex_text2 <- c("RENAL AND URINARY DISORDERS", "\tNEPHROLITHIASIS")
cat(paste(str_indent_wrap(ex_text1, width = 8), collapse = "\n\n"), "\n")
cat(paste(str_indent_wrap(ex_text2, tab_width = 4), collapse = "\n\n"), "\n")
Name of the per-column-variable synthetic-row marker
Description
Records which column variable a duplicated row was created for, so a total group can skip copies made on its own variable while still spanning copies made for a different one.
Usage
synth_marker(col_var)
Arguments
col_var |
Character(1) column variable name |
Value
Character(1) marker column name
Tag a numeric-data snapshot with its grouping columns
Description
Records which columns identify a row rather than hold a statistic, so
tplyr_stats_data() can subset to grouping columns plus one statistic
without guessing from column types (a grouping variable can be numeric).
Usage
tag_numeric_group_cols(snapshot, group_cols)
Arguments
snapshot |
data.table snapshot, modified by reference |
group_cols |
Character vector of candidate grouping column names |
Value
snapshot, invisibly
Create a total group configuration
Description
Specifies that a synthetic "Total" column level should be added by duplicating all rows with the specified column variable set to the label.
Usage
total_group(col_var, label = "Total")
Arguments
col_var |
Character string naming the column variable to totalize |
label |
Character string for the total group label (default: "Total") |
Value
A tplyr_total_group object
Examples
# Adds a "Total" column spanning every arm, alongside the individual arms
spec <- tplyr_spec(
cols = "TRT01P",
total_groups = list(total_group("TRT01P")),
layers = tplyr_layers(group_count("AGEGR1"))
)
tplyr_build(spec, tplyr_adsl)
# Rename the total column
all_pts <- tplyr_spec(
cols = "TRT01P",
total_groups = list(total_group("TRT01P", label = "All Patients")),
layers = tplyr_layers(group_count("SEX"))
)
tplyr_build(all_pts, tplyr_adsl)
Get or set tplyr2 package options
Description
View and modify tplyr2-specific options. When called with no arguments, returns all current tplyr2 options with their defaults. When called with named arguments, sets those options.
Usage
tplyr2_options(...)
Arguments
... |
Named arguments to set (e.g., |
Details
Available options:
- tplyr2.IBMRounding
Logical. Use round-half-away-from-zero instead of R's default banker's rounding. Default:
FALSE.- tplyr2.quantile_type
Integer. Quantile algorithm type passed to
quantile(). Default:7.- tplyr2.precision_cap
Named numeric vector
c(int=, dec=). Maximum int/dec widths for auto-precision. Default:NULL.- tplyr2.custom_summaries
Named list of expressions for global custom summary functions. Default:
NULL.- tplyr2.scipen
Integer. scipen value used during
tplyr_build()to prevent scientific notation. Default:9999.
An unrecognized option name is an error rather than a silent no-op, so a misspelling cannot quietly leave the build on the default behavior.
Value
When called with no arguments, a named list of current option values. When called with arguments, invisibly returns the previous values.
Examples
# Inspect the current values
tplyr2_options()
# Setting returns the previous values, so the change can be undone
old <- tplyr2_options(IBMRounding = TRUE)
getOption("tplyr2.IBMRounding")
do.call(options, old)
getOption("tplyr2.IBMRounding")
# IBM (half-away-from-zero) rounding vs R's banker's rounding
fmt <- f_str("xx", "n")
apply_formats(fmt, n = 2.5)
old <- tplyr2_options(IBMRounding = TRUE)
apply_formats(fmt, n = 2.5)
do.call(options, old)
# A misspelled name errors instead of setting a dead option
try(tplyr2_options(IBMrounding = TRUE))
Adverse events analysis dataset
Description
A sample CDISC ADaM ADAE dataset from the PHUSE Test Data Factory. Contains adverse event records.
Usage
tplyr_adae
Format
A data.frame
Laboratory data analysis dataset
Description
A sample CDISC ADaM ADLB dataset from the PHUSE Test Data Factory. Contains laboratory test results.
Usage
tplyr_adlb
Format
A data.frame
Subject-level analysis dataset
Description
A sample CDISC ADaM ADSL dataset from the PHUSE Test Data Factory. Contains subject-level demographics and baseline characteristics.
Usage
tplyr_adsl
Format
A data.frame
Build a tplyr2 table from a spec and data
Description
Executes the table specification against the provided data, producing a formatted output data frame.
Usage
tplyr_build(spec, data, pop_data = NULL, metadata = FALSE, ...)
Arguments
spec |
A tplyr_spec object (or path to a JSON/YAML spec file) |
data |
A data.frame to process |
pop_data |
Optional population data.frame (overrides spec pop_data) |
metadata |
If TRUE, attach cell-level metadata enabling traceability
back to source data rows via |
... |
Additional named arguments overriding spec-level parameters. Names
must match a field of |
Value
A data.frame with rowlabel, res, and ord columns
See Also
tplyr_spec() to build the specification, and
tplyr_numeric_data() for the unformatted values behind the cells.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1"))
)
tplyr_build(spec, tplyr_adsl)
# Override spec fields at build time without editing the spec. Overrides go
# through `...`, which evaluates eagerly, so a `where` must be a string or
# quoted -- not the bare expression tplyr_spec() accepts.
tplyr_build(spec, tplyr_adsl, where = "SEX == 'F'")
tplyr_build(spec, tplyr_adsl, where = quote(SEX == "F"))
# Population data supplies the denominators and the header N
pop_spec <- tplyr_spec(
cols = "TRTA",
pop_data = pop_data(cols = c("TRTA" = "TRT01P")),
layers = tplyr_layers(
group_count("AEBODSYS",
settings = layer_settings(distinct_by = "USUBJID"))
)
)
head(tplyr_build(pop_spec, tplyr_adae, pop_data = tplyr_adsl))
Reconstruct a formatted table from ARD and a spec
Description
Takes Analysis Results Data (long format) and a tplyr_spec, then
applies the spec's formatting rules to produce a formatted output table.
Usage
tplyr_from_ard(ard, spec)
Arguments
ard |
A data.frame in ARD format (as produced by |
spec |
A |
Value
A data.frame with the same structure as tplyr_build() output
See Also
tplyr_to_ard() to produce the ARD.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_desc("AGE"))
)
built <- tplyr_build(spec, tplyr_adsl)
# Round-tripping through the ARD reproduces the formatted cells, so a table
# can be rebuilt from stored results without the subject-level data
ard <- tplyr_to_ard(built)
recon <- tplyr_from_ard(ard, spec)
recon[, c("rowlabel1", "res1")]
identical(as.vector(recon$res1), as.vector(built$res1))
# Changing only the spec's formats re-renders the same numbers differently
respec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_desc("AGE", settings = layer_settings(
format_strings = list("Mean (SD)" = f_str("xx.xx (xx.xxx)", "mean", "sd"))))
)
)
tplyr_from_ard(ard, respec)[, c("rowlabel1", "res1")]
Extract header N from a tplyr2 build result
Description
Returns the population-based header N values that were computed during
tplyr_build(). Only available when population data was provided.
Usage
tplyr_header_n(result)
Arguments
result |
A data.frame produced by |
Value
A data.frame with column variable levels and their N values, or NULL if no population data was used.
Examples
spec <- tplyr_spec(
cols = "TRTA",
pop_data = pop_data(cols = c("TRTA" = "TRT01P")),
layers = tplyr_layers(group_count("AEBODSYS"))
)
built <- tplyr_build(spec, tplyr_adae, pop_data = tplyr_adsl)
tplyr_header_n(built)
# NULL when the build had no population data to draw an N from
no_pop <- tplyr_build(
tplyr_spec(cols = "TRT01P", layers = tplyr_layers(group_count("SEX"))),
tplyr_adsl
)
tplyr_header_n(no_pop)
Create a list of layers
Description
Wraps one or more layer objects into a validated list for use in tplyr_spec().
Usage
tplyr_layers(...)
Arguments
... |
Layer objects created by group_count(), group_desc(), group_shift(), or group_analyze() |
Value
A list of tplyr_layer objects
Examples
# Layers stack in the order given, and may mix types freely
layers <- tplyr_layers(
group_desc("AGE"),
group_count("SEX"),
group_count("AGEGR1")
)
length(layers)
spec <- tplyr_spec(cols = "TRT01P", layers = layers)
tplyr_build(spec, tplyr_adsl)
Metadata object for a tplyr output cell
Description
Contains filter expressions that, when evaluated against the original data, reproduce the subset of rows that contributed to a specific cell in the output table.
Usage
tplyr_meta(
names = character(0),
filters = list(),
layer_index = integer(0),
anti_join = NULL,
statistic = NULL
)
Arguments
names |
Character vector of variable names relevant to this cell |
filters |
List of R language objects (call expressions) representing filter conditions |
layer_index |
Integer layer index (1-based) |
anti_join |
NULL or a |
statistic |
NULL or a character string naming which statistic the
cell displays (set for |
Value
A tplyr_meta object
See Also
tplyr_meta_result() to retrieve one from a build.
Examples
# Usually obtained from a build rather than constructed by hand
m <- tplyr_meta(
names = c("TRT01P", "AGEGR1"),
filters = list(quote(TRT01P == "Placebo"), quote(AGEGR1 == "65-80")),
layer_index = 1L
)
m
# The filters are ordinary language objects, so they can be applied directly
subset(tplyr_adsl, TRT01P == "Placebo" & AGEGR1 == "65-80")[1:3, c("USUBJID", "AGEGR1")]
Anti-join metadata for missing subjects
Description
Anti-join metadata for missing subjects
Usage
tplyr_meta_anti_join(join_meta, on)
Arguments
join_meta |
A |
on |
Character vector of join key variable names (e.g., |
Value
A tplyr_meta_anti_join object
Get metadata for a specific output cell
Description
Returns a tplyr_meta object containing the filter expressions
that describe the source data for the specified cell.
Usage
tplyr_meta_result(result, row_id, column)
Arguments
result |
A data.frame from |
row_id |
Character row ID (from |
column |
Character column name (e.g., |
Value
A tplyr_meta object, or NULL if no metadata for that cell
See Also
tplyr_meta_subset() to fetch the source rows themselves.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1"))
)
built <- tplyr_build(spec, tplyr_adsl, metadata = TRUE)
built[, c("row_id", "rowlabel1", "res1")]
# The filters behind the Placebo / 65-80 cell
tplyr_meta_result(built, built$row_id[1], "res1")
Get source data rows for a specific output cell
Description
Evaluates the stored filter expressions against the original data to return the rows that contributed to the specified output cell.
Usage
tplyr_meta_subset(result, row_id, column, data, pop_data = NULL)
Arguments
result |
A data.frame from |
row_id |
Character row ID |
column |
Character column name (e.g., |
data |
The original data.frame that was passed to |
pop_data |
Optional population data.frame, required when the cell represents a missing subjects row (anti-join) |
Value
A data.frame subset of the original data, or NULL if no metadata
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_count("AGEGR1"))
)
built <- tplyr_build(spec, tplyr_adsl, metadata = TRUE)
built[, c("row_id", "rowlabel1", "res1")]
# Trace the first cell back to the subjects it counted
src <- tplyr_meta_subset(built, built$row_id[1], "res1", tplyr_adsl)
nrow(src)
head(src[, c("USUBJID", "TRT01P", "AGEGR1")])
# The row count matches the number displayed in the cell
built$res1[1]
Retrieve raw numeric data from a tplyr_build result
Description
Returns the unformatted numeric data that was computed during the build process, before formatting and pivoting to wide format.
Usage
tplyr_numeric_data(result, layer = NULL)
Arguments
result |
A data.frame produced by |
layer |
Integer layer index (1-based), or NULL for all layers |
Value
If layer is specified, a data.frame of raw statistics for that
layer. If layer is NULL, a named list of data.frames keyed by layer
index. Returns NULL if numeric data was not retained.
See Also
tplyr_stats_data() for a single statistic with its grouping
columns.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1"),
group_desc("AGE")
)
)
built <- tplyr_build(spec, tplyr_adsl)
# One data.frame per layer, keyed by layer index
names(tplyr_numeric_data(built))
# The counts behind the formatted cells, before rounding and padding
head(tplyr_numeric_data(built, 1))
# Every statistic the desc layer computed, including unused ones
names(tplyr_numeric_data(built, 2))
Read a tplyr_spec from JSON or YAML
Description
Deserializes a spec from a file. Expressions are reconstructed from their string representations.
Usage
tplyr_read_spec(path)
Arguments
path |
File path to a JSON or YAML spec file |
Value
A tplyr_spec object
See Also
tplyr_write_spec() to write one.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
where = SAFFL == "Y",
layers = tplyr_layers(group_count("AGEGR1", by = "SEX"))
)
path <- file.path(tempdir(), "spec.json")
tplyr_write_spec(spec, path)
# The round trip reproduces the spec, and the same table
spec2 <- tplyr_read_spec(path)
spec2
identical(tplyr_build(spec, tplyr_adsl), tplyr_build(spec2, tplyr_adsl))
# tplyr_build() also accepts a spec file path directly
head(tplyr_build(path, tplyr_adsl))
unlink(path)
Round numbers with optional IBM rounding
Description
When getOption("tplyr2.IBMRounding", FALSE) is TRUE, uses
round-half-away-from-zero (IBM convention) instead of R's default
banker's rounding (round half to even).
Usage
tplyr_round(x, digits = 0)
Arguments
x |
Numeric vector |
digits |
Number of decimal places |
Value
Numeric vector
Create a tplyr2 table specification
Description
The spec is a pure configuration object describing what to compute.
No data processing occurs until tplyr_build() is called.
Usage
tplyr_spec(
cols,
where = NULL,
pop_data = NULL,
total_groups = NULL,
custom_groups = NULL,
layers = tplyr_layers(),
settings = NULL
)
Arguments
cols |
Character vector of column variable names |
where |
Expression for global data filter (optional) |
pop_data |
A pop_data() object for population-based features (optional) |
total_groups |
List of total_group() objects (optional) |
custom_groups |
List of custom_group() objects (optional) |
layers |
A list of layer objects from tplyr_layers() |
settings |
Additional spec-level settings (optional) |
Value
A tplyr_spec object
See Also
tplyr_build() to execute a spec, tplyr_layers() to assemble
layers, and layer_settings() for per-layer configuration.
Examples
# A spec is inert configuration -- nothing is computed until tplyr_build()
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1"),
group_desc("AGE")
)
)
spec
tplyr_build(spec, tplyr_adsl)
# A global `where` filter applies to every layer
safety <- tplyr_spec(
cols = "TRT01P",
where = SAFFL == "Y",
layers = tplyr_layers(group_count("SEX"))
)
tplyr_build(safety, tplyr_adsl)
Retrieve raw statistic values from a tplyr_build result
Description
Filters the raw numeric data for a specific layer and statistic. Use
tplyr_numeric_data() to get every statistic for the layer.
Usage
tplyr_stats_data(result, layer, statistic)
Arguments
result |
A data.frame produced by |
layer |
Integer layer index (1-based) |
statistic |
Character string naming the statistic column to extract (e.g., "n", "pct", "mean", "sd") |
Value
A data.frame with the layer's grouping columns and the requested statistic. Returns NULL if the layer has no numeric data or does not compute the statistic.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(group_desc("AGE"))
)
built <- tplyr_build(spec, tplyr_adsl)
tplyr_stats_data(built, 1, "mean")
Convert tplyr_build output to Analysis Results Data (ARD) format
Description
Transforms the numeric data attached to a tplyr_build() result into
a long-format data frame with one row per statistic per group combination.
This is compatible with the CDISC Analysis Results Data standard.
Usage
tplyr_to_ard(result)
Arguments
result |
A data.frame produced by |
Value
A data.frame in long format with columns:
- analysis_id
Integer layer index
- stat_name
Character name of the statistic
- stat_value
Numeric value of the statistic
- ...
Grouping columns from the original data
See Also
tplyr_from_ard() to rebuild a formatted table from an ARD.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
layers = tplyr_layers(
group_count("AGEGR1"),
group_desc("AGE")
)
)
built <- tplyr_build(spec, tplyr_adsl)
ard <- tplyr_to_ard(built)
head(ard)
# One row per statistic per group; analysis_id identifies the layer
table(ard$analysis_id)
unique(ard$stat_name[ard$analysis_id == 2])
Write a tplyr_spec to JSON or YAML
Description
Serializes a spec object to a file. The format is determined by the file
extension: .json for JSON, .yaml or .yml for YAML.
Usage
tplyr_write_spec(spec, path)
Arguments
spec |
A tplyr_spec object |
path |
File path. Extension determines format. |
Details
Expressions (e.g., where clauses) are deparsed to strings and
reconstructed on read. Format string objects (f_str) are stored as
their component parts and regenerated on read.
Value
Invisible file path
See Also
tplyr_read_spec() to read one back.
Examples
spec <- tplyr_spec(
cols = "TRT01P",
where = SAFFL == "Y",
layers = tplyr_layers(
group_count("AGEGR1", by = "SEX", settings = layer_settings(
denoms_by = c("TRT01P", "SEX"),
format_strings = list(n_counts = f_str("xx (xx.x%)", "n", "pct"))))
)
)
path <- file.path(tempdir(), "spec.json")
tplyr_write_spec(spec, path)
cat(readLines(path), sep = "\n")
# YAML is chosen by the file extension
if (requireNamespace("yaml", quietly = TRUE)) {
ypath <- file.path(tempdir(), "spec.yaml")
tplyr_write_spec(spec, ypath)
cat(readLines(ypath), sep = "\n")
unlink(ypath)
}
unlink(path)
Translate a group value to filter expressions
Description
When a column value corresponds to a total group or custom group label,
translates back to the appropriate filter. Total groups produce no filter
(all values pass). Custom groups produce a %in% filter with
component values.
Usage
translate_group_value(value, col_var, total_groups, custom_groups)
Arguments
value |
The column value from the output (e.g., "Total", "Active") |
col_var |
The column variable name |
total_groups |
List of tplyr_total_group objects |
custom_groups |
List of tplyr_custom_group objects |
Value
A list with filters (list of call expressions) and
is_total (logical)
Transpose stats-as-columns
Description
Transposes the standard wide output so that statistics become columns.
Without a by variable, treatment groups become the rows and stat names
become the columns. With a by variable, the by groups stay as rows and
each column is a treatment x statistic combination (issue #20) — e.g. one
"Arm A | Mean" and "Arm A | n" column per treatment — so the by dimension
is preserved instead of being collapsed.
Usage
transpose_stats_to_columns(wide)
Arguments
wide |
data.table from standard desc processing |
Value
Transposed data.table
Transpose desc stats to columns while keeping by groups as rows
Description
Produces one row per by-group combination and one result column per
treatment x statistic, ordered treatment-major then statistic. Result
columns carry a "<treatment label> | <stat name>" label attribute
following the same grammar as count-layer stat_columns.
Usage
transpose_stats_with_by(wide, res_cols, trt_labels, by_cols, stat_col)
Validate data compatibility at build time
Description
Checks that the columns referenced in the spec actually exist in the data. Called after data conversion to data.table.
Usage
validate_build_data(spec, dt)
Arguments
spec |
A tplyr_spec object |
dt |
A data.table |
Value
Invisible TRUE if valid
Validate that every layer produces the same result-column shape
Description
Result columns are aligned positionally by name across layers. A shift layer
emits one column per cols level crossed with its shift-column
variable, and a stats_as_columns desc layer emits one per level crossed
with each statistic. Either alongside a layer with the plain one-column-per-
level shape leaves the combined table's res columns meaning different
things in different row blocks, with only the first layer's column labels
retained — so the values appear under the wrong treatment arm.
Usage
validate_column_shape_alignment(layers)
Arguments
layers |
List of tplyr_layer objects |
Value
Invisible TRUE if valid
Validate denoms_by against the layer's grouping variables
Description
Most denominator merges join on intersect(denom_group, names(x)). A
denoms_by naming a variable that is not one of the layer's grouping
columns silently shrinks the join-key set, leaving the denominator table with
several rows per remaining key — so the merge either multiplies table rows or
attaches another group's denominator, with no error. Pinning the invariant
here makes every one of those intersects provably a no-op.
Usage
validate_denoms_by(layer, index, cols, dt_names)
Arguments
layer |
A tplyr_layer object |
index |
Integer layer index |
cols |
Character vector of spec-level column variables |
dt_names |
Column names of the build data |
Value
Invisible TRUE
Validate format strings in layer settings
Description
Validate format strings in layer settings
Usage
validate_format_strings(fmt_list, layer_index)
Arguments
fmt_list |
A named list expected to contain f_str objects |
layer_index |
Integer layer index (for error messages) |
Value
Invisible TRUE if valid
Validate a single layer
Description
Validate a single layer
Usage
validate_layer(layer, index, cols = NULL)
Arguments
layer |
A tplyr_layer object |
index |
Integer layer index (for error messages) |
cols |
Character vector of spec column variables (for cross-checks such as pairwise assoc_test); may be NULL when validating a layer in isolation. |
Value
Invisible TRUE if valid
Validate that format string vars are valid stats for the layer type
Description
Issues warnings (not errors) for unrecognized statistic names, since custom summaries can add arbitrary stat names.
Usage
validate_layer_stats(layer, index)
Arguments
layer |
A tplyr_layer object |
index |
Integer layer index |
Value
Invisible TRUE
Validate the keys of a missing_count configuration
Description
missing_count is a free-form list, so an unrecognized key used to be
accepted and then never read — the table built without the requested
behavior and nothing pointed at the mistake.
Usage
validate_missing_count(missing_count, index)
Arguments
missing_count |
The layer's |
index |
Integer layer index |
Value
Invisible TRUE
Warn when pop_data does not cover the analysis data's column levels
Description
A column level present in the analysis data but absent from the population
(an arm recoded "Xanomeline High Dose" vs "High Dose") yields
an NA denominator for every one of its cells.
Usage
validate_pop_data_coverage(dt, pop_dt, cols)
Arguments
dt |
Analysis data.table |
pop_dt |
Population data.table, or NULL |
cols |
Character vector of column variables |
Value
Invisible TRUE
Validate a tplyr_spec object structurally
Description
Checks that the spec has the correct class and structure. Called after overrides are applied but before data is processed.
Usage
validate_spec(spec)
Arguments
spec |
A tplyr_spec object |
Value
Invisible TRUE if valid, otherwise stops with informative error
Validate stat_columns in layer settings
Description
Validate stat_columns in layer settings
Usage
validate_stat_columns(stat_cols, layer_index)
Arguments
stat_cols |
A named list expected to contain f_str objects |
layer_index |
Integer layer index (for error messages) |
Value
Invisible TRUE if valid
Validate stat_columns consistency across layers
Description
Layers using stat_columns emit one res column per statistic per column group, while other layers emit one per column group. harmonize_and_bind() aligns layers positionally by res column name, so mixing the two shapes in one spec would silently place results under the wrong column labels.
Usage
validate_stat_columns_alignment(layers)
Arguments
layers |
List of tplyr_layer objects |
Value
Invisible TRUE if valid
Emit the collected user-code failures as a single warning
Description
Emit the collected user-code failures as a single warning
Usage
warn_user_fn_errors(entries)
Arguments
entries |
List of entries recorded by |
Value
Invisible NULL
Zero-fill count statistics left NA by grid completion or merges
Description
Counts left NA by grid completion are genuine zeros. Percentages are not: a percentage is only zero when a usable denominator says so, and filling one whose denominator is missing or zero would print a number that was never computed (#76). Those stay NA and render blank.
Usage
zero_fill_stats(dt)
Arguments
dt |
data.table to modify by reference |
Value
dt, invisibly