Exporting table1 Tables
v08-table1.Rmd
library(writetfl)
library(table1)
#>
#> Attaching package: 'table1'
#> The following objects are masked from 'package:base':
#>
#> units, units<-The table1
package creates publication-ready “Table 1” summary tables commonly used
in clinical and epidemiological reporting. writetfl accepts
table1 objects directly in export_tfl(),
preserving column labels, bold variable names, indented summary
statistics, and stratification headers.
Basic usage
Pass a table1 object directly to
export_tfl():
dat <- data.frame(
age = c(45, 52, 61, 38, 55, 47, 63, 41, 58, 50),
sex = c("Male", "Female", "Male", "Female", "Male",
"Female", "Male", "Female", "Male", "Female")
)
tbl <- table1(~ age + sex, data = dat)
export_tfl(tbl, preview = TRUE,
header_left = "Study Report",
header_rule = TRUE,
footer_rule = TRUE
)
Column labels
Use table1::label() to set variable labels. These are
used as the bold row headers in the output:
label(dat$age) <- "Age (years)"
label(dat$sex) <- "Sex"
tbl <- table1(~ age + sex, data = dat)
export_tfl(tbl, preview = TRUE,
header_left = "Labeled Variables"
)
Variable labels appear as bold row headings with the summary statistics indented beneath them.
Caption and footnote
The caption and footnote arguments to
table1() are automatically extracted into writetfl’s
caption and footnote zones:
tbl <- table1(~ age + sex, data = dat,
caption = "Table 1. Baseline Demographics",
footnote = "ITT Population (N = 10)")
export_tfl(tbl, preview = TRUE,
header_left = "Protocol XY-001",
header_right = "2025-01-15",
header_rule = TRUE,
footer_rule = TRUE
)
Stratification
Use the formula interface with | to stratify by a
grouping variable. Column headers show the stratum labels and sample
sizes:
dat$trt <- c(rep("Treatment", 5), rep("Placebo", 5))
label(dat$age) <- "Age (years)"
label(dat$sex) <- "Sex"
tbl <- table1(~ age + sex | trt, data = dat,
caption = "Table 1. Demographics by Treatment Group")
export_tfl(tbl, preview = TRUE,
header_left = "Study Report",
header_rule = TRUE,
footer_rule = TRUE
)
The overall argument adds a combined column:
tbl <- table1(~ age + sex | trt, data = dat, overall = "Total",
caption = "Table 1. Demographics with Overall")
export_tfl(tbl, preview = TRUE,
header_left = "Study Report",
header_rule = TRUE
)
Page layout
All export_tfl_page() layout arguments work with table1
tables:
tbl <- table1(~ age + sex | trt, data = dat,
caption = "Table 1. Demographics")
export_tfl(tbl, preview = TRUE,
header_left = "Protocol XY-001",
header_center = "CONFIDENTIAL",
header_right = "2025-01-15",
footnote = "Percentages based on non-missing values.",
footer_left = "Sponsor: Acme Corp",
header_rule = TRUE,
footer_rule = TRUE,
gp = list(
header = grid::gpar(fontsize = 11, fontface = "bold"),
caption = grid::gpar(fontsize = 9, fontface = "italic"),
footer = grid::gpar(fontsize = 8)
)
)
Multiple tables
Pass a list of table1 objects to create a multi-page
PDF:
tbl_age <- table1(~ age | trt, data = dat,
caption = "Table 1a. Age by Treatment")
tbl_sex <- table1(~ sex | trt, data = dat,
caption = "Table 1b. Sex by Treatment")
export_tfl(
list(tbl_age, tbl_sex),
preview = TRUE,
header_left = "Study Report",
header_rule = TRUE,
footer_rule = TRUE
)

Automatic pagination
When a table has too many variables to fit on a single page,
export_tfl() paginates automatically. Page breaks fall
between variable groups (a variable label and its summary rows are kept
together).
set.seed(42)
big_dat <- data.frame(
v01 = rnorm(50), v02 = rnorm(50), v03 = rnorm(50),
v04 = rnorm(50), v05 = rnorm(50), v06 = rnorm(50),
v07 = rnorm(50), v08 = rnorm(50), v09 = rnorm(50),
v10 = rnorm(50), v11 = rnorm(50), v12 = rnorm(50)
)
for (v in names(big_dat)) label(big_dat[[v]]) <- paste("Variable", v)
big_tbl <- table1(
~ v01 + v02 + v03 + v04 + v05 + v06 + v07 + v08 + v09 + v10 + v11 + v12,
data = big_dat,
caption = "Table 2. Many Variables"
)
export_tfl(big_tbl,
preview = 1:2,
pg_height = 5,
header_left = "Study Report",
header_rule = TRUE,
footer_rule = TRUE
)

Preserved features
| Feature | How it’s handled |
|---|---|
Column labels (label()) |
Bold variable-name rows in the table body |
| Indented summary statistics | Spaces preserved from t1flex() conversion |
Stratification (\| group) |
Column headers with stratum labels and N counts |
| Overall column | Included when overall is set |
| Caption | Extracted to writetfl caption zone |
| Footnote | Extracted to writetfl footnote zone |
| Custom render functions | Applied before conversion; output preserved |
Variable units (units()) |
Displayed in variable labels |
| Row label heading | Header for the first column |
| CSS styles | Translated to flextable formatting via t1flex()
|
| Group-aware pagination | Variable groups kept together across page breaks |
How it works
Under the hood, export_tfl() uses
table1::t1flex() to convert the table1 object to a flextable, then
renders the flextable to a grid grob via
flextable::gen_grob(). This preserves all visual formatting
including bold labels, indented statistics, borders, and spanning
headers.
Caption and footnote are extracted from the table1 object’s internal structure (not the flextable) to ensure they appear in writetfl’s annotation zones rather than being duplicated inside the table.