To customize the appearance of tables, modelsummary supports four of the most popular table-making packages:

  1. gt: https://gt.rstudio.com
  2. kableExtra: http://haozhu233.github.io/kableExtra
  3. huxtable: https://hughjonesd.github.io/huxtable/
  4. flextable: https://davidgohel.github.io/flextable/

Users are encouraged to visit these websites to determine which package suits their needs best. Each of them has different strengths and weaknesses. For instance, gt allows seamless integration with the RStudio IDE, but kableExtra’s LaTeX (and PDF) output is far more mature.

Post-processing

To create customized tables, the analyst begins by calling modelsummary(models) to create a summary table. Then, she post-processes the table by applying functions from one of the packages listed above. It is often convenient to use the %>% operator to do this.

To illustrate, we download data from the Rdatasets repository and we estimate 5 models:

library(modelsummary)

url <- 'https://vincentarelbundock.github.io/Rdatasets/csv/HistData/Guerry.csv'
dat <- read.csv(url)

models <- list()
models[['OLS 1']] <- lm(Donations ~ Literacy, data = dat)
models[['Poisson 1']] <- glm(Donations ~ Literacy + Clergy, family = poisson, data = dat)
models[['OLS 2']] <- lm(Crime_pers ~ Literacy, data = dat)
models[['Poisson 2']] <- glm(Crime_pers ~ Literacy + Clergy, family = poisson, data = dat)
models[['OLS 3']] <- lm(Crime_prop ~ Literacy + Clergy, data = dat)

In the rest of this vignette, we will customize tables using tools supplied by the gt, kableExtra, flextable, and huxtable packages. In each case, the pattern will be similar. First, we create a table by calling modelsummary and by specifying the output format with the output parameter. Then, we will use functions from the four packages to customize the appearance of our tables.

gt

To illustrate how to customize tables using the gt package we will use the following functions from the gt package:

  • tab_spanner creates labels to group columns.
  • tab_footnote adds a footnote and a matching marking in a specific cell.
  • tab_style can modify the text and color of rows, columns, or cells.

To produce a “cleaner” look, we will also use modelsummary’s stars, coef_map, gof_omit, and title arguments.

Note that in order to access gt functions, we must first load the library.

library(gt)

# build table with `modelsummary` 
cm <- c( '(Intercept)' = 'Constant', 'Literacy' = 'Literacy (%)', 'Clergy' = 'Priests/capita')
cap <- 'A modelsummary table customized with gt'

tab <- modelsummary(models,
                output = "gt",
                coef_map = cm, stars = TRUE,
                title = cap, gof_omit = 'IC|Log|Adj')

# customize table with `gt`

tab %>%

    # column labels
    tab_spanner(label = 'Donations', columns = 2:3) %>%
    tab_spanner(label = 'Crimes (persons)', columns = 4:5) %>%
    tab_spanner(label = 'Crimes (property)', columns = 6) %>%

    # footnote
    tab_footnote(footnote = md("A very **important** variable."),
                 locations = cells_body(rows = 3, columns = 1)) %>%

    # text and background color
    tab_style(style = cell_text(color = 'red'),
              locations = cells_body(rows = 3)) %>%
    tab_style(style = cell_fill(color = 'lightblue'),
              locations = cells_body(rows = 5))
A modelsummary table customized with gt
Donations Crimes (persons) Crimes (property)
OLS 1 Poisson 1 OLS 2 Poisson 2 OLS 3
Constant 8759.068*** 8.986*** 20357.309*** 9.708*** 11243.544***
(1559.363) (0.004) (2020.980) (0.003) (1011.240)
Literacy (%)1 -42.886 -0.006*** -15.358 0.000*** -68.507***
(36.362) (0.000) (47.127) (0.000) (18.029)
Priests/capita 0.002*** 0.004*** -16.376
(0.000) (0.000) (12.522)
Num.Obs. 86 86 86 86 86
R2 0.016 0.001 0.152
F 1.391 0.106 7.441
* p < 0.1, ** p < 0.05, *** p < 0.01

1 A very important variable.

The gt website offers many more examples. The possibilities are endless. For instance, gt allows you to embed images in your tables using the text_transform and local_image functions:

f <- function(x) web_image(url = "https://user-images.githubusercontent.com/987057/82732352-b9aabf00-9cda-11ea-92a6-26750cf097d0.png", height = 80)

tab %>%
    text_transform(locations = cells_body(columns = 2:6, rows = 1), fn = f)
A modelsummary table customized with gt
OLS 1 Poisson 1 OLS 2 Poisson 2 OLS 3
Constant
(1559.363) (0.004) (2020.980) (0.003) (1011.240)
Literacy (%) -42.886 -0.006*** -15.358 0.000*** -68.507***
(36.362) (0.000) (47.127) (0.000) (18.029)
Priests/capita 0.002*** 0.004*** -16.376
(0.000) (0.000) (12.522)
Num.Obs. 86 86 86 86 86
R2 0.016 0.001 0.152
F 1.391 0.106 7.441
* p < 0.1, ** p < 0.05, *** p < 0.01

kableExtra

We will now illustrate how to customize tables using functions from the kableExtra package:

  • add_header_above creates labels to group columns.
  • add_footnote adds a footnote and a matching marking in a specific cell.
  • row_spec can modify the text and color of rows, columns, or cells.

We use the same code as above, but specify output='kableExtra' in the modelsummary() call:

library(kableExtra)

# build table with `modelsummary` 
cm <- c( '(Intercept)' = 'Constant', 'Literacy' = 'Literacy (%)', 'Clergy' = 'Priests/capita')
cap <- 'A modelsummary table customized with kableExtra'

