Skip to content

Repository files navigation

ltsummary

R-CMD-check pkgdown Lifecycle: experimental

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.

Installation

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.

Example

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")

What is implemented

The package covers descriptive, continuous, cross, survival, hierarchical and regression tables, and the functions that combine them:

  • tbl_summary() with the by, label, statistic, digits, type, value, missing, missing_text, missing_stat, sort, percent and include arguments;
  • add_overall(), add_n(), add_p(), add_difference(), add_difference_row(), add_ci(), add_stat() and add_stat_label(), and separate_p_footnotes();
  • tbl_continuous() with its add_p(), add_overall() and inline_text() methods;
  • tbl_cross() with its add_p() and inline_text() methods;
  • tbl_hierarchical() and tbl_hierarchical_count() for adverse-event-style nested tables, with sort_hierarchical(), filter_hierarchical(), add_overall() and add_difference();
  • tbl_likert() (with add_n()), tbl_wide_summary(), and tbl_custom_summary() with the ratio_summary() and proportion_summary() helpers;
  • tbl_survfit() for survfit objects, lists of them, or a data frame, with add_p(), add_n() and add_nevent();
  • tbl_regression() for lm, glm and coxph models (and any model with a tidier) and tbl_uvregression(), with add_global_p(), add_nevent(), add_glance_table(), add_glance_source_note(), combine_terms(), add_significance_stars() and add_vif();
  • tbl_stack(), tbl_merge(), tbl_strata(), tbl_strata2() and tbl_strata_nested_stack() to combine tables, and tbl_split_by_rows() and tbl_split_by_columns() to split them;
  • add_q(), sort_p() and filter_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() and bold_p(), with remove_row_type(), add_variable_group_header() and tbl_butcher();
  • the style_*() formatters and inline_text();
  • themes: set_ltsummary_theme(), with_ltsummary_theme() and without_ltsummary_theme(), and the theme_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.

Differences from gtsummary

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() replaces as_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 with text_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() is set_ltsummary_theme(), theme_gtsummary_compact() is theme_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.

Design notes

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/.

About

Presentation-ready summary tables with the gtsummary interface, computed in base R and rendered with lt

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages