Formatting results tables

Tutorial
Demonstration of how to appropriately report the results of regression models
Modified

April 7, 2025

Note

There are many great resources for creating summary results tables in R. If you search online you’ll find dozens of packages and tutorials showing you how to create beautiful tables. This tutorial shows just a small fraction of options.

library(tidyverse)
library(tidymodels)
library(skimr)
library(gt)
library(gtsummary)

chipotle <- read_csv(file = "data/chipotle.csv")

set.seed(123)

Summary statistics

Don’t do this

skim(chipotle)
Data summary
Name chipotle
Number of rows 30
Number of columns 7
_______________________
Column type frequency:
character 4
Date 1
numeric 2
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
order 0 1 6 6 0 2 0
meat 0 1 7 8 0 2 0
store 0 1 7 7 0 3 0
food 0 1 4 7 0 2 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
date 0 1 2024-01-12 2024-02-10 2024-01-26 30

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
day 0 1 15.5 8.80 1.00 8.25 15.50 22.75 30.00 ▇▇▇▇▇
weight 0 1 810.8 123.37 510.29 715.82 793.79 907.18 1048.93 ▁▆▇▇▂

skim() is great for exploratory analysis - not for presenting results in a published document.

Do this instead

{gtsummary} has functions for calculating descriptive statistics and formatting them nicely in a table.

chipotle |>
  # select columns to summarize
  select(order:weight) |>
  # use gtsummary::tbl_summary()
  tbl_summary()
Characteristic N = 301
order
    Online 15 (50%)
    Person 15 (50%)
meat
    Carnitas 10 (33%)
    Chicken 20 (67%)
store
    Store 1 10 (33%)
    Store 2 10 (33%)
    Store 3 10 (33%)
food
    bowl 15 (50%)
    burrito 15 (50%)
weight 794 (709, 907)
1 n (%); Median (Q1, Q3)
# by group
chipotle |>
  select(order:weight) |>
  tbl_summary(
    by = order,
    # include human readable labels for variables
    label = list(
      meat = "Meat",
      store = "Store",
      food = "Meal type",
      weight = "Weight (grams)"
    )
  )
Characteristic Online
N = 151
Person
N = 151
Meat

    Carnitas 6 (40%) 4 (27%)
    Chicken 9 (60%) 11 (73%)
Store

    Store 1 5 (33%) 5 (33%)
    Store 2 5 (33%) 5 (33%)
    Store 3 5 (33%) 5 (33%)
Meal type

    bowl 8 (53%) 7 (47%)
    burrito 7 (47%) 8 (53%)
Weight (grams) 709 (652, 822) 907 (794, 936)
1 n (%); Median (Q1, Q3)
Read the vignette

The tbl_summary() tutorial includes many examples of how to generate and customize summary tables.

Linear regression model

Don’t do this

Say you estimate a linear regression model to predict the weight of each order based on the order type, meat type, food type, and store. You might report the results like this:

# observed results
obs_fit <- chipotle |>
  specify(weight ~ order + meat + food + store) |>
  fit()

# null distribution for p-values
null_full_dist <- chipotle |>
  specify(weight ~ order + meat + food + store) |>
  hypothesize(null = "independence") |>
  generate(reps = 1000, type = "permute") |>
  fit()

p_vals <- get_p_value(null_full_dist, obs_stat = obs_fit, direction = "two-sided")

visualize(null_full_dist) +
  shade_p_value(obs_stat = obs_fit, direction = "two-sided")

# bootstrap distribution for CIs
boot_full_dist <- chipotle |>
  specify(weight ~ order + meat + food + store) |>
  generate(reps = 1000, type = "bootstrap") |>
  fit()

# get 95% confidence interval
conf_ints <- get_ci(boot_full_dist, level = 0.95, point_estimate = obs_fit)

visualize(boot_full_dist) +
  shade_ci(endpoints = conf_ints)

Please don’t. {infer} has several methods for visualizing \(p\)-values and confidence intervals, but they are best used for exploratory analysis, not for reporting results.

Do this instead

Once again, {gtsummary} can be used to create regression results tables.