tab <- modelsummary(models, output = 'kableExtra',
                coef_map = cm, stars = TRUE,
                title = cap, gof_omit = 'IC|Log|Adj')

# customize table with `kableExtra`
tab %>%

    # column labels
    add_header_above(c(" " = 1, "Donations" = 2, "Crimes (person)" = 2, "Crimes (property)" = 1)) %>%

    # text and background color
    row_spec(3, color = 'red') %>%
    row_spec(5, background = 'lightblue')
A modelsummary table customized with kableExtra
Donations
Crimes (person)
Crimes (property)
OLS 1 Poisson 1 OLS 2 Poisson 2 OLS 3
Constant 8759.068*** 8.986*** 20357.309*** 9.708*** 11243.544***
(1559.363) (0.004) (2020.980) (0.003) (1011.240)
Literacy (%) -42.886 -0.006*** -15.358 0.000*** -68.507***
(36.362) (0.000) (47.127) (0.000) (18.029)
Priests/capita 0.002*** 0.004*** -16.376
(0.000) (0.000) (12.522)
Num.Obs. 86 86 86 86 86
R2 0.016 0.001 0.152
F 1.391 0.106 7.441
* p < 0.1, ** p < 0.05, *** p < 0.01

These kableExtra functions can be used to produce LaTeX / PDF tables such as this one:

flextable

We will now illustrate how to customize tables using functions from the flextable package:

  • color to modify the color of the text
  • bg to modify the color of the background
  • autofit sets column width to sensible values.

We use the same code as above, but specify output='flextable' in the modelsummary() call:

library(flextable)
#> 
#> Attaching package: 'flextable'
#> The following objects are masked from 'package:kableExtra':
#> 
#>     as_image, footnote

# build table with `modelsummary` 
cm <- c( '(Intercept)' = 'Constant', 'Literacy' = 'Literacy (%)', 'Clergy' = 'Priests/capita')
cap <- 'A modelsummary table customized with flextable'

tab <- modelsummary(models, output = 'flextable',
                coef_map = cm, stars = TRUE,
                title = cap, gof_omit = 'IC|Log|Adj')

# customize table with `flextable`
tab %>%

    # text and background color
    color(3, color = 'red') %>%
    bg(5, bg = 'lightblue') %>%

    # column widths
    autofit()
A modelsummary table customized with flextable

OLS 1

Poisson 1

OLS 2

Poisson 2

OLS 3

Constant

8759.068***

8.986***

20357.309***

9.708***

11243.544***

(1559.363)

(0.004)

(2020.980)

(0.003)

(1011.240)

Literacy (%)

-42.886

-0.006***

-15.358

0.000***

-68.507***

(36.362)

(0.000)

(47.127)

(0.000)

(18.029)

Priests/capita

0.002***

0.004***

-16.376

(0.000)

(0.000)

(12.522)

Num.Obs.

86

86

86

86

86

R2

0.016

0.001

0.152

F

1.391

0.106

7.441

* p < 0.1, ** p < 0.05, *** p < 0.01

huxtable

We will now illustrate how to customize tables using functions from the huxtable package:

  • set_text_color to change the color of some entries

We use the same code as above, but specify output='huxtable' in the modelsummary() call:

library(huxtable)
#> 
#> Attaching package: 'huxtable'
#> The following objects are masked from 'package:flextable':
#> 
#>     align, as_flextable, bold, font, height, italic, set_caption,
#>     valign, width
#> The following object is masked from 'package:kableExtra':
#> 
#>     add_footnote
#> The following object is masked from 'package:gt':
#> 
#>     fmt_percent

# build table with `modelsummary` 
cm <- c( '(Intercept)' = 'Constant', 'Literacy' = 'Literacy (%)', 'Clergy' = 'Priests/capita')
cap <- 'A modelsummary table customized with huxtable'

tab <- modelsummary(models, output = 'huxtable',
                coef_map = cm, stars = TRUE,
                title = cap, gof_omit = 'IC|Log|Adj')

# customize table with `huxtable`
tab %>%

    # text color
    set_text_color(row = 4, col = 1:ncol(.), value = 'red')
A modelsummary table customized with huxtable
OLS 1 Poisson 1 OLS 2 Poisson 2 OLS 3
Constant 8759.068*** 8.986*** 20357.309*** 9.708*** 11243.544***
(1559.363) (0.004) (2020.980) (0.003) (1011.240)
Literacy (%) -42.886 -0.006*** -15.358 0.000*** -68.507***
(36.362) (0.000) (47.127) (0.000) (18.029)
Priests/capita 0.002*** 0.004*** -16.376
(0.000) (0.000) (12.522)
Num.Obs. 86 86 86 86 86
R2 0.016 0.001 0.152
F 1.391 0.106 7.441
* p < 0.1, ** p < 0.05, *** p < 0.01

Warning: Saving to file

When users supply a file name to the output argument, the table is written immediately to file. This means that users cannot post-process and customize the resulting table using functions from gt, kableExtra, huxtable, or flextable. When users specify a filename in the output argument, the modelsummary() call should be the final one in the chain.

This is OK:

modelsummary(models, output = 'table.html')

This is not OK:

modelsummary(models, output = 'table.html') %>%
    tab_spanner(label = 'Literacy', columns = c('OLS 1', 'Poisson 1'))

To save a customized table, you should apply all the customization functions you need before saving it using gt::gtsave, kableExtra::save_kable, or the appropriate helper function from the package that you are using to customize your table.