Skip to contents

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.

Usage

cols_apply(.spec, .cols, .col_spec)

Arguments

.spec

The tabular_spec to 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 predicate function(names) -> logical evaluated against names(.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 of names(.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 with col_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

 

Statisticplacebodrug_50drug_100Total
Age (years)
Mean (SD)75.2 (8.59)76.0 (8.11)73.8 (7.94) 75.1 (8.25)
Median76.0       78.0       75.5        77.0       
Min, Max52  , 89   51  , 88   56  , 88    51  , 89   
Q1, Q369.2, 81.8 71.0, 82.0 70.5, 79.0  70.0, 81.0 
n86         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)  
WHITE78 (90.7)  90 (93.8)  62 (86.1)  230 (90.6)  
 
Sex, n (%)
F53 (61.6)  55 (57.3)  35 (48.6)  143 (56.3)  
M33 (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

 

Responseplacebodrug_50drug_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         
MISSING83   (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)