Clinical Demographics Table
Demographic summary tables appear in nearly every clinical trial report. This example builds one from scratch with two treatment arms, categorical and continuous variables, missing data, and regulatory-style footnotes.
The data
julia
using StyledTables, DataFrames, SummaryTables
demo = DataFrame(
variable = [
"Sex", "Sex", "Sex",
"Age (years)", "Age (years)", "Age (years)",
"Race", "Race", "Race", "Race",
],
category = [
"Male", "Female", "Unknown",
"Mean (SD)", "Median", "Range",
"White", "Black or African American", "Asian", "Other/Unknown",
],
placebo_n = [22, 28, missing, "48.3 (12.1)", "47", "24–72", 36, 8, 3, 3],
treatment_n = [19, 30, 1, "49.7 (11.8)", "50", "22–74", 32, 9, 5, 4],
)10×4 DataFrame
| Row | variable | category | placebo_n | treatment_n |
|---|---|---|---|---|
| String | String | Any | Any | |
| 1 | Sex | Male | 22 | 19 |
| 2 | Sex | Female | 28 | 30 |
| 3 | Sex | Unknown | missing | 1 |
| 4 | Age (years) | Mean (SD) | 48.3 (12.1) | 49.7 (11.8) |
| 5 | Age (years) | Median | 47 | 50 |
| 6 | Age (years) | Range | 24–72 | 22–74 |
| 7 | Race | White | 36 | 32 |
| 8 | Race | Black or African American | 8 | 9 |
| 9 | Race | Asian | 3 | 5 |
| 10 | Race | Other/Unknown | 3 | 4 |
Step 1: Basic stub table with multiline column labels
Mark :category as the stub, group rows by :variable, and use Multiline for two-line column headers.
julia
tbl = StyledTable(demo)
tab_stub!(tbl, :category)
tab_rowgroup!(tbl, :variable)
cols_hide!(tbl, :variable)
cols_label!(tbl,
:placebo_n => Multiline("Placebo (N=50)", "n (%)"),
:treatment_n => Multiline("Treatment (N=50)", "n (%)"),
)
render(tbl)| Placebo (N=50) n (%) |
Treatment (N=50) n (%) |
|
| Sex | ||
| Male | 22 | 19 |
| Female | 28 | 30 |
| Unknown | missing | 1 |
| Age (years) | ||
| Mean (SD) | 48.3 (12.1) | 49.7 (11.8) |
| Median | 47 | 50 |
| Range | 24–72 | 22–74 |
| Race | ||
| White | 36 | 32 |
| Black or African American | 8 | 9 |
| Asian | 3 | 5 |
| Other/Unknown | 3 | 4 |
Step 2: Add header, stub label, missing handling, and notes
julia
sub_missing!(tbl, "—")
tab_footnote!(tbl, "Percentages computed on non-missing observations" => [:placebo_n, :treatment_n])
tab_sourcenote!(tbl, "Abbreviations: SD = standard deviation; N = total per arm")
render(tbl)| Placebo (N=50) n (%)1 |
Treatment (N=50) n (%)1 |
|
| Sex | ||
| Male | 22 | 19 |
| Female | 28 | 30 |
| Unknown | — | 1 |
| Age (years) | ||
| Mean (SD) | 48.3 (12.1) | 49.7 (11.8) |
| Median | 47 | 50 |
| Range | 24–72 | 22–74 |
| Race | ||
| White | 36 | 32 |
| Black or African American | 8 | 9 |
| Asian | 3 | 5 |
| Other/Unknown | 3 | 4 |
| Abbreviations: SD = standard deviation; N = total per arm | ||
| 1 Percentages computed on non-missing observations | ||
Row groups label the variable category, the stub column names each subgroup, and multiline headers identify each treatment arm and statistic type. The missing Placebo Unknown count appears as an em dash.