Table Styling and Formatting

library(sumExtras)
library(gtsummary)
library(dplyr)
library(gt)

use_jama_theme()

Group Headers

gtsummary::add_variable_group_header() creates section headers in your table. {sumExtras} provides functions to style them.

trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  )
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Demographics



    Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Clinical Measures



    Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
    Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
    T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
    Tumor Response 61 (32%) 28 (29%) 33 (34%) 0.530
    Unknown 7 3 4
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

The headers are there, but they don’t stand out. That’s where add_group_styling() comes in.

add_group_styling()

Adds bold and/or italic formatting to group headers. Also restores left-justified variable label indentation that add_variable_group_header() changes.

Without styling

trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  )

With add_group_styling()

trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  ) |>
  add_group_styling()
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Patient Variables



    Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
    Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
    Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
    T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Patient Variables



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

The format argument controls the text style:

# Bold only
trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Characteristics",
    variables = age:stage
  ) |>
  add_group_styling(format = "bold")
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Patient Characteristics



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

Options are "bold", "italic", or c("bold", "italic") (the default).

add_group_colors()

Adds a background color to group header rows. This is a terminal operation in that it converts the table to {gt}. It must be the last step in your pipeline.

trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  ) |>
  add_group_styling() |>
  add_group_colors(color = "#E3F2FD")
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Demographics



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Clinical Measures



Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
Tumor Response 61 (32%) 28 (29%) 33 (34%) 0.530
    Unknown 7 3 4
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

The default color is "#E8E8E8" (light gray). Pass any CSS color string, or a vector of colors (one per group):

trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  ) |>
  add_group_styling() |>
  add_group_colors(color = c("#E3F2FD", "#FFF9E6"))
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Demographics



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Clinical Measures



Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
Tumor Response 61 (32%) 28 (29%) 33 (34%) 0.530
    Unknown 7 3 4
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

get_group_rows()

If you need more control than add_group_colors() provides, get_group_rows() returns the row indices of group headers. You can then use those with gt::tab_style() directly:

my_table <- trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age:marker
  ) |>
  add_variable_group_header(
    header = "Disease",
    variables = grade:stage
  ) |>
  add_group_styling()

group_rows <- get_group_rows(my_table)

my_table |>
  as_gt() |>
  gt::tab_style(
    style = list(
      gt::cell_fill(color = "#E8E8E8"),
      gt::cell_text(weight = "bold")
    ),
    locations = gt::cells_body(rows = group_rows)
  )
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Demographics



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
Disease



Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

Matching gt Tables with theme_gt_compact()

If you mix {gtsummary} tables with plain {gt} tables in the same document, they won’t match visually. theme_gt_compact() applies the same JAMA compact look to {gt} tables so everything is consistent:

gtsummary with extras()

trial |>
  tbl_summary(
    by = trt,
    include = c(age, grade, marker)
  ) |>
  extras()

gt with theme_gt_compact()

trial |>
  select(trt, age, grade, marker) |>
  head(10) |>
  gt() |>
  theme_gt_compact()
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
    Unknown 11 7 4
Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
    Unknown 10 6 4
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test
Chemotherapy Treatment Age Grade Marker Level (ng/mL)
Drug A 23 II 0.160
Drug B 9 I 1.107
Drug A 31 II 0.277
Drug A NA III 2.067
Drug A 51 III 2.767
Drug B 39 I 0.613
Drug A 37 II 0.354
Drug A 32 I 1.739
Drug A 31 II 0.144
Drug B 34 I 0.205

See vignette("themes") for more on theming.

Complete Example

dictionary <- tibble::tribble(
  ~variable,    ~description,
  "trt",        "Treatment Assignment",
  "age",        "Age at Baseline (years)",
  "marker",     "Biomarker Level (ng/mL)",
  "stage",      "Clinical Stage",
  "grade",      "Tumor Grade",
  "response",   "Treatment Response",
  "death",      "Patient Died"
)

trial |>
  select(trt, age, marker, grade, stage, response, death) |>
  tbl_summary(by = trt, missing = "no") |>
  add_auto_labels(dictionary = dictionary) |>
  extras() |>
  add_variable_group_header(
    header = "BASELINE CHARACTERISTICS",
    variables = age:marker
  ) |>
  add_variable_group_header(
    header = "DISEASE CHARACTERISTICS",
    variables = grade:stage
  ) |>
  add_variable_group_header(
    header = "OUTCOMES",
    variables = response:death
  ) |>
  add_group_styling() |>
  add_group_colors(color = "#E8E8E8")
Overall
N = 200
1
Drug A
N = 98
1
Drug B
N = 102
1
p-value2
BASELINE CHARACTERISTICS



Age 47 (38, 57) 46 (37, 60) 48 (39, 56) 0.718
Marker Level (ng/mL) 0.64 (0.22, 1.41) 0.84 (0.23, 1.60) 0.52 (0.18, 1.21) 0.085
DISEASE CHARACTERISTICS



Grade


0.871
    I 68 (34%) 35 (36%) 33 (32%)
    II 68 (34%) 32 (33%) 36 (35%)
    III 64 (32%) 31 (32%) 33 (32%)
T Stage


0.866
    T1 53 (27%) 28 (29%) 25 (25%)
    T2 54 (27%) 25 (26%) 29 (28%)
    T3 43 (22%) 22 (22%) 21 (21%)
    T4 50 (25%) 23 (23%) 27 (26%)
OUTCOMES



Tumor Response 61 (32%) 28 (29%) 33 (34%) 0.530
Patient Died 112 (56%) 52 (53%) 60 (59%) 0.412
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

More Vignettes