# fit a standard linear regression model and rely on theoretical assumptions
# for confidence intervals and p-values
linear_reg() |>
  fit(weight ~ order + meat + food + store, data = chipotle) |>
  # basic results table
  tbl_regression()
Characteristic Beta 95% CI p-value
order


    Online
    Person 161 98, 223 <0.001
meat


    Carnitas
    Chicken -29 -96, 38 0.4
food


    bowl
    burrito -82 -145, -20 0.012
store


    Store 1
    Store 2 -62 -138, 14 0.10
    Store 3 -93 -169, -18 0.018
Abbreviation: CI = Confidence Interval
```{r}
#| tbl-cap: An example regression results table
linear_reg() |>
  fit(weight ~ order + meat + food + store, data = chipotle) |>
  # basic results table
  tbl_regression(
    # format variable labels
    label = list(
      order = "Order method",
      meat = "Meat",
      store = "Store",
      food = "Meal type"
    ),
    # round p-values to 2 significant digits
    pvalue_fun = label_style_pvalue(digits = 2)
    ) |>
  # add standard error and test statistic
  modify_header(
    statistic = "**Statistic**",
    std.error = "**SE**"
  ) |>
  # anything below the alpha threshold of 0.05 - format in bold
  bold_p(t = 0.05) |>
  # make labels bold
  bold_labels() |>
  # italicize levels for categorical variables
  italicize_levels()
```
An example regression results table
Characteristic Beta SE Statistic 95% CI p-value
Order method




    Online
    Person 161 30.2 5.31 98, 223 <0.001
Meat




    Carnitas
    Chicken -29 32.5 -0.907 -96, 38 0.37
Meal type




    bowl
    burrito -82 30.2 -2.73 -145, -20 0.012
Store




    Store 1
    Store 2 -62 36.7 -1.69 -138, 14 0.10
    Store 3 -93 36.7 -2.54 -169, -18 0.018
Abbreviations: CI = Confidence Interval, SE = Standard Error
Captioning tables in Quarto

Use the tbl-cap code chunk option to add captions for tables produced programmatically. See the Quarto documentation for more examples of formatting tables using Quarto.

Alternatively, you can create a regression results plot that reports your estimated coefficients and CIs using a point range plot.

# visualize regression results using a coefficient plot
obs_fit |>
  # join with confidence intervals
  left_join(conf_ints) |>
  # order the coefficients by size, pull intercept to the beginning (by convention)
  mutate(term = fct_reorder(.f = term, .x = estimate) |>
    fct_relevel("intercept")) |>
  # draw a pointrange plot
  ggplot(mapping = aes(x = estimate, y = term, xmin = lower_ci, xmax = upper_ci)) +
  geom_pointrange() +
  # draw a vertical line at 0
  geom_vline(xintercept = 0, linetype = "dashed") +
  theme_minimal()

