Demographic Tables with flextable


Clinical studies often begin with a summary table of the demographic characteristics of the patients ncluded in the study to demonstrate a balance between treatments and other subgroups. This table is called “Demographic Tables”.

The format is standard and can be realized with flextable in 2 operations.

The two steps in creating these tables are:

  1. summarize the information with the flextable::summarizor() function. It computes a set of statistics for each variable by groups. It returns a data.frame ready to be consumed by flextable::as_flextable().
  2. Create the flextable with the as_flextable() function and customize it with the ‘flextable’ functions.

Example data



  border.color = "#AAAAAA", = "Arial",
  font.size = 10, padding = 2, line_spacing = 1.5
adsl <- select(adam_adsl, AGE, SEX, BMIBLGR1, DURDIS, ARM)


We will use the function flextable::summarizor(). It produces an aggregated data.frame structured for flextable::as_flextable().

dat <- summarizor(adsl, by = "ARM")

flextable creation

We want a visual where the treatments are distributed in columns and where the content of the paragraphs is flexible enough to allow the creation of the table.

ft <- as_flextable(dat)

The flextable::as_flextable() method supports the arguments of the flextable::tabulator() method, we’ll use the spread_first_col = TRUE argument to split the variable names as separator lines and not display the column anymore. We will also add a caption and a note at the bottom of the table and some additional parameters.

ft <- as_flextable(dat, spread_first_col = TRUE, separate_with = "variable") %>%
  bold(i = ~ !, j = 1, bold = TRUE) %>%
    autonum = officer::run_autonum(seq_id = "tab", bkm = "demo_tab", bkm_all = FALSE),
    fp_p = officer::fp_par(text.align = "left", padding = 5),
    align_with_table = FALSE,
    caption = as_paragraph(
          "Demographic Characteristics",
          "\nx.x: Study Subject Data"
  ) %>% 
  add_footer_lines("Source: ADaM adsl data frame from r package 'safetyData'") %>% 
  fix_border_issues() %>% 



This part will allow us to retrieve a set of labels that we will use to replace some texts displayed in the flextable.

The function to use is labelizor(), it takes a simple argument as a named vector, the names are the values to replace, the values are the replacement values.

We will retrieve the labels of the columns stored in the original table.

col_labels <- map_chr(adsl, function(x) attr(x, "label"))
##                            AGE                            SEX 
##                          "Age"                          "Sex" 
##                       BMIBLGR1                         DURDIS 
##  "Pooled Baseline BMI Group 1" "Duration of Disease (Months)" 
##                            ARM 
##   "Description of Planned Arm"

ft <- labelizor(ft, j = "stat", labels = col_labels, part = "all")

In a Word document

We will prepare the table for exportation in a Word document by adding a header line with the page number. The caption will be auto-numbered and left aligned in the document.

ft %>%
  add_header_lines("Page ") %>%
  append_chunks(i = 1, part = "header", j = 1, as_word_field(x = "Page")) %>% 
  save_as_docx(path = "adsl.docx")

The resulting Word document can be downloaded here: adsl.docx. A miniature below show the expected document.

Word miniatures