group_rows() names the structural columns whose runs of
identical values define the table's row hierarchy, ordered outer to
inner. List only the keys that drive the structure — the section
headers and any hidden break keys; the visible row-label column
(e.g. the statistic stub) stays an ordinary cols() column and is
indented automatically. It is the row-structure counterpart of
sort_rows(): one declaration per table, replaced wholesale on
a repeat call.
Arguments
- .spec
The
tabular_specto attach the grouping plan to.<tabular_spec>: required.- by
Structural grouping key columns, ordered outer to inner.
<character>: required. Names at least one column ofdata; duplicates are rejected. List only the section-header and break-only keys, not the visible label column.Interaction: A
subgroup()(by = )partition column may also be a grouping key; within each partition the key is constant and auto-hidden, so the combination composes.- display
How the keys' values render in the body.
<character(1)>: default "section". One value, applied to every key:"section"(default) — each unique value emits a section header row spanning the visible columns; the key column is hidden from the body. The canonical submission shape."collapse"— the key column stays visible; repeated values are suppressed so only the first row of each run shows the label. The classic listing shape."repeat"— the key column stays visible and every row repeats the value. The export / QC shape, where every row must be self-describing.
Tip: for a hidden break-only key, set
col_spec(visible = FALSE)on it rather than a display mode.- skip
Which keys get a blank spacer row between their groups.
<TRUE | FALSE | character>: default TRUE. A logical flag or an explicit character set (thereadr::read_csv(col_names = )pattern):TRUE(default) — derive: a"section"key or a break-only (visible = FALSE) key breaks with a blank line; a visible"collapse"/"repeat"key runs continuous.FALSE— no spacer rows anywhere.<character>— exactly thesebykeys break, e.g.skip = "param"(blank line between params, none between visits). Every name must be inby;character(0)is equivalent toFALSE.
Value
<tabular_spec>. A new spec with @row_groups replaced;
pipe into the remaining build verbs or emit().
Details
One plan per table. A second group_rows() call replaces the
first (the sort_rows() contract); levels never accumulate
across calls.
Structural keys only. Nesting is just listing the header keys:
by = c("param", "visit") renders param as the outer section
header and visit as the indented sub-header, and the first visible
column beneath (the label stub) is auto-indented one level per
header. You do not put the label column in by.
Break-only keys use visible = FALSE. A key you mark
col_spec(visible = FALSE) renders nothing and contributes only
group transitions — the blank spacer between blocks (skip) and the
decimal-alignment reset — exactly what a hidden sort/break key needs.
There is no separate display mode for it.
See also
Column display: cols() / col_spec() for labels,
alignment, and visibility of the key columns.
Row order: sort_rows() — sort so each key's runs are
contiguous before grouping.
Pagination: paginate() — the grouping keys form the
default panel stub; keep_together protects runs across page
breaks independently of grouping.
Examples
# ---- Example 1: Demographics with section headers and a stat column ----
#
# The canonical demographics shape: `variable` is the one structural
# key. The defaults do all the work -- `display = "section"` renders
# one section header row per parameter (Age, Sex, ...) and hides the
# key column; `skip = TRUE` derives a blank spacer between sections.
# `stat_label` is NOT a grouping key -- it stays an ordinary column
# and is auto-indented one level under each section header.
n <- stats::setNames(cdisc_saf_n$n, cdisc_saf_n$arm_short)
tabular(
cdisc_saf_demo,
titles = c("Table 14.1.1", "Demographics", "Safety Population")
) |>
cols(
variable = "Parameter",
stat_label = "Statistic",
placebo = "Placebo\nN={n['placebo']}",
drug_50 = "Drug 50\nN={n['drug_50']}",
drug_100 = "Drug 100\nN={n['drug_100']}",
Total = "Total\nN={n['Total']}"
) |>
cols_apply(
c("placebo", "drug_50", "drug_100", "Total"),
col_spec(align = "decimal")
) |>
group_rows(by = "variable")
Table 14.1.1
Demographics
Safety Population
Statistic Placebo
N=86 Drug 50
N=96 Drug 100
N=72 Total
N=254 Age (years) n 86 96 72 254 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 Q1, Q3 69.2, 81.8 71.0, 82.0 70.5, 79.0 70.0, 81.0 Min, Max 52 , 89 51 , 88 56 , 88 51 , 89 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) Race, n (%) WHITE 78 (90.7) 90 (93.8) 62 (86.1) 230 (90.6) BLACK OR AFRICAN AMERICAN 8 ( 9.3) 6 ( 6.2) 9 (12.5) 23 ( 9.1) ASIAN 0 0 0 0 AMERICAN INDIAN OR ALASKA NATIVE 0 0 1 ( 1.4) 1 ( 0.4)
# ---- Example 2: Nested parameter / visit with a hidden break key ----
#
# `param` and `visit` nest as section headers (outer then indented
# sub-header) just by listing both -- no per-key modes. `paramcd`
# never renders: col_spec(visible = FALSE) makes it a break-only key
# whose transitions reset the decimal-alignment sections. `skip =
# "param"` puts a blank line between parameters but not between
# visits within a parameter.
tabular(cdisc_saf_vital, titles = "Vital Signs by Parameter and Visit") |>
cols(
paramcd = col_spec(visible = FALSE),
param = "Parameter",
visit = "Visit",
stat_label = "Statistic",
placebo = "Placebo\nN={n['placebo']}",
drug_50 = "Drug 50\nN={n['drug_50']}",
drug_100 = "Drug 100\nN={n['drug_100']}"
) |>
cols_apply(
c("placebo", "drug_50", "drug_100"),
col_spec(align = "decimal")
) |>
group_rows(by = c("param", "visit"), skip = "param")
Vital Signs by Parameter and Visit
Statistic Placebo
N=86 Drug 50
N=96 Drug 100
N=72 Diastolic Blood Pressure (mmHg) Baseline n 340 384 288 Mean (SD) 77.1 (10.7) 76.6 ( 9.8) 78.2 (10.3) Median 77.7 76.7 78.8 Min, Max 40 , 110 48 , 108 51 , 108 Week 8 n 292 240 224 Mean (SD) 75.2 ( 9.1) 75.4 (10.6) 77.4 ( 9.1) Median 76.0 74.0 78.3 Min, Max 49 , 101 52 , 100 54 , 98 Week 16 n 272 168 148 Mean (SD) 75.1 (10.9) 75.2 (10.0) 76.0 ( 9.0) Median 76.0 75.7 77.3 Min, Max 49 , 98 55 , 98 50 , 92 End of Treatment n 222 177 168 Mean (SD) 74.4 (10.7) 76.0 (11.2) 76.0 ( 9.9) Median 73.5 76.0 78.0 Min, Max 49 , 104 50 , 100 56 , 98 Pulse Rate (beats/min) Baseline n 340 384 288 Mean (SD) 73.5 (11.6) 72.1 (10.8) 72.4 (9.7) Median 72.3 70.0 71.7 Min, Max 51 , 134 50 , 104 52 , 100 Week 8 n 292 240 224 Mean (SD) 71.8 ( 9.0) 72.6 (11.1) 74.0 (8.9) Median 72.0 72.0 73.2 Min, Max 52 , 102 49 , 104 50 , 104 Week 16 n 272 168 148 Mean (SD) 70.6 ( 8.8) 68.8 ( 9.4) 73.2 (9.5) Median 70.2 68.0 72.0 Min, Max 50 , 90 48 , 104 51 , 96 End of Treatment n 222 177 168 Mean (SD) 75.2 (11.5) 74.1 ( 9.4) 73.6 (9.6) Median 74.0 75.0 73.0 Min, Max 51 , 106 50 , 94 50 , 98 Systolic Blood Pressure (mmHg) Baseline n 340 384 288 Mean (SD) 136.8 (17.6) 137.9 (18.5) 137.8 (17.2) Median 136.3 138.0 138.0 Min, Max 80 , 184 100 , 194 100 , 192 Week 8 n 292 240 224 Mean (SD) 136.3 (17.0) 134.9 (17.8) 135.1 (15.5) Median 136.5 132.3 134.0 Min, Max 90 , 189 92 , 200 91 , 198 Week 16 n 272 168 148 Mean (SD) 134.6 (18.3) 132.5 (14.3) 133.7 (16.0) Median 134.0 130.0 132.0 Min, Max 76 , 190 100 , 168 99 , 186 End of Treatment n 222 177 168 Mean (SD) 132.7 (15.4) 133.0 (17.1) 132.3 (15.6) Median 131.0 130.0 131.0 Min, Max 78 , 172 92 , 178 100 , 177 Temperature (C) Baseline n 172 190 144 Mean (SD) 36.6 (0.4) 36.5 (0.4) 36.6 (0.4) Median 36.7 36.6 36.6 Min, Max 35 , 37 35 , 37 36 , 37 Week 8 n 146 118 112 Mean (SD) 36.6 (0.4) 36.6 (0.4) 36.6 (0.4) Median 36.6 36.7 36.7 Min, Max 36 , 37 36 , 37 36 , 37 Week 16 n 136 82 74 Mean (SD) 36.7 (0.3) 36.6 (0.4) 36.6 (0.4) Median 36.7 36.6 36.7 Min, Max 36 , 37 36 , 37 36 , 37 End of Treatment n 74 59 56 Mean (SD) 36.7 (0.4) 36.6 (0.4) 36.6 (0.4) Median 36.8 36.7 36.7 Min, Max 35 , 37 35 , 38 36 , 37
# ---- Example 3: Listing shape with a visible, collapsed key column ----
#
# The same vitals data as a continuous listing: `display = "collapse"`
# keeps `param` and `visit` as visible columns and suppresses repeated
# values, so only the first row of each run shows the label; `skip =
# FALSE` removes every blank spacer. Use `display = "repeat"` instead
# to print the value on every row -- the export / QC shape.
tabular(cdisc_saf_vital, titles = "Vital Signs Listing") |>
cols(
paramcd = col_spec(visible = FALSE),
param = "Parameter",
visit = "Visit",
stat_label = "Statistic",
placebo = "Placebo\nN={n['placebo']}",
drug_50 = "Drug 50\nN={n['drug_50']}",
drug_100 = "Drug 100\nN={n['drug_100']}"
) |>
cols_apply(
c("placebo", "drug_50", "drug_100"),
col_spec(align = "decimal")
) |>
group_rows(by = c("param", "visit"), display = "collapse", skip = FALSE)
Vital Signs Listing
Parameter Visit Statistic Placebo
N=86 Drug 50
N=96 Drug 100
N=72 Diastolic Blood Pressure (mmHg) Baseline n 340 384 288 Mean (SD) 77.1 (10.7) 76.6 ( 9.8) 78.2 (10.3) Median 77.7 76.7 78.8 Min, Max 40 , 110 48 , 108 51 , 108 Week 8 n 292 240 224 Mean (SD) 75.2 ( 9.1) 75.4 (10.6) 77.4 ( 9.1) Median 76.0 74.0 78.3 Min, Max 49 , 101 52 , 100 54 , 98 Week 16 n 272 168 148 Mean (SD) 75.1 (10.9) 75.2 (10.0) 76.0 ( 9.0) Median 76.0 75.7 77.3 Min, Max 49 , 98 55 , 98 50 , 92 End of Treatment n 222 177 168 Mean (SD) 74.4 (10.7) 76.0 (11.2) 76.0 ( 9.9) Median 73.5 76.0 78.0 Min, Max 49 , 104 50 , 100 56 , 98 Pulse Rate (beats/min) Baseline n 340 384 288 Mean (SD) 73.5 (11.6) 72.1 (10.8) 72.4 ( 9.7) Median 72.3 70.0 71.7 Min, Max 51 , 134 50 , 104 52 , 100 Week 8 n 292 240 224 Mean (SD) 71.8 ( 9.0) 72.6 (11.1) 74.0 ( 8.9) Median 72.0 72.0 73.2 Min, Max 52 , 102 49 , 104 50 , 104 Week 16 n 272 168 148 Mean (SD) 70.6 ( 8.8) 68.8 ( 9.4) 73.2 ( 9.5) Median 70.2 68.0 72.0 Min, Max 50 , 90 48 , 104 51 , 96 End of Treatment n 222 177 168 Mean (SD) 75.2 (11.5) 74.1 ( 9.4) 73.6 ( 9.6) Median 74.0 75.0 73.0 Min, Max 51 , 106 50 , 94 50 , 98 Systolic Blood Pressure (mmHg) Baseline n 340 384 288 Mean (SD) 136.8 (17.6) 137.9 (18.5) 137.8 (17.2) Median 136.3 138.0 138.0 Min, Max 80 , 184 100 , 194 100 , 192 Week 8 n 292 240 224 Mean (SD) 136.3 (17.0) 134.9 (17.8) 135.1 (15.5) Median 136.5 132.3 134.0 Min, Max 90 , 189 92 , 200 91 , 198 Week 16 n 272 168 148 Mean (SD) 134.6 (18.3) 132.5 (14.3) 133.7 (16.0) Median 134.0 130.0 132.0 Min, Max 76 , 190 100 , 168 99 , 186 End of Treatment n 222 177 168 Mean (SD) 132.7 (15.4) 133.0 (17.1) 132.3 (15.6) Median 131.0 130.0 131.0 Min, Max 78 , 172 92 , 178 100 , 177 Temperature (C) Baseline n 172 190 144 Mean (SD) 36.6 ( 0.4) 36.5 ( 0.4) 36.6 ( 0.4) Median 36.7 36.6 36.6 Min, Max 35 , 37 35 , 37 36 , 37 Week 8 n 146 118 112 Mean (SD) 36.6 ( 0.4) 36.6 ( 0.4) 36.6 ( 0.4) Median 36.6 36.7 36.7 Min, Max 36 , 37 36 , 37 36 , 37 Week 16 n 136 82 74 Mean (SD) 36.7 ( 0.3) 36.6 ( 0.4) 36.6 ( 0.4) Median 36.7 36.6 36.7 Min, Max 36 , 37 36 , 37 36 , 37 End of Treatment n 74 59 56 Mean (SD) 36.7 ( 0.4) 36.6 ( 0.4) 36.6 ( 0.4) Median 36.8 36.7 36.7 Min, Max 35 , 37 35 , 38 36 , 37