library(gt)
library(tidyverse)
Overview
This post is inspired by themockup’s blog and RStudio’s documentation of gt
package1.
1 You can find the documentation here.
“We can construct a wide variety of useful tables with a cohesive set of table parts. These include the table header, the stub, the column labels and spanner column labels, the table body, and the table footer.”
--- The gt philosophy
1 Component
The typical gt table starts with converting a data frame into a gt object. The gt
object is then modified by adding various components to it. The components include the table header, the stub, the column labels and spanner column labels, the table body, and the table footer.
2 Example: sp500 data
As always, we install and load gt
and tidyverse
packages.
gt()
function can convert data.frame intogt
object. Agt
object can be directly output and rendered into HTML.
# Define the start and end dates for the data range
<- "2010-06-07"
start_date <- "2010-06-14"
end_date
# Create a gt table based on preprocessed
# `sp500` table data
<- sp500 |>
sp500_gt ::filter(date >= start_date & date <= end_date) |>
dplyr::select(-adj_close) |>
dplyrgt()
sp500_gt
date | open | high | low | close | volume |
---|---|---|---|---|---|
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
2.1 tab_header
: title and subtitle
- The
gt
object can be further modified by adding components to it, for exampletab_header
adds Table Headers (including title and subtitle).- For narratives like title or subtitle, you can also use markdown format with the
md
function.
- For narratives like title or subtitle, you can also use markdown format with the
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
Note that if your Quarto HTML has CSS style, the markdown format in gt
object may not work as expected. Like the following title will be bold but with underline (my CSS style).
|>
sp500_gt tab_header(
title = md("**S&P 500**"),
subtitle = md("*June 7-14*, 2010")
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
2.2 fmt_<column_type>
: format columns
fmt_<column_type>
functions can be used to format the columns of the table.- For example,
fmt_currency
can be used to format theclose
column as currency. fmt_date
can be used to format thedate
column as date.fmt_number
can be used to format thevolume
column as a number.
- For example,
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) fmt_currency(
columns = vars(c(open, high, low, close)),
currency = "USD"
|>
) fmt_date(
columns = vars(date),
date_style = "wday_month_day_year"
|>
) fmt_number(
columns = vars(volume),
suffixing = TRUE
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
Monday, June 14, 2010 | $1,095.00 | $1,105.91 | $1,089.03 | $1,089.63 | 4.43B |
Friday, June 11, 2010 | $1,082.65 | $1,092.25 | $1,077.12 | $1,091.60 | 4.06B |
Thursday, June 10, 2010 | $1,058.77 | $1,087.85 | $1,058.77 | $1,086.84 | 5.14B |
Wednesday, June 9, 2010 | $1,062.75 | $1,077.74 | $1,052.25 | $1,055.69 | 5.98B |
Tuesday, June 8, 2010 | $1,050.81 | $1,063.15 | $1,042.17 | $1,062.00 | 6.19B |
Monday, June 7, 2010 | $1,065.84 | $1,071.36 | $1,049.86 | $1,050.47 | 5.47B |
2.3 tab_source_note
: source note
tab_source_note
can be used to add a source note to the table underneath the table.
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) tab_source_note(
source_note = "Data from Yahoo Finance"
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
Data from Yahoo Finance |
2.4 tab_footnote
: footnotes
Beside the markdown format, Quarto HTML also accepts HTML code using htmltools::p
function. But it may only apply to HTML not PDF. As Figure 1 shows, footnotes are located at the bottom of the table but at the top of source notes.
cells_*()
: target cells in the table
We were able to supply the reference locations in the table by using the cells_body() helper function and supplying the necessary targeting through the columns and rows arguments. Other cells_*() functions have similar interfaces and they allow us to target cells in different parts of the table.
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) tab_source_note(
source_note = htmltools::p(align="right", "Data from Yahoo Finance")
|>
) tab_footnote(
footnote = "All values are in USD.",
locations = cells_body(
columns = vars(open),
rows = date == "2010-06-14"
) )
S&P 500 | |||||
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
---|---|---|---|---|---|
2010-06-14 | 1 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
Data from Yahoo Finance |
|||||
1 All values are in USD. |
2.5 tab_row_group
: row groups
- We can make a new row group with each tab_row_group() call.
- The inputs are row group names in the label argument, and row references in the rows argument.
- key arguments:
label
: the name of the row group.rows
: the logical vector that specifies which rows belong to the group.
Note that the sequence of adding tab_row_group
will affect the order of the row groups in the final table. Lastest added group will be on the top.
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) tab_row_group(
label = "Last three days",
rows = date %in% c("2010-06-10", "2010-06-11", "2010-06-14")
|>
) tab_row_group(
label = "First three days",
rows = date %in% c("2010-06-07", "2010-06-08", "2010-06-09")
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date | open | high | low | close | volume |
First three days | |||||
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
Last three days | |||||
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2.6 tab_spanner
: column groups and spann column labels
- key arguments:
label
: the name of the column group.columns
: the columns that belong to the group.
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) tab_spanner(
label = "Price",
columns = vars(open, high, low, close)
|>
) tab_spanner(
label = "Volume",
columns = vars(volume)
)
S&P 500 | |||||
---|---|---|---|---|---|
June 7-14, 2010 | |||||
date |
Price
|
Volume
|
|||
open | high | low | close | volume | |
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 |
2.7 summary_rows
: summary rows
summary_rows
can be used to add summary rows to the table bygroup
.- if you want to have grant summary statistics, use
grand_summary_rows
instead.
- if you want to have grant summary statistics, use
- key arguments:
fns
: a list of functions to apply to the columns.columns
: the columns to which the functions are applied.
Note that the fmt
argument is used to format the summary values with a list. There are multiple formatting functions for numeric variables available in the gt package:
fmt_number(cols = vars(...), decimals = 2)
: format numbers with a specified number of 2 decimal places.fmt_scientific(cols = vars(...), decimals = 3)
: format numbers in scientific notation with a specified number of 3 decimal places.
|>
sp500_gt tab_header(
title = "S&P 500",
subtitle = "June 7-14, 2010"
|>
) grand_summary_rows(
fns = list(
"Mean" = ~ mean(., na.rm =TRUE),
"SD" = ~ sd(., na.rm =TRUE)
),columns = vars(open, high, low, close, volume),
fmt = list(
~ fmt_scientific(data = .,
columns = vars(volume),
decimals = 3),
~ fmt_number(data = .,
columns = vars(open, high, low, close),
decimals = 2)
) )
S&P 500 | ||||||
---|---|---|---|---|---|---|
June 7-14, 2010 | ||||||
date | open | high | low | close | volume | |
2010-06-14 | 1095.00 | 1105.91 | 1089.03 | 1089.63 | 4425830000 | |
2010-06-11 | 1082.65 | 1092.25 | 1077.12 | 1091.60 | 4059280000 | |
2010-06-10 | 1058.77 | 1087.85 | 1058.77 | 1086.84 | 5144780000 | |
2010-06-09 | 1062.75 | 1077.74 | 1052.25 | 1055.69 | 5983200000 | |
2010-06-08 | 1050.81 | 1063.15 | 1042.17 | 1062.00 | 6192750000 | |
2010-06-07 | 1065.84 | 1071.36 | 1049.86 | 1050.47 | 5467560000 | |
Mean | — | 1,069.30 | 1,083.04 | 1,061.53 | 1,072.70 | 5.212 × 109 |
SD | — | 16.41 | 15.43 | 17.91 | 18.66 | 8.454 × 108 |
2.8 cols_xxx()
: manupulate columns’ positions
- Functions:
cols_move_to_start()
: move columns to the start of the table.cols_move_to_end()
: move columns to the end of the table.cols_hide()
: hide columns.
3 Styling of the table
A basic gt
table can be created as so
data("iris")
glimpse(iris)
Rows: 150
Columns: 5
$ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.…
$ Sepal.Width <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.…
$ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.…
$ Petal.Width <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.…
$ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…
You can add row names (rowname_col
argument) and add group names (groupname_col
argument) into the table:
<- iris |>
iris_gt arrange(desc(Sepal.Length)) |> # 6 types of iris with largest sepal length
mutate(Rank = 1:nrow(iris)) |>
slice_head(n = 3, by = Species) |> # select top 3 Sepal length of each species
gt(groupname_col = "Species", rowname_col = "Rank")
iris_gt
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | |
---|---|---|---|---|
virginica | ||||
1 | 7.9 | 3.8 | 6.4 | 2.0 |
2 | 7.7 | 3.8 | 6.7 | 2.2 |
3 | 7.7 | 2.6 | 6.9 | 2.3 |
versicolor | ||||
13 | 7.0 | 3.2 | 4.7 | 1.4 |
14 | 6.9 | 3.1 | 4.9 | 1.5 |
18 | 6.8 | 2.8 | 4.8 | 1.4 |
setosa | ||||
71 | 5.8 | 4.0 | 1.2 | 0.2 |
78 | 5.7 | 4.4 | 1.5 | 0.4 |
79 | 5.7 | 3.8 | 1.7 | 0.3 |
3.1 tab_style
: change style of cells
tab_style(data, style, locations)
allows you to change the style (style
) of cells (locations
) in the table.
style
argument
- the background color of the cell (
cell_fill()
: color) - the cell’s text color, font, and size (
cell_text()
: color, font, size) - the text style (
cell_text()
: style), enabling the use of italics or oblique text. - the text weight (
cell_text()
: weight), allowing the use of thin to bold text (the degree of choice is greater with variable fonts) - the alignment and indentation of text (
cell_text()
: align and indent) - the cell borders (
cell_borders()
)
<- iris_gt |>
iris_gt_colored tab_style( # style for virginica
style = list(
cell_fill(color = "lightblue"),
cell_text(weight = "bold")
),locations = cells_body(
columns = colnames(iris),
rows = Species == "virginica")
|>
) tab_style( # style for versicolor
style = list(
cell_fill(color = "royalblue"),
cell_text(color = "red", weight = "bold")
),locations = cells_body(
columns = colnames(iris),
rows = Species == "versicolor")
) iris_gt_colored
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | |
---|---|---|---|---|
virginica | ||||
1 | 7.9 | 3.8 | 6.4 | 2.0 |
2 | 7.7 | 3.8 | 6.7 | 2.2 |
3 | 7.7 | 2.6 | 6.9 | 2.3 |
versicolor | ||||
13 | 7.0 | 3.2 | 4.7 | 1.4 |
14 | 6.9 | 3.1 | 4.9 | 1.5 |
18 | 6.8 | 2.8 | 4.8 | 1.4 |
setosa | ||||
71 | 5.8 | 4.0 | 1.2 | 0.2 |
78 | 5.7 | 4.4 | 1.5 | 0.4 |
79 | 5.7 | 3.8 | 1.7 | 0.3 |
- Next, the boarder could be added into the table:
|>
iris_gt_colored tab_style( # tab_style to change style of cells,
# cells_borders provides the formatting
# locations tells it where add black borders to all column labels
style = cell_borders(
sides = "left",
color = "black",
weight = px(1.2)
),locations = cells_body(columns = colnames(iris))
|>
) # Add botton line below the column names
tab_style(
style = cell_borders(
sides = "bottom",
color = "black",
weight = px(3)
),locations = cells_column_labels(columns = gt::everything())
)
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | |
---|---|---|---|---|
virginica | ||||
1 | 7.9 | 3.8 | 6.4 | 2.0 |
2 | 7.7 | 3.8 | 6.7 | 2.2 |
3 | 7.7 | 2.6 | 6.9 | 2.3 |
versicolor | ||||
13 | 7.0 | 3.2 | 4.7 | 1.4 |
14 | 6.9 | 3.1 | 4.9 | 1.5 |
18 | 6.8 | 2.8 | 4.8 | 1.4 |
setosa | ||||
71 | 5.8 | 4.0 | 1.2 | 0.2 |
78 | 5.7 | 4.4 | 1.5 | 0.4 |
79 | 5.7 | 3.8 | 1.7 | 0.3 |
3.2 tab_options
: table output options
tab_options
allows you to change the output options of the table. There are multiple options available:
table.font.names=
allows you to change the font of the table.
|>
iris_gt_colored tab_options(
table.font.names = c("Tenor Sans")
)
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | |
---|---|---|---|---|
virginica | ||||
1 | 7.9 | 3.8 | 6.4 | 2.0 |
2 | 7.7 | 3.8 | 6.7 | 2.2 |
3 | 7.7 | 2.6 | 6.9 | 2.3 |
versicolor | ||||
13 | 7.0 | 3.2 | 4.7 | 1.4 |
14 | 6.9 | 3.1 | 4.9 | 1.5 |
18 | 6.8 | 2.8 | 4.8 | 1.4 |
setosa | ||||
71 | 5.8 | 4.0 | 1.2 | 0.2 |
78 | 5.7 | 4.4 | 1.5 | 0.4 |
79 | 5.7 | 3.8 | 1.7 | 0.3 |
table.width=
allows you to change the width of the table.
|>
iris_gt_colored tab_options(table.width = px(700))
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | |
---|---|---|---|---|
virginica | ||||
1 | 7.9 | 3.8 | 6.4 | 2.0 |
2 | 7.7 | 3.8 | 6.7 | 2.2 |
3 | 7.7 | 2.6 | 6.9 | 2.3 |
versicolor | ||||
13 | 7.0 | 3.2 | 4.7 | 1.4 |
14 | 6.9 | 3.1 | 4.9 | 1.5 |
18 | 6.8 | 2.8 | 4.8 | 1.4 |
setosa | ||||
71 | 5.8 | 4.0 | 1.2 | 0.2 |
78 | 5.7 | 4.4 | 1.5 | 0.4 |
79 | 5.7 | 3.8 | 1.7 | 0.3 |