Skip to content

SummaryTables

Publication-ready tables for Julia - in HTML, docx, LaTeX and Typst

SummaryTables

SummaryTables

SummaryTables is focused on creating tables for publications in HTML, docx, LaTeX and Typst formats. It offers both convenient predefined table functions that are inspired by common table formats in the pharma space, as well as an API to create completely custom tables.

It deliberately uses an opinionated, limited styling API so that styling can be as consistent as possible across the different backends.

Examples

julia
using SummaryTables
using DataFrames

data = DataFrame(
    sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"],
    age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76],
    blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"],
    smoker = [true, false, false, false, true, true, true, false, false, false],
)

simple_table(
    data,
    [:age => "Age (years)", :sex => "Sex", :smoker => "Smoker", :blood_type => "Blood Type"],
    halign = [:left, :right, :right, :right],
)
Age (years) Sex Smoker Blood Type
27 m true A
45 m false 0
34 m false B
85 m false B
55 f true B
44 f true A
24 f true 0
29 f false A
37 f false A
76 f false B
julia
using SummaryTables
using DataFrames

data = DataFrame(
    sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"],
    age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76],
    blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"],
    smoker = [true, false, false, false, true, true, true, false, false, false],
)

table_one(
    data,
    [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"],
    groupby = :sex => "Sex",
    show_n = true
)
Sex
Total
(n=10)
f
(n=6)
m
(n=4)
Age (years)
Mean (SD) 45.6 (20.7) 44.2 (19.1) 47.8 (25.9)
Median [Min, Max] 40.5 [24, 85] 40.5 [24, 76] 39.5 [27, 85]
Blood type
0 2 (20%) 1 (16.7%) 1 (25%)
A 4 (40%) 3 (50%) 1 (25%)
B 4 (40%) 2 (33.3%) 2 (50%)
Smoker
false 6 (60%) 3 (50%) 3 (75%)
true 4 (40%) 3 (50%) 1 (25%)
julia
using DataFrames
using SummaryTables
using Statistics

data = DataFrame(
    concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2],
    id = repeat([1, 2], inner = 5),
    time = repeat([0, 0.5, 1, 2, 3], 2)
)

listingtable(
    data,
    :concentration => "Concentration (ng/mL)",
    rows = :id => "ID",
    cols = :time => "Time (hr)",
    summarize_rows = [
        length => "N",
        mean => "Mean",
        std => "SD",
    ]
)
Time (hr)
0 0.5 1 2 3
ID Concentration (ng/mL)
1 1.2 4.5 2 1.5 0.1
2 1.8 3.2 1.8 1.2 0.2
N 2 2 2 2 2
Mean 1.5 3.85 1.9 1.35 0.15
SD 0.424 0.919 0.141 0.212 0.0707
julia
using DataFrames
using SummaryTables
using Statistics

data = DataFrame(
    concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2],
    id = repeat([1, 2], inner = 5),
    time = repeat([0, 0.5, 1, 2, 3], 2)
)

summarytable(
    data,
    :concentration => "Concentration (ng/mL)",
    cols = :time => "Time (hr)",
    summary = [
        length => "N",
        mean => "Mean",
        std => "SD",
    ]
)
Time (hr)
0 0.5 1 2 3
Concentration (ng/mL)
N 2 2 2 2 2
Mean 1.5 3.85 1.9 1.35 0.15
SD 0.424 0.919 0.141 0.212 0.0707
julia
using SummaryTables

categories = ["Deciduous", "Deciduous", "Evergreen", "Evergreen", "Evergreen"]
species = ["Beech", "Oak", "Fir", "Spruce", "Pine"]
fake_data = [
    "35m" "40m" "38m" "27m" "29m"
    "10k" "12k" "18k" "9k" "7k"
    "500yr" "800yr" "600yr" "700yr" "400yr"
    "80\$" "150\$" "40\$" "70\$" "50\$"
]
labels = ["", "", "Size", Annotated("Water consumption", "Liters per year"), "Age", "Value"]

body = [
    Cell.(categories, bold = true, merge = true, border_bottom = true)';
    Cell.(species)';
    Cell.(fake_data)
]

Table(hcat(
    Cell.(labels, italic = true, halign = :right),
    body
), header = 2)
Deciduous Evergreen
Beech Oak Fir Spruce Pine
Size 35m 40m 38m 27m 29m
Water consumption1 10k 12k 18k 9k 7k
Age 500yr 800yr 600yr 700yr 400yr
Value 80$ 150$ 40$ 70$ 50$
1 Liters per year