ltsummary builds the summary tables that clinical and epidemiological
papers open with: descriptive statistics by treatment arm, with
p-values, overall columns and footnotes. It follows the interface of
gtsummary, so
tbl_summary(trial, by = trt) |> add_p() |> bold_labels() works the way
you expect, and it renders the result with
lt, Yihui Xie’s lightweight grammar of
tables.
The point is the footprint. Installing ltsummary pulls in two packages
beyond base R: lt and xfun. Installing gtsummary pulls in 62, because it
renders through gt, which needs sass, V8 and htmlwidgets. In a validated
computing environment every package in that tree is a risk assessment to
write and an upgrade to keep under change control, so the difference
outlasts install time. Every statistic in ltsummary is computed in base
R and tested against the matching stats:: call or hand arithmetic
executed in the test suite, and parity with gtsummary 2.6.1 is verified
cell for cell whenever gtsummary and broom are installed. The
validation
article
collects the evidence for a package risk assessment.
ltsummary is an independent re-implementation of the interface designed by Daniel D. Sjoberg and the gtsummary authors. Function names, arguments, defaults and output strings follow gtsummary deliberately, and so do the wording of the reference pages and the structure of the articles, so that gtsummary’s documentation applies here too. ltsummary is not affiliated with or endorsed by the gtsummary project.
ltsummary is not on CRAN yet. Install it from GitHub with pak or remotes; R 4.1 or later is required, because every example uses the native pipe.
# install.packages("pak")
pak::pak("tgerke/ltsummary")
# or
# install.packages("remotes")
remotes::install_github("tgerke/ltsummary")The package is marked experimental because functions are still being added. The functions that exist mirror gtsummary 2.6.1, and their signatures are not expected to move.
library(ltsummary)
tbl <- trial |>
tbl_summary(by = trt, include = c(age, marker, grade, response)) |>
add_overall() |>
add_p() |>
bold_labels() |>
modify_caption("**Table 1. Patient characteristics**")In a knitr, Quarto or litedown document the table is rendered directly; the image above is for GitHub, which does not run JavaScript. The object behind the table is a data frame of formatted cells plus a list of styling instructions, so any cell can be pulled into the text of a report:
inline_text(tbl, variable = age, column = "Drug A")
#> [1] "59 (51, 66)"
inline_text(tbl, variable = grade, level = "II", column = "Drug B")
#> [1] "43 (41%)"
inline_text(tbl, variable = age, column = p.value)
#> [1] "p=0.043"Regression models get the same treatment: tbl_regression() summarizes
a fitted lm, glm or coxph model, and tbl_uvregression() fits and
stacks one model per variable.
tbl2 <- glm(response ~ age + grade + trt, trial, family = binomial) |>
tbl_regression(exponentiate = TRUE) |>
add_global_p() |>
bold_labels()as_lt() returns the lt object itself, so the lt verbs keep working
after the ltsummary ones:
as_lt(tbl) |>
lt::lt_width("70%") |>
lt::lt_export("table1.pdf")The package covers descriptive, continuous, cross, survival, hierarchical and regression tables, and the functions that combine them:
tbl_summary()with theby,label,statistic,digits,type,value,missing,missing_text,missing_stat,sort,percentandincludearguments;add_overall(),add_n(),add_p(),add_difference(),add_difference_row(),add_ci(),add_stat()andadd_stat_label(), andseparate_p_footnotes();tbl_continuous()with itsadd_p(),add_overall()andinline_text()methods;tbl_cross()with itsadd_p()andinline_text()methods;tbl_hierarchical()andtbl_hierarchical_count()for adverse-event-style nested tables, withsort_hierarchical(),filter_hierarchical(),add_overall()andadd_difference();tbl_likert()(withadd_n()),tbl_wide_summary(), andtbl_custom_summary()with theratio_summary()andproportion_summary()helpers;tbl_survfit()forsurvfitobjects, lists of them, or a data frame, withadd_p(),add_n()andadd_nevent();tbl_regression()forlm,glmandcoxphmodels (and any model with a tidier) andtbl_uvregression(), withadd_global_p(),add_nevent(),add_glance_table(),add_glance_source_note(),combine_terms(),add_significance_stars()andadd_vif();tbl_stack(),tbl_merge(),tbl_strata(),tbl_strata2()andtbl_strata_nested_stack()to combine tables, andtbl_split_by_rows()andtbl_split_by_columns()to split them;add_q(),sort_p()andfilter_p();- the
modify_*()family for headers, spanning headers, footnotes, abbreviations, captions, source notes, column visibility, alignment, indentation, merging and formatting; bold_labels(),bold_levels(),italicize_labels(),italicize_levels()andbold_p(), withremove_row_type(),add_variable_group_header()andtbl_butcher();- the
style_*()formatters andinline_text(); - themes:
set_ltsummary_theme(),with_ltsummary_theme()andwithout_ltsummary_theme(), and thetheme_ltsummary_*()constructors (compact, journal conventions, mean and SD, exploratory statistics, and seventeen languages); as_lt()with print, knitr and data frame methods.
That is the whole gtsummary 2.6.1 surface except the survey and ARD families and a few functions that need packages outside base R; the migration article lists them.
Function names, arguments, defaults and output strings follow gtsummary
2.6.1. Functions that gtsummary has deprecated (modify_footnote(),
modify_column_indent(), tbl_split(), continuous_summary()) are not
mirrored. Beyond that:
as_lt()replacesas_gt(), and there are no converters for flextable, huxtable or kable.- Header and footnote text supports a markdown subset (
**bold**,_italic_, line breaks) instead of full markdown. Pass HTML withtext_interpret = "html"for anything else. - Column selection uses a base R implementation of the common tidyselect
forms (
c(age, grade),-trt,starts_with("a"),all_continuous()) rather than tidyselect itself. - The theme functions carry the ltsummary name:
set_gtsummary_theme()isset_ltsummary_theme(),theme_gtsummary_compact()istheme_ltsummary_compact(), and so on. The element names inside a theme are gtsummary’s, so a theme list written for gtsummary carries over. A few defaults can also be set with options; the tbl_summary() tutorial lists them, and a theme outranks them.
Decisions that are not obvious from the code (why base R, how the object
model mirrors gtsummary, the testing policy, which gtsummary version is
the reference) are recorded in
decisions/.