Notice this still requires substantial cleaning to make it publication-ready (e.g. title, axis labels, human-readable labels for each coefficient, etc.).

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.4.2 (2024-10-31)
 os       macOS Sonoma 14.6.1
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/New_York
 date     2025-04-07
 pandoc   3.4 @ /usr/local/bin/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package       * version    date (UTC) lib source
 archive         1.1.9      2024-09-12 [1] CRAN (R 4.4.1)
 backports       1.5.0      2024-05-23 [1] CRAN (R 4.4.0)
 base64enc       0.1-3      2015-07-28 [1] CRAN (R 4.3.0)
 bit             4.0.5      2022-11-15 [1] CRAN (R 4.3.0)
 bit64           4.0.5      2020-08-30 [1] CRAN (R 4.3.0)
 broom         * 1.0.6      2024-05-17 [1] CRAN (R 4.4.0)
 broom.helpers   1.20.0     2025-03-06 [1] CRAN (R 4.4.1)
 cards           0.5.1      2025-03-01 [1] CRAN (R 4.4.1)
 class           7.3-22     2023-05-03 [1] CRAN (R 4.4.2)
 cli             3.6.4      2025-02-13 [1] CRAN (R 4.4.1)
 codetools       0.2-20     2024-03-31 [1] CRAN (R 4.4.2)
 commonmark      1.9.1      2024-01-30 [1] CRAN (R 4.3.1)
 crayon          1.5.3      2024-06-20 [1] CRAN (R 4.4.0)
 data.table      1.15.4     2024-03-30 [1] CRAN (R 4.3.1)
 dials         * 1.3.0      2024-07-30 [1] CRAN (R 4.4.0)
 DiceDesign      1.10       2023-12-07 [1] CRAN (R 4.3.1)
 dichromat       2.0-0.1    2022-05-02 [1] CRAN (R 4.3.0)
 digest          0.6.37     2024-08-19 [1] CRAN (R 4.4.1)
 dplyr         * 1.1.4      2023-11-17 [1] CRAN (R 4.3.1)
 evaluate        1.0.3      2025-01-10 [1] CRAN (R 4.4.1)
 farver          2.1.2      2024-05-13 [1] CRAN (R 4.3.3)
 fastmap         1.2.0      2024-05-15 [1] CRAN (R 4.4.0)
 forcats       * 1.0.0      2023-01-29 [1] CRAN (R 4.3.0)
 foreach         1.5.2      2022-02-02 [1] CRAN (R 4.3.0)
 furrr           0.3.1      2022-08-15 [1] CRAN (R 4.3.0)
 future          1.33.2     2024-03-26 [1] CRAN (R 4.3.1)
 future.apply    1.11.2     2024-03-28 [1] CRAN (R 4.3.1)
 generics        0.1.3      2022-07-05 [1] CRAN (R 4.3.0)
 ggplot2       * 3.5.1      2024-04-23 [1] CRAN (R 4.3.1)
 globals         0.16.3     2024-03-08 [1] CRAN (R 4.3.1)
 glue            1.8.0      2024-09-30 [1] CRAN (R 4.4.1)
 gower           1.0.1      2022-12-22 [1] CRAN (R 4.3.0)
 GPfit           1.0-8      2019-02-08 [1] CRAN (R 4.3.0)
 gt            * 0.11.1     2024-10-04 [1] CRAN (R 4.4.1)
 gtable          0.3.6      2024-10-25 [1] CRAN (R 4.4.1)
 gtsummary     * 2.1.0      2025-02-19 [1] CRAN (R 4.4.1)
 hardhat         1.4.0      2024-06-02 [1] CRAN (R 4.4.0)
 haven           2.5.4      2023-11-30 [1] CRAN (R 4.3.1)
 here            1.0.1      2020-12-13 [1] CRAN (R 4.3.0)
 hms             1.1.3      2023-03-21 [1] CRAN (R 4.3.0)
 htmltools       0.5.8.1    2024-04-04 [1] CRAN (R 4.3.1)
 htmlwidgets     1.6.4      2023-12-06 [1] CRAN (R 4.3.1)
 infer         * 1.0.7      2024-03-25 [1] CRAN (R 4.3.1)
 ipred           0.9-14     2023-03-09 [1] CRAN (R 4.3.0)
 iterators       1.0.14     2022-02-05 [1] CRAN (R 4.3.0)
 jsonlite        1.8.9      2024-09-20 [1] CRAN (R 4.4.1)
 knitr           1.49       2024-11-08 [1] CRAN (R 4.4.1)
 labeling        0.4.3      2023-08-29 [1] CRAN (R 4.3.0)
 labelled        2.13.0     2024-04-23 [1] CRAN (R 4.4.0)
 lattice         0.22-6     2024-03-20 [1] CRAN (R 4.4.2)
 lava            1.8.0      2024-03-05 [1] CRAN (R 4.3.1)
 lhs             1.1.6      2022-12-17 [1] CRAN (R 4.3.0)
 lifecycle       1.0.4      2023-11-07 [1] CRAN (R 4.3.1)
 listenv         0.9.1      2024-01-29 [1] CRAN (R 4.3.1)
 lubridate     * 1.9.3      2023-09-27 [1] CRAN (R 4.3.1)
 magrittr        2.0.3      2022-03-30 [1] CRAN (R 4.3.0)
 markdown        1.13       2024-06-04 [1] CRAN (R 4.4.0)
 MASS            7.3-61     2024-06-13 [1] CRAN (R 4.4.2)
 Matrix          1.7-1      2024-10-18 [1] CRAN (R 4.4.2)
 modeldata     * 1.4.0      2024-06-19 [1] CRAN (R 4.4.0)
 nnet            7.3-19     2023-05-03 [1] CRAN (R 4.4.2)
 parallelly      1.37.1     2024-02-29 [1] CRAN (R 4.3.1)
 parsnip       * 1.2.1      2024-03-22 [1] CRAN (R 4.3.1)
 pillar          1.10.2     2025-04-05 [1] CRAN (R 4.4.1)
 pkgconfig       2.0.3      2019-09-22 [1] CRAN (R 4.3.0)
 prodlim         2023.08.28 2023-08-28 [1] CRAN (R 4.3.0)
 purrr         * 1.0.2      2023-08-10 [1] CRAN (R 4.3.0)
 R6              2.5.1      2021-08-19 [1] CRAN (R 4.3.0)
 RColorBrewer    1.1-3      2022-04-03 [1] CRAN (R 4.3.0)
 Rcpp            1.0.14     2025-01-12 [1] CRAN (R 4.4.1)
 readr         * 2.1.5      2024-01-10 [1] CRAN (R 4.3.1)
 recipes       * 1.0.10     2024-02-18 [1] CRAN (R 4.3.1)
 repr            1.1.7      2024-03-22 [1] CRAN (R 4.4.0)
 rlang           1.1.5      2025-01-17 [1] CRAN (R 4.4.1)
 rmarkdown       2.29       2024-11-04 [1] CRAN (R 4.4.1)
 rpart           4.1.23     2023-12-05 [1] CRAN (R 4.4.2)
 rprojroot       2.0.4      2023-11-05 [1] CRAN (R 4.3.1)
 rsample       * 1.2.1      2024-03-25 [1] CRAN (R 4.3.1)
 rstudioapi      0.17.0     2024-10-16 [1] CRAN (R 4.4.1)
 sass            0.4.9      2024-03-15 [1] CRAN (R 4.3.1)
 scales        * 1.3.0.9000 2025-03-19 [1] Github (bensoltoff/scales@71d8f13)
 sessioninfo     1.2.2      2021-12-06 [1] CRAN (R 4.3.0)
 skimr         * 2.1.5      2022-12-23 [1] CRAN (R 4.3.0)
 stringi         1.8.4      2024-05-06 [1] CRAN (R 4.3.1)
 stringr       * 1.5.1      2023-11-14 [1] CRAN (R 4.3.1)
 survival        3.7-0      2024-06-05 [1] CRAN (R 4.4.2)
 tibble        * 3.2.1      2023-03-20 [1] CRAN (R 4.3.0)
 tidymodels    * 1.2.0      2024-03-25 [1] CRAN (R 4.3.1)
 tidyr         * 1.3.1      2024-01-24 [1] CRAN (R 4.3.1)
 tidyselect      1.2.1      2024-03-11 [1] CRAN (R 4.3.1)
 tidyverse     * 2.0.0      2023-02-22 [1] CRAN (R 4.3.0)
 timechange      0.3.0      2024-01-18 [1] CRAN (R 4.3.1)
 timeDate        4032.109   2023-12-14 [1] CRAN (R 4.3.1)
 tune          * 1.2.1      2024-04-18 [1] CRAN (R 4.3.1)
 tzdb            0.4.0      2023-05-12 [1] CRAN (R 4.3.0)
 vctrs           0.6.5      2023-12-01 [1] CRAN (R 4.3.1)
 vroom           1.6.5      2023-12-05 [1] CRAN (R 4.3.1)
 withr           3.0.2      2024-10-28 [1] CRAN (R 4.4.1)
 workflows     * 1.1.4      2024-02-19 [1] CRAN (R 4.4.0)
 workflowsets  * 1.1.0      2024-03-21 [1] CRAN (R 4.3.1)
 xfun            0.50.5     2025-01-15 [1] https://yihui.r-universe.dev (R 4.4.2)
 xml2            1.3.6      2023-12-04 [1] CRAN (R 4.3.1)
 yaml            2.3.10     2024-07-26 [1] CRAN (R 4.4.0)
 yardstick     * 1.3.1      2024-03-21 [1] CRAN (R 4.3.1)

 [1] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library

──────────────────────────────────────────────────────────────────────────────