Migrating from gtsummary
Source:vignettes/articles/migrating-from-gtsummary.Rmd
migrating-from-gtsummary.Rmdltsummary mirrors gtsummary 2.6.1 on purpose: the same function
names, the same argument names and defaults, the same output strings.
Most gtsummary code runs after changing the library() call,
and as_gt() becomes as_lt(). This article
shows the same table built with both packages, lists the few functions
that stay out of scope, and explains the few places where behavior
differs.
Both packages ship a trial data set with the same
columns, but the simulated values differ, so the examples below use
gtsummary’s data for both tables. Both packages also export
tbl_summary(), add_p() and the rest, so the
calls are namespaced here; in your own scripts you would load one
package or the other.
trial <- gtsummary::trial
lt_tbl <- trial |>
ltsummary::tbl_summary(by = trt, include = c(age, grade, response)) |>
ltsummary::add_overall() |>
ltsummary::add_p() |>
ltsummary::bold_labels()
gt_tbl <- trial |>
gtsummary::tbl_summary(by = trt, include = c(age, grade, response)) |>
gtsummary::add_overall() |>
gtsummary::add_p() |>
gtsummary::bold_labels()The formatted cells are the same, down to the markdown markers that
as.data.frame() adds for bold text:
identical(as.data.frame(lt_tbl), as.data.frame(gt_tbl))
#> [1] TRUE
all.equal(lt_tbl$table_body$p.value, unname(gt_tbl$table_body$p.value))
#> [1] TRUEAnd the rendered tables look alike. ltsummary:
lt_tblgtsummary:
gt_tbl| Characteristic |
Overall N = 2001 |
Drug A N = 981 |
Drug B N = 1021 |
p-value2 |
|---|---|---|---|---|
| Age | 47 (38, 57) | 46 (37, 60) | 48 (39, 56) | 0.7 |
| Unknown | 11 | 7 | 4 | |
| Grade | 0.9 | |||
| I | 68 (34%) | 35 (36%) | 33 (32%) | |
| II | 68 (34%) | 32 (33%) | 36 (35%) | |
| III | 64 (32%) | 31 (32%) | 33 (32%) | |
| Tumor Response | 61 (32%) | 28 (29%) | 33 (34%) | 0.5 |
| Unknown | 7 | 3 | 4 | |
| 1 Median (Q1, Q3); n (%) | ||||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | ||||
The test suite checks this agreement over a matrix of
tbl_summary() settings, the add_*() functions,
the style_*() formatters and inline_text()
whenever gtsummary and broom are installed, so differences are treated
as bugs unless a change in gtsummary is deliberate.
What carries over
The functions below exist in both packages with the same arguments. Arguments that gtsummary has deprecated are dropped rather than mirrored.
With 0.8.0 the mirrored surface follows gtsummary 2.6.1: every function outside the deprecated rows above and the list below exists here with the same arguments.
Not planned: tbl_svysummary() and the survey functions,
the ARD-based tbl_ard_*() builders and their
brdg_*()/pier_*() infrastructure, the
comparison methods that need emmeans or lme4 (emmeans,
lme4, ancova_lme4), regression models that
need lme4, mice or other packages to tidy, the
tidy_robust()-style wrappers around broom.helpers,
as_flex_table(), as_hux_table(),
as_kable(), as_kable_extra() and
save_flex_docx().
Where behavior differs
Rendering. lt builds the HTML table in the browser
from a JSON specification with a 10 KB JavaScript file. The table
reflows and styles with CSS, and hovering a formatted cell shows the
unrounded value. There is no LaTeX or RTF output;
lt::lt_export() writes static HTML, PDF or PNG files
through a headless browser, and Quarto can turn lt’s static HTML into
Word and PDF tables. The Quarto and R Markdown
article covers each output format.
Spanning headers. The current lt release renders a
single row of spanning headers.
modify_spanning_header(level = 2) is accepted and stored,
with a message, but only level 1 is shown.
Combined tables. tbl_stack()’s group
headers render as full-width lt separator rows, the counterpart of gt’s
row groups, and bold_labels() on such a table puts the bold
on the label column (in gtsummary it targets the hidden group column and
nothing visible changes). tbl_merge() writes its spanning
headers at level 1, replacing any level-1 spanner the inputs carried, so
they render within lt’s single spanner row. tbl_strata()
accepts data frames, not survey designs.
Markdown. gtsummary passes header and footnote text
through gt’s markdown renderer. ltsummary converts a subset:
**bold**, _italic_ or *italic*,
and line breaks. Every default header in gtsummary falls within the
subset. For links, code or anything else, pass HTML and set
text_interpret = "html";
text_interpret = "none" shows the text verbatim.
Regression models. gtsummary tidies models with
broom.helpers; ltsummary tidies lm, glm and
coxph models in base R and reproduces broom.helpers’ rules
for variables, levels, reference rows and interactions. The intervals
are the ones broom reports: t-based for lm, profile
likelihood for glm (stats::confint(), from
MASS on R older than 4.4) and Wald for coxph.
add_global_p() computes
car::Anova(type = "III")’s tests in base R, and
add_glance_table() computes broom::glance()’s
statistics in base R for the three model classes. Other model classes
are tidied by broom::tidy() when broom is installed, or by
a tidy_fun you supply, and get one row per coefficient when
the class has no model matrix. tbl_regression(),
tbl_uvregression() and glance_fun_s3() are
plain functions rather than S3 generics, so that gtsummary’s generics of
the same names keep working when both packages are attached.
Column selection. gtsummary uses tidyselect.
ltsummary implements the common forms in base R: bare names, strings,
c() including mixed selections such as
c(age:grade, -marker), - for exclusion,
ranges, character vectors held in a variable, the all_*()
selectors and everything(), all_of(),
any_of(), starts_with(),
ends_with(), contains(),
matches(), where() and
last_col(). Boolean operators (!,
&, |) are not supported.
Themes and languages. The theme functions exist with the ltsummary name, the one place a function name differs between the packages:
| gtsummary | ltsummary |
|---|---|
set_gtsummary_theme(),
get_gtsummary_theme(),
reset_gtsummary_theme(),
check_gtsummary_theme(),
with_gtsummary_theme(),
without_gtsummary_theme()
|
set_ltsummary_theme() and so on; the deprecated
quiet argument is not mirrored |
theme_gtsummary_compact(),
theme_gtsummary_journal(),
theme_gtsummary_language(),
theme_gtsummary_continuous2(),
theme_gtsummary_mean_sd(),
theme_gtsummary_eda()
|
theme_ltsummary_compact() and so on |
theme_gtsummary_printer() |
not mirrored; use options(ltsummary.print_engine =) or
the pkgwide-str:print_engine element |
The element names inside a theme are gtsummary’s
("tbl_summary-arg:statistic",
"pkgwide-str:language", …), so a theme list written for
gtsummary passes to set_ltsummary_theme() element by
element; elements for functions ltsummary does not have (the survey
family, the print-engine addl_cmds) are rejected by name,
and "as_lt-lst:table_style" styles the rendered table in
their place. See decisions/0015
for the accepted list.
A few defaults can also be set with options
(ltsummary.big.mark, ltsummary.decimal.mark,
ltsummary.cat_threshold,
ltsummary.default_con_type,
ltsummary.print_engine); the tbl_summary() tutorial lists them. A
theme outranks the options, and an argument passed explicitly outranks
both.
Comparison methods. The built-in tests come from the
stats package, and three groups of gtsummary’s methods are
re-implemented in base R: ancova (an lm() fit,
so add_p(adj.vars =) and
add_difference(adj.vars =) work), smd, which
reproduces smd::smd() including its standard error, and the
effect sizes cohens_d, hedges_g,
paired_cohens_d and paired_hedges_g, which
reproduce the estimates and noncentral t intervals of effectsize. The
standardized mean difference uses conf.level, where
gtsummary always reports 95% limits. The methods that need emmeans or
lme4 are not available; see ?tests for the full list.
Survival tables. tbl_survfit() is one
function rather than a generic. The formula and data of each
survfit() call are evaluated where
tbl_survfit() is called, so a formula stored in a variable
works and a data frame local to the calling function is found, where
gtsummary evaluates them from its own namespace. add_n()
counts sum(x$n) from the survfit object rather
than the rows of model.frame(), which is the same number
unless the call used subset.
Footnote symbols. gtsummary 2.6.0 adds
modify_footnote_symbol(),
remove_footnote_symbol() and the
pkgwide-chr:footnote_symbol theme element. lt numbers
footnotes itself, so there is no equivalent, and the element is rejected
by name.
Custom tests. A custom test function may return a one-row data frame, as in gtsummary, or a list with scalar elements.
Objects
An ltsummary object is a list with the same shape as a gtsummary
object: table_body is a data frame of formatted cells with
variable, var_type, row_type and
label columns, and table_styling is a list of
data frames recording headers, footnotes, formatting and so on. Code
that inspects x$table_body works unchanged. The ARD that
gtsummary stores in x$cards is replaced by
x$stats, a list holding one simpler long-format data frame
per function that computed something, which is what
inline_text() and add_stat_label() read.
Objects from both packages share the tbl_summary class.
Each package dispatches its methods through its own generics, so loading
both at once, as this article does, is safe.