Field-merge a single col_spec() onto every column matched by
name or by a predicate. The vectorized companion to cols() for
the common case of a variable number of treatment-arm columns that
all share the same display rule (decimal alignment, a numeric
format), so you avoid do.call() / !!! splicing one named
argument per arm.
Arguments
- .spec
The
tabular_specto extend.<tabular_spec>: required. Dot-prefixed so partial matching cannot bind a user name in another slot.- .cols
Columns to match.
<character | function>: required. Either a character vector of input column names in.spec@data, or a predicatefunction(names) -> logicalevaluated againstnames(.spec@data)(one logical per column, same length).Restriction: Named columns must exist in
.spec@data. A predicate must return a logical vector the length ofnames(.spec@data). Tip: No tidyselect helpers ship; pass a base vector (grep("^ARM", names(df), value = TRUE)) or a predicate (\(nm) startsWith(nm, "ARM")).- .col_spec
The spec to field-merge onto every match.
<col_spec>: required. Built withcol_spec().
Value
The updated tabular_spec. Continue chaining with
headers(), sort_rows(), style().
Details
Field-merge, not replace. cols_apply() reuses the same
field-by-field merge as repeated cols() calls: a non-default
field on .col_spec overrides; a default-valued field leaves any
prior attribute on the matched column intact. Set the shared rule
across arms first, then refine an individual arm with a later
cols() call (or the reverse).
Per-column label token. A label that references {.name} (or
its alias {.col}) inside a {expr} is resolved per matched
column, with .name and .col both bound to that column's name.
This makes a variable-N arm header a single declarative call instead
of a hand-written loop. The rest of the {expr} evaluates in the
calling environment, so a per-arm BigN looked up from a named vector
works directly:
n <- c(placebo = 86, drug_50 = 84, drug_100 = 84)
cols_apply(
spec, c("placebo", "drug_50", "drug_100"),
col_spec(label = "{.name}\n(N={n[.name]})", align = "decimal")
)
# placebo -> "placebo\n(N=86)" ; drug_50 -> "drug_50\n(N=84)" ; ...The token is a plain-string feature; a label wrapped in md() /
html() is parsed eagerly and does not interpolate. A failing
token expression aborts naming the offending column.
width merge. width's default sentinel for the merge is
"auto": a later cols() / cols_apply() call carrying the default
width = "auto" leaves a previously pinned width intact (only an
explicit non-"auto" width overrides). Apply a shared width last to
broadcast it across arms.
See also
Companion verbs: cols() attaches per-column specs by name;
col_spec() builds the spec.
Sibling build verbs: headers(), sort_rows(),
style(), paginate(), preset().
Examples
# ---- Example 1: Decimal-align every arm column by name vector ----
#
# Demographics table whose treatment-arm columns are selected by a
# name vector (`grep()` against the data) and given one shared
# decimal-alignment spec, while the two row-label columns keep
# their own roles set with `cols()`.
arm_cols <- grep("^placebo$|^drug_|^Total$", names(cdisc_saf_demo), value = TRUE)
tabular(
cdisc_saf_demo,
titles = c(
"Table 14.1.1",
"Demographics and Baseline Characteristics",
"Safety Population"
)
) |>
cols(
variable = col_spec(usage = "group", label = "Parameter"),
stat_label = col_spec(label = "Statistic")
) |>
cols_apply(arm_cols, col_spec(align = "decimal")) |>
sort_rows(by = c("variable", "stat_label"))
Table 14.1.1
Demographics and Baseline Characteristics
Safety Population
Statistic placebo drug_50 drug_100 Total Age (years) Mean (SD) 75.2 (8.59) 76.0 (8.11) 73.8 (7.94) 75.1 (8.25) Median 76.0 78.0 75.5 77.0 Min, Max 52 , 89 51 , 88 56 , 88 51 , 89 Q1, Q3 69.2, 81.8 71.0, 82.0 70.5, 79.0 70.0, 81.0 n 86 96 72 254 Race, n (%) AMERICAN INDIAN OR ALASKA NATIVE 0 0 1 ( 1.4) 1 ( 0.4) ASIAN 0 0 0 0 BLACK OR AFRICAN AMERICAN 8 ( 9.3) 6 ( 6.2) 9 (12.5) 23 ( 9.1) WHITE 78 (90.7) 90 (93.8) 62 (86.1) 230 (90.6) Sex, n (%) F 53 (61.6) 55 (57.3) 35 (48.6) 143 (56.3) M 33 (38.4) 41 (42.7) 37 (51.4) 111 (43.7)
# ---- Example 2: Select arm columns with a predicate ----
#
# Best Overall Response table. The arm columns are matched with a
# predicate over the column names; the hidden sort helpers and the
# response label are declared with `cols()`. The predicate scales
# to any number of arms without editing the call.
bor_levels <- c(
"CR", "PR", "SD", "NON-CR/NON-PD", "PD", "NE", "MISSING",
"ORR (CR + PR)", "CBR (CR + PR + SD)",
"DCR (CR + PR + SD + NON-CR/NON-PD)", "95% CI (Clopper-Pearson)"
)
eff <- cdisc_eff_resp
eff$stat_label <- factor(eff$stat_label, levels = bor_levels)
tabular(
eff,
titles = c(
"Table 14.2.1",
"Best Overall Response and Response Rates",
"Efficacy Evaluable Population"
)
) |>
cols(
stat_label = col_spec(label = "Response"),
row_type = col_spec(visible = FALSE),
groupid = col_spec(visible = FALSE),
group_label = col_spec(visible = FALSE)
) |>
cols_apply(
\(nm) nm %in% c("placebo", "drug_50", "drug_100"),
col_spec(align = "decimal")
) |>
sort_rows(by = c("groupid", "stat_label"))
Table 14.2.1
Best Overall Response and Response Rates
Efficacy Evaluable Population
Response placebo drug_50 drug_100 CR 1 ( 1.2) 1 ( 1.2) 1 ( 1.2) PR 1 ( 1.2) 0 0 SD 1 ( 1.2) 0 0 NON-CR/NON-PD 0 0 1 ( 1.2) PD 0 0 1 ( 1.2) NE 0 1 ( 1.2) 0 MISSING 83 (96.5) 82 (97.6) 81 (96.4) ORR (CR + PR) 2 ( 2.3) 1 ( 1.2) 1 ( 1.2) 95% CI (Clopper-Pearson) ( 0.3, 8.1) ( 0.0, 6.5) ( 0.0, 6.5) CBR (CR + PR + SD) 3 ( 3.5) 1 ( 1.2) 1 ( 1.2) 95% CI (Clopper-Pearson) ( 0.7, 9.9) ( 0.0, 6.5) ( 0.0, 6.5) DCR (CR + PR + SD + NON-CR/NON-PD) 3 ( 3.5) 1 ( 1.2) 2 ( 2.4) 95% CI (Clopper-Pearson) ( 0.7, 9.9) ( 0.0, 6.5) ( 0.3, 8.3)