derrick converts analysis-ready clinical tables into
files written by reporter. It accepts either a
gtsummary object or an already assembled plain data frame,
then applies report metadata, column labels, spanning headers, widths,
pagination controls, and output-format selection in one call to
derrick::gtsummary_reporter().
derrick uses reporter as the rendering
engine. The examples in this vignette continue to rely on
reporter for:
The main difference is the level of abstraction. With
reporter, the caller usually builds the report and table
specification directly. derrick builds that specification
from a gtsummary object or a plain data frame, then passes
it to reporter.
derrick adds a clinical-table workflow layer around
reporter:
gtsummary objects;column_labels and spanning_headers;title1,
footnote1, progname, and related numbered
variables;max_table_width,
column_widths, and max_chars_per_line;A typical workflow is:
title1,
footnote1, and progname.derrick::gtsummary_reporter() with the table,
output path, layout arguments, and requested
output_types.The examples below write TXT and HTML outputs so the results can be
displayed inside this HTML vignette. Use the same table specification
with output_types = c("RTF", "DOCX", "PDF"), or any
supported subset, when final delivery files are needed.
The most common input is a gtsummary object. In this
example, the analysis layer creates a demographic summary with a p-value
column and a spanning header. derrick then exports the
displayed table structure.
demog_tbl <- gtsummary::trial |>
dplyr::select(trt, age, grade) |>
gtsummary::tbl_summary(by = trt) |>
gtsummary::add_p() |>
gtsummary::modify_spanning_header(
gtsummary::all_stat_cols() ~ "**Treatment Group**"
)
demog_tbl| Characteristic |
Treatment Group
|
p-value2 | |
|---|---|---|---|
| Drug A N = 981 |
Drug B N = 1021 |
||
| Age | 46 (37, 60) | 48 (39, 56) | 0.7 |
| Unknown | 7 | 4 | |
| Grade | 0.9 | ||
| I | 35 (36%) | 33 (32%) | |
| II | 32 (33%) | 36 (35%) | |
| III | 31 (32%) | 33 (32%) | |
| 1 Median (Q1, Q3); n (%) | |||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | |||
For gtsummary inputs, derrick reads
displayed columns, column labels, indentation rules, p-values, and
spanning headers from the object.
preview_cols <- intersect(
c("label", "stat_1", "stat_2", "p.value"),
names(demog_tbl$table_body)
)
utils::head(demog_tbl$table_body[preview_cols])
#> # A tibble: 6 × 4
#> label stat_1 stat_2 p.value
#> <chr> <chr> <chr> <dbl>
#> 1 Age 46 (37, 60) 48 (39, 56) 0.718
#> 2 Unknown 7 4 NA
#> 3 Grade <NA> <NA> 0.871
#> 4 I 35 (36%) 33 (32%) NA
#> 5 II 32 (33%) 36 (35%) NA
#> 6 III 31 (32%) 33 (32%) NAtitle1 <- "Table 14.1.1 Summary of Demographics"
title2 <- "Safety Population"
footnote1 <- "Percentages are based on subjects in each treatment group."
progname <- "programs/t-demog.R"The file_path argument supplies the output stem. The
actual formats written are controlled by output_types, and
the function returns the generated file paths.
demog_paths <- gtsummary_reporter(
gts_obj = demog_tbl,
file_path = file.path(output_dir, "t-demog.rtf"),
output_types = c("TXT", "HTML"),
save_rds = TRUE
)
output_manifest(demog_paths)
#> format file bytes
#> 1 TXT t-demog.txt 856
#> 2 HTML t-demog.html 3984TXT output is useful for checking fixed-width alignment, indentation, page titles, footnotes, and line wrapping.
Table 14.1.1 Summary of Demographics
Safety Population
______________________________________________
Treatment Group
_______________________
Drug A Drug B
N = 98 N = 102
Characteristic N (%) N (%) p-value
______________________________________________
Age 46 (37, 60) 48 (39, 56) 0.7183
Unknown 7 4
Grade 0.8708
I 35 (36%) 33 (32%)
II 32 (33%) 36 (35%)
III 31 (32%) 33 (32%)
______________________________________________
Percentages are based on subjects in each tr...
programs/t-demog.R 03JUL2026 19:03
HTML output is useful for a quick browser review of proportional-width table layout and header styling.
Different gtsummary table types use the same export
function. This example uses tbl_cross() to create a
treatment-by-grade frequency table.
cross_tbl <- gtsummary::trial |>
dplyr::select(trt, grade) |>
gtsummary::tbl_cross(
row = grade,
col = trt,
percent = "row"
) |>
gtsummary::modify_spanning_header(
gtsummary::all_stat_cols() ~ "**Treatment Group**"
)
cross_tbl|
Treatment Group
|
|||
|---|---|---|---|
| Drug A | Drug B | Total | |
| Grade | |||
| I | 35 (51%) | 33 (49%) | 68 (100%) |
| II | 32 (47%) | 36 (53%) | 68 (100%) |
| III | 31 (48%) | 33 (52%) | 64 (100%) |
| Total | 98 (49%) | 102 (51%) | 200 (100%) |
title1 <- "Table 14.1.2 Grade by Treatment"
title2 <- "Safety Population"
footnote1 <- "Percentages are row percentages."
progname <- "programs/t-grade-cross.R"
cross_paths <- gtsummary_reporter(
gts_obj = cross_tbl,
file_path = file.path(output_dir, "t-grade-cross.rtf"),
output_types = c("TXT", "HTML"),
save_rds = FALSE
)
output_manifest(cross_paths)
#> format file bytes
#> 1 TXT t-grade-cross.txt 674
#> 2 HTML t-grade-cross.html 3640Table 14.1.2 Grade by Treatment
Safety Population
_____________________________________
Treatment Group
_____________________________
Drug A Drug B Total
N (%) N (%) N (%)
_____________________________________
Grade
I 35 (51%) 33 (49%) 68 (100%)
II 32 (47%) 36 (53%) 68 (100%)
III 31 (48%) 33 (52%) 64 (100%)
Total 98 (49%) 102 (51%) 200 (100%)
_____________________________________
Percentages are row percentages.
programs/t-grade-cross.R 03JUL2026 19:03
Use a plain data frame when the table has already been assembled
outside gtsummary, for example from a validated analysis
data set or a custom summary program. In this mode, provide report
labels and spanning headers explicitly, because there is no
gtsummary styling metadata to read.
ae_tbl <- data.frame(
label = c(
"Serious adverse events",
" Subjects with at least one event",
" Events leading to study drug interruption",
"Treatment-emergent adverse events",
" Headache",
" Nausea",
" Alanine aminotransferase increased"
),
placebo = c("", "2 (10.0%)", "1 (5.0%)", "", "6 (30.0%)", "4 (20.0%)", "1 (5.0%)"),
active_low = c("", "1 (5.0%)", "0", "", "5 (25.0%)", "3 (15.0%)", "2 (10.0%)"),
active_high = c("", "3 (15.0%)", "2 (10.0%)", "", "7 (35.0%)", "5 (25.0%)", "3 (15.0%)"),
total = c("", "6 (10.0%)", "3 (5.0%)", "", "18 (30.0%)", "12 (20.0%)", "6 (10.0%)"),
stringsAsFactors = FALSE
)
ae_tbl
#> label placebo active_low active_high
#> 1 Serious adverse events
#> 2 Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%)
#> 3 Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%)
#> 4 Treatment-emergent adverse events
#> 5 Headache 6 (30.0%) 5 (25.0%) 7 (35.0%)
#> 6 Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%)
#> 7 Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%)
#> total
#> 1
#> 2 6 (10.0%)
#> 3 3 (5.0%)
#> 4
#> 5 18 (30.0%)
#> 6 12 (20.0%)
#> 7 6 (10.0%)title1 <- "Table 14.3.1 Overview of Adverse Events"
title2 <- "Safety Population"
footnote1 <- "Events are counted once per subject within each row."
progname <- "programs/t-ae-overview.R"
ae_paths <- gtsummary_reporter(
gts_obj = ae_tbl,
file_path = file.path(output_dir, "t-ae-overview.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
output_types = c("TXT", "HTML"),
save_rds = FALSE
)
output_manifest(ae_paths)
#> format file bytes
#> 1 TXT t-ae-overview.txt 1338
#> 2 HTML t-ae-overview.html 4612Table 14.3.1 Overview of Adverse Events
Safety Population
____________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
____________________________________________________________________________________
Serious adverse events
Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%) 6 (10.0%)
Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%) 3 (5.0%)
Treatment-emergent adverse events
Headache 6 (30.0%) 5 (25.0%) 7 (35.0%) 18 (30.0%)
Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%) 12 (20.0%)
Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
____________________________________________________________________________________
Events are counted once per subject within each row.
programs/t-ae-overview.R 03JUL2026 19:03
The same table can look very different depending on the width
controls. Start with reporter-managed automatic widths, then use
max_table_width or column_widths when the
report shell requires a specific layout.
With no width overrides, derrick passes no column widths
to reporter. reporter then estimates the
display columns from the target output format, font settings, headers,
table content, and available page width.
title1 <- "Table 14.3.1 Overview of Adverse Events"
title2 <- "Automatic column widths"
footnote1 <- "This version lets reporter estimate the width of each column."
progname <- "programs/t-ae-overview.R"
ae_auto_paths <- gtsummary_reporter(
gts_obj = ae_tbl,
file_path = file.path(output_dir, "t-ae-auto.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
output_types = "TXT",
save_rds = FALSE
)
basename(ae_auto_paths)
#> [1] "t-ae-auto.txt"Table 14.3.1 Overview of Adverse Events
Automatic column widths
____________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
____________________________________________________________________________________
Serious adverse events
Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%) 6 (10.0%)
Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%) 3 (5.0%)
Treatment-emergent adverse events
Headache 6 (30.0%) 5 (25.0%) 7 (35.0%) 18 (30.0%)
Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%) 12 (20.0%)
Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
____________________________________________________________________________________
This version lets reporter estimate the width of each column.
programs/t-ae-overview.R 03JUL2026 19:03
max_table_width caps the whole table by passing a
table-width constraint to reporter. A narrower width forces
long labels to wrap sooner and leaves less room for treatment
columns.
title1 <- "Table 14.3.1 Overview of Adverse Events"
title2 <- "Narrow table width"
footnote1 <- "This version caps the total table width at 5.5 inches."
progname <- "programs/t-ae-overview.R"
ae_narrow_paths <- gtsummary_reporter(
gts_obj = ae_tbl,
file_path = file.path(output_dir, "t-ae-narrow.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
max_table_width = 5.5,
output_types = "TXT",
save_rds = FALSE
)
basename(ae_narrow_paths)
#> [1] "t-ae-narrow.txt"Table 14.3.1 Overview of Adverse Events
Narrow table width
____________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
____________________________________________________________________________________
Serious adverse events
Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%) 6 (10.0%)
Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%) 3 (5.0%)
Treatment-emergent adverse events
Headache 6 (30.0%) 5 (25.0%) 7 (35.0%) 18 (30.0%)
Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%) 12 (20.0%)
Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
____________________________________________________________________________________
This version caps the total table width at 5.5 inches.
programs/t-ae-overview.R 03JUL2026 19:03
column_widths assigns widths in display-column order.
For this data frame, the order is label,
placebo, active_low, active_high,
and total. Manual widths are useful when the label column
needs more space than reporter’s automatic estimate provides.
title1 <- "Table 14.3.1 Overview of Adverse Events"
title2 <- "Manual column widths"
footnote1 <- "This version gives more room to the label column."
progname <- "programs/t-ae-overview.R"
ae_manual_paths <- gtsummary_reporter(
gts_obj = ae_tbl,
file_path = file.path(output_dir, "t-ae-manual.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
column_widths = "3.8|1.1|1.3|1.3|1.1",
output_types = "TXT",
save_rds = FALSE
)
basename(ae_manual_paths)
#> [1] "t-ae-manual.txt"Table 14.3.1 Overview of Adverse Events
Manual column widths
_______________________________________________________________________________________________________
Treatment Group
_________________________________________________________
System Organ Class / Preferred Term Placebo Active Low Active High Total
_______________________________________________________________________________________________________
Serious adverse events
Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%) 6 (10.0%)
Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%) 3 (5.0%)
Treatment-emergent adverse events
Headache 6 (30.0%) 5 (25.0%) 7 (35.0%) 18 (30.0%)
Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%) 12 (20.0%)
Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
_______________________________________________________________________________________________________
This version gives more room to the label column.
programs/t-ae-overview.R 03JUL2026 19:03
By default, rows_per_page = NULL leaves pagination to
reporter. Set rows_per_page when a table shell
requires predictable manual chunks before the report is written. The
example below expands the adverse event table and starts a new page
after every seven data rows.
paged_ae_tbl <- rbind(
ae_tbl,
data.frame(
label = c(
"Skin and subcutaneous tissue disorders",
" Rash",
" Pruritus",
"Respiratory, thoracic and mediastinal disorders",
" Cough",
" Oropharyngeal pain",
"Gastrointestinal disorders",
" Diarrhea",
" Abdominal pain",
"Investigations",
" Blood creatine phosphokinase increased"
),
placebo = c(
"", "2 (10.0%)", "1 (5.0%)", "", "3 (15.0%)", "1 (5.0%)",
"", "2 (10.0%)", "1 (5.0%)", "", "1 (5.0%)"
),
active_low = c(
"", "3 (15.0%)", "1 (5.0%)", "", "2 (10.0%)", "2 (10.0%)",
"", "4 (20.0%)", "2 (10.0%)", "", "2 (10.0%)"
),
active_high = c(
"", "4 (20.0%)", "2 (10.0%)", "", "5 (25.0%)", "2 (10.0%)",
"", "5 (25.0%)", "3 (15.0%)", "", "4 (20.0%)"
),
total = c(
"", "9 (15.0%)", "4 (6.7%)", "", "10 (16.7%)", "5 (8.3%)",
"", "11 (18.3%)", "6 (10.0%)", "", "7 (11.7%)"
),
stringsAsFactors = FALSE
)
)
nrow(paged_ae_tbl)
#> [1] 18title1 <- "Table 14.3.2 Adverse Events by Body System"
title2 <- "Manual pagination"
footnote1 <- "This version starts a new report page after every seven data rows."
progname <- "programs/t-ae-paged.R"
paged_paths <- gtsummary_reporter(
gts_obj = paged_ae_tbl,
file_path = file.path(output_dir, "t-ae-paged.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
rows_per_page = 7,
output_types = c("TXT", "HTML"),
save_rds = FALSE
)
output_manifest(paged_paths)
#> format file bytes
#> 1 TXT t-ae-paged.txt 3845
#> 2 HTML t-ae-paged.html 12019TXT output repeats the title, table header, footnote, and footer after each manual page break.
Table 14.3.2 Adverse Events by Body System
Manual pagination
____________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
____________________________________________________________________________________
Serious adverse events
Subjects with at least one event 2 (10.0%) 1 (5.0%) 3 (15.0%) 6 (10.0%)
Events leading to study drug interruption 1 (5.0%) 0 2 (10.0%) 3 (5.0%)
Treatment-emergent adverse events
Headache 6 (30.0%) 5 (25.0%) 7 (35.0%) 18 (30.0%)
Nausea 4 (20.0%) 3 (15.0%) 5 (25.0%) 12 (20.0%)
Alanine aminotransferase increased 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
____________________________________________________________________________________
This version starts a new report page after every seven data rows.
programs/t-ae-paged.R 03JUL2026 19:03
Table 14.3.2 Adverse Events by Body System
Manual pagination
________________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
________________________________________________________________________________________
Skin and subcutaneous tissue disorders
Rash 2 (10.0%) 3 (15.0%) 4 (20.0%) 9 (15.0%)
Pruritus 1 (5.0%) 1 (5.0%) 2 (10.0%) 4 (6.7%)
Respiratory, thoracic and mediastinal disorders
Cough 3 (15.0%) 2 (10.0%) 5 (25.0%) 10 (16.7%)
Oropharyngeal pain 1 (5.0%) 2 (10.0%) 2 (10.0%) 5 (8.3%)
Gastrointestinal disorders
________________________________________________________________________________________
This version starts a new report page after every seven data rows.
programs/t-ae-paged.R 03JUL2026 19:03
Table 14.3.2 Adverse Events by Body System
Manual pagination
_________________________________________________________________________________
Treatment Group
________________________________________
Active Active
System Organ Class / Preferred Term Placebo Low High Total
_________________________________________________________________________________
Diarrhea 2 (10.0%) 4 (20.0%) 5 (25.0%) 11 (18.3%)
Abdominal pain 1 (5.0%) 2 (10.0%) 3 (15.0%) 6 (10.0%)
Investigations
Blood creatine phosphokinase increased 1 (5.0%) 2 (10.0%) 4 (20.0%) 7 (11.7%)
_________________________________________________________________________________
This version starts a new report page after every seven data rows.
programs/t-ae-paged.R 03JUL2026 19:03
These arguments are the main controls to review when moving from a draft table to a production report shell:
output_types chooses the files to write. This vignette
runs TXT and HTML because they can be rendered directly inside an HTML
page.column_labels overrides displayed headers for plain
data frames or special sponsor wording.spanning_headers adds or overrides multi-column
headers.max_table_width passes a total table-width constraint
to reporter.column_widths controls exact column allocation.max_chars_per_line constrains TXT output to a known
character budget.rows_per_page forces manual chunks before
reporter writes the report; leave it NULL when
reporter’s automatic pagination is sufficient.gtsummary_reporter(
gts_obj = ae_tbl,
file_path = file.path(output_dir, "t-ae-final.rtf"),
column_labels = c(
label = "System Organ Class / Preferred Term",
placebo = "Placebo",
active_low = "Active Low",
active_high = "Active High",
total = "Total"
),
spanning_headers = data.frame(
from = "placebo",
to = "total",
label = "Treatment Group"
),
column_widths = "3.8|1.1|1.3|1.3|1.1",
max_chars_per_line = 132,
rows_per_page = 24,
output_types = c("RTF", "TXT", "HTML")
)Before treating generated files as final, check that:
gtsummary object
or plain data frame);rows_per_page is used;reporter.