Chapter 6 - Presenting Tables in R

Government Analysis Function and Data Science Campus Logos.

1 Introduction

In most reports, communication of results happens through a combination of data visualization and tables.

We Use a table when you want to present numbers clearly and systematically.

They have features which make them suited to certain types of data.

Tables are good for:

  • Enabling your audience to look up specific values or compare individual values in a table

  • Showing multiple units of measure in the same display

  • Combining summary and detail information in the same display i.e. to show values and their sums

  • Showing data values with a great range between the smallest and largest numbers

In R, there are many ways to create beautiful tables that effectively communicate your results.

  • gt - It provides a Grammar of Tables to turn tabular data into a table

  • kableExtra - great for HTML

  • formattable- Great for custom fill of cells and HTML

  • reactable - Great for reactive tables

  • flextable - A very useful package for Word-based table

2 Preliminaries

2.1 Packages

As always, the first port of call is to bring in the packages we need for this session.

library(gt) # Grammar of tables
Warning: package 'gt' was built under R version 4.4.2
library(tidyverse) # Data Manipulation
Warning: package 'tidyverse' was built under R version 4.4.2
Warning: package 'ggplot2' was built under R version 4.4.2
Warning: package 'tidyr' was built under R version 4.4.2
Warning: package 'readr' was built under R version 4.4.2
Warning: package 'purrr' was built under R version 4.4.2
Warning: package 'stringr' was built under R version 4.4.2
Warning: package 'forcats' was built under R version 4.4.2
Warning: package 'lubridate' was built under R version 4.4.2
library(janitor) # Clean names 
Warning: package 'janitor' was built under R version 4.4.2

2.2 Data

Next is to read in the dataset that we will use, which is the same as we have used in previous chapters, the gapminder data. We will clean column names with janitor and drop the NA values as we have done previously.

# Importing data using the read_csv function 

gapminder <- read_csv("./data/gapminder.csv")

# Cleaning column names using the clean_names function

# Dropping missing values using the drop_na function 

gapminder <- clean_names(gapminder) |>
             drop_na()

3 gt

gt stands for “grammar of tables”.

The goal of gt is similar to that of ggplot2, is to make it easy to produce nice-looking display tables. e.g. summary tables, presentation tables.

This is a recent package been released in March 2020, developed by Richard Iannone.

It was designed to mirror the layered style of creating ggplot2 figures.

If you need to make beautiful customized tables then gt is great choice. There are so many ways to structure a table, apply formatting and annotations, and style it just the way you want.

Currently gt renders tables to the HTML output format (and has the ability to export to image files).

If your primary output is model summaries rather than data tables, make sure to check out the wonderful gt summary R package which extends the gt package for statistical model summaries.

Categorizing the useful features of a table.

Features of a table

Image taken from: (https://github.com/laRusers/presentations/blob/master/2020-05-27-rich-avik/gt_talk_la_r_users-rich_i.pdf).

3.1 Parts of a gt table

The table has the components shown in the image below, within each of these components, there may be sub components. e.g. the table header contains a title and subtitle

Parts of a gt table Image taken from: (https://gt.rstudio.com/articles/intro-creating-gt-tables.html).

  • Table Header - contains a title and subtitle

  • Stub and the Stub Head - contains row labels

  • Column Labels - contains column labels

  • Table Body -contains columns and rows of cells

  • Table Footer - footnotes and source notes

3.2 Creating a Basic Table

lets first of all get our data ready, we will continue using the gapminder data.

# Data manipulation

population_for_select_countries_3y <- gapminder |>
  filter(year %in% c(1997, 2002, 2007)) |>
  select(country, continent, year, pop) |>
  # Reshaping the data
  tidyr::pivot_wider(names_from = year, values_from = pop) |>
  tidyr::drop_na() |>
         group_by(continent) |>
  # Selecting the top 2 values
  dplyr::top_n(n = 2)
Selecting by 2007
# Cleaning the column names
population_for_select_countries_3y <- janitor::clean_names(population_for_select_countries_3y)

# To Display the data
population_for_select_countries_3y
# A tibble: 10 × 5
# Groups:   continent [5]
   country       continent      x1997      x2002      x2007
   <chr>         <chr>          <dbl>      <dbl>      <dbl>
 1 Australia     Oceania     18565243   19546792   20434176
 2 Brazil        Americas   168546719  179914212  190010647
 3 China         Asia      1230075000 1280400000 1318683096
 4 Egypt         Africa      66134291   73312559   80264543
 5 Germany       Europe      82011073   82350671   82400996
 6 India         Asia       959000000 1034172547 1110396331
 7 New Zealand   Oceania      3676187    3908037    4115771
 8 Nigeria       Africa     106207839  119901274  135031164
 9 Turkey        Europe      63047647   67308928   71158647
10 United States Americas   272911760  287675526  301139947

We will use the gt function to create a table.

gt(): Create a gt table object (this is similar to creating a baseline plot object with ggplot()) and the function takes a massive number of arguments, some we will introduce here are the following:

  • data,
  • rowname_col = “rowname”,
  • groupname_col = group_vars(data),
  • rownames_to_stub = FALSE,
  • auto_align = TRUE,
  • id = NULL,
  • row_group.sep = getOption(“gt.row_group.sep”, ” - “)

We will explain what these mean in detail as we proceed through the chapter.

# Basic table

# Creating a gt table
population_for_select_countries_3y |>
  gt()
country x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

Once we have the gt table object, we style and edit it as we please.

Please note that her I am using grouped tibble (via dplyr’s group_by()) for more precise divisions of rows into row groups.

We can also use the arguments, rowname_col and groupname_col to do our groupings.

# Specifying the rowname and groupname

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent")
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

Typically, the data in the groupname_col will consist of categories of data in a table and the data in the rowname_col are unique labels (perhaps unique across the entire table or unique within groups).

Another useful function is the gt_preview() which shows a nicely-formatted preview of a data table (defaulting to the first 5 rows and the last row).

3.3 Titles and Subtitles

To add a heading and subtitle we can use the tab_header().

This includes a title and subtitle.

tab_header(): Add a table header and takes the following arguments:

  • data,
  • title,
  • subtitle = NULL
# Add Title and Subtitle
# We use tab_header and specify the title and subtitle arguments

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  tab_header(title = "Table 1: Population of select countries over three years:
             Population of select countries over three years",
             subtitle = "This is my subtitle")
Table 1: Population of select countries over three years: Population of select countries over three years
This is my subtitle
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

We may also style the title and subtitle using Markdown or HTML formatting. We do this by wrapping the values passed to title or subtitle with the with md() or html().

# Add Title and Subtitle with markdown

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  # Markdown formating
  tab_header(title = gt::md("**Table 1: Population of select countries over three years:
                            Population of select countries over three years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             )
Table 1: Population of select countries over three years: Population of select countries over three years
Population for 1997, 2002 and 2007
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.4 Spanner and Stub head

A stub is a special column that contains row labels/names.

Just as with the stub, we can create groupings of columns with spanner column labels that encompass one or more columns. The tab_spanner() function makes this possible. This label is placed above one or more column labels, spanning the width of those columns and column labels.

Should the columns not be adjacent to each other, tab_spanner() will automatically gather them together.

tab_spanner(): Add a spanner column label and takes the following arguments:

  • data,
  • label,
  • columns,
  • gather = TRUE

tab_stubhead(): Add label text to the stubhead and takes some similar arguments:

  • data,
  • label

With the columns argument we can use column names in double quotes (“”), in vars() (vars()), or, we can use the following tidyselect expressions:

  • contains(): contains a literal string
  • matches(): matches a regular expression
  • starts_with(): starts with a prefix
  • ends_with(): ends with a suffix
  • everything(): selects all columns

These are covered in Introduction to R Chapter 4, so feel free to review these should you need a reminder.

# Add spanner and stub head

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  tab_header(title = gt::md("**Table 1: Population of select countries over three
                            years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             ) |>
  # Add a spanner
  tab_spanner(label = gt::md("**Years**"), columns = everything()
              ) |>
  # Add a stub head
  tab_stubhead(label = gt::md("**Continent**")
               )
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
Continent
Years
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.5 Source and Footnotes

A source note is useful for citing the data included in the table.

To add a source and footnote we can use the tab_source_note() and tab_footnote().

tab_source_note(): Add a source note citation, taking the following arguments:

  • data
  • source_note
# Source and footnote

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  tab_header(title = gt::md("**Table 1: Population of select countries over three
                            years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             ) |>
  tab_spanner(label = gt::md("**Years**"), columns = everything()
              ) |>
  # Add a source note
  tab_source_note(source_note = "Gapminder.org")
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
Years
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
Gapminder.org

Several can be added to the footer, simply use multiple calls of tab_source_note() and they will be inserted in the order provided.

Just like the headers we can also use Markdown formatting for the note

# Source and footnote - multiple (using markdown)


population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  tab_header(title = gt::md("**Table 1: Population of select countries over three
                            years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             ) |>
  tab_spanner(label = gt::md("**Years**"), columns = everything()
              ) |>
  # Add a source note
  tab_source_note(source_note = gt::md("Gapminder.org")
                  ) |>
  # Add another source note
  tab_source_note(source_note = gt::md("This is my second source note")
                  )
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
Years
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
Gapminder.org
This is my second source note

The formatting of the source notes can be controlled through the use of various parameters in the tab_options() function. We will look at this later on through the course.

tab_footnote(): Add a table footnote, using the following arguments:

  • data
  • footnote
  • locations

There are two components to a footnote:

  • A footnote mark that is attached to the targeted cell text, and

  • The footnote text (that starts with the corresponding footnote mark) that is placed in the table’s footer area.

The arguments we need to provide are footnote and locations.

Locations is the cell or set of cells to be associated with the footnote. Supplying any of the cells_*() helper functions is a useful way to target the location cells that are associated with the footnote text.

These helper functions are:

  • cells_title() - Location helper for targeting the table title and subtitle

  • cells_stubhead() - Location helper for targeting the table stubhead cell

  • cells_column_spanners() - Location helper for targeting the column spanners

  • cells_column_labels() - Location helper for targeting the column labels

  • cells_row_groups() - Location helper for targeting row groups

  • cells_stub() - Location helper for targeting cells in the table stub

  • cells_body() - Location helper for targeting data cells in the table body

  • cells_summary() - Location helper for targeting group summary cells

Additionally, we can enclose several cells_*() calls within a list() if we wish to link the footnote text to different types of locations (e.g., body cells, row group labels, the table title, etc.).

# We can footnotes using tab footnote - using locations

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  tab_header(title = gt::md("**Table 1: Population of select countries over three
                            years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             ) |>
  tab_source_note(source_note = gt::md("Gapminder.org")
                  ) |>
  tab_spanner(label = gt::md("**Years**"), columns = everything()
              ) |>
  tab_source_note(source_note = gt::md("This is my second source note")
                  ) |>
  # Add a footnote
  tab_footnote(footnote = "This is my footnote",
               locations = cells_column_labels(columns = ("x1997")))
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
Years
x19971 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
Gapminder.org
This is my second source note
1 This is my footnote

We can add multiple footnotes.

# Multiple footnotes

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  tab_header(title = gt::md("**Table 1: Population of select countries over three
                            years**"),
             subtitle = gt::md("*Population for 1997, 2002 and 2007*")
             ) |>
  tab_spanner(label = gt::md("**Years**"), columns = everything()
              ) |>
  tab_source_note(source_note = gt::md("Gapminder.org")
                  ) |>
  tab_source_note(source_note = gt::md("This is my second source note")
                  ) |>
  # Add a footnote
  tab_footnote(footnote = "This is my footnote",
               locations = cells_column_labels(columns = ("x1997")
                                               )
  ) |>
  # Add another footnote
  tab_footnote(footnote = "This is my second footnote",
               locations = cells_row_groups(groups = TRUE)
               )
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
Years
x19971 x2002 x2007
Oceania2
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas2
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia2
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa2
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe2
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
Gapminder.org
This is my second source note
1 This is my footnote
2 This is my second footnote

3.6 Exercise

  1. Create a gt table with the exibble dataset.

  2. Add a title and Subtitle to your table, format the title so that its in bold.

  3. Add a spanner and a stubhead to your table.

  4. Add a footnote (footnote should be in the num column highlighting the missing value).

  1. Create a gt table with the exibble dataset.
# Creating a gt table

exibble |>
  gt()
  1. Add a title and Subtitle to your table, format the title so that its in bold.
# Adding a header

exibble |>
  gt() |>
  tab_header() # adding a header
  1. Add a spanner and a stubhead to your table.
# Adding a spanner and stub head

exibble |>
  gt() |>
  tab_spanner() |> # Add a spanner
  tab_stubhead() # Add a stub head
  1. Add a footnote (footnote should be in the num column highlighting the missing value).
# Adding a footnote

exibble |>
  gt() |>
  tab_footnote() # Adding a footnote
  1. Create a gt table with the exibble dataset.
# creating a gt table

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group")
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
  1. Add a title and Subtitle to your table, format the title so that its in bold.
# Adding a header

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group") |>
  
  tab_header(title = md("**Title**"),
             subtitle = "Subtitle")
Title
Subtitle
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
  1. Add a spanner and a stubhead to your table.
# Adding a spanner and stub head

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group") |>
  tab_header(title = md("**Title**"),
             subtitle = "Subtitle") |> 
  # Add a spanner
  tab_spanner(label = md("**Columns**"),
              columns = everything()
              ) |> 
  # Add a stub head
  tab_stubhead(label = md("**My Groups**")
               )
Title
Subtitle
My Groups
Columns
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
  1. Add a footnote (footnote should be in the num column highlighting the missing value).
# Adding a footnote

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group") |>
  tab_header(title = md("**Title**"),
             subtitle = "Subtitle") |>
  tab_spanner(label = md("**Columns**"),
              columns = everything()
              ) |>
  tab_stubhead(label = md("**My Groups**")
               ) |>
  # Add a footnote
  tab_footnote(footnote = "A footnote",
               locations = cells_body(columns = 1, rows = 6)
               )
Title
Subtitle
My Groups
Columns
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 1 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
1 A footnote

3.7 Modfying Columns

The cols_*() functions allow for modifications that act on entire columns.

  • cols_align() - Set the alignment of columns

  • cols_width() - Set the widths of columns

  • cols_label() - Relabel one or more columns

  • cols_move_to_start() - Move one or more columns to the start

  • cols_move_to_end() - Move one or more columns to the end

  • cols_move() - Move one or more columns

  • cols_hide() - Hide one or more columns

  • cols_merge_range() - Merge two columns to a value range column

  • cols_merge() - Merge data from two or more columns to a single column

3.7.1 Aligning Columns

We can set the alignment of columns using:

cols_align(): Set the alignment of columns, taking the following arguments:

  • data
  • align = c(“auto”, “left”, “center”, “right”)
  • columns = TRUE

Note that in newer versions of gt, vars() has been deprecated and as such, it is preferred to use c() instead to give a vector of columns.

# Align column text

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  # Center column text
  cols_align(align = "center",
             columns = c(x1997, x2002, x2007)
             )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

The individual alignments of columns (which includes the column labels and all of their data cells) can be modified. We have the option to align text to the left, the center, and the right.

we can also allow gt to automatically choose the alignment of each column based on the data type (with the “auto” option).

3.7.2 Relabel Columns

We can relabel our columns using

cols_label(): Relabel one or more columns, with the following arguments:

  • data
  • … (old_name = “new_name”)
  • .list = list2(…)

Column labels can be modified from their default values (the names of the columns from the input table data).

When you create a gt table object using gt(), column names effectively become the column labels. While this serves as a good first approximation, column names aren’t often appealing as column labels in a gt output table. The cols_label() function provides the flexibility to relabel one or more columns.

# relabel columns

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  # relabel columns
  cols_label(x1997 = "1997",
             x2002 = "2002",
             x2007 = "2007")
1997 2002 2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

We also have the option to use the md() of html() helper functions for rendering column labels from Markdown or using HTML.

# relabel columns
# Formating using md


population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  # relabel columns
  cols_label(x1997 = md("**1997**"),
             x2002 = md("**2002**"),
             x2007 = md("**2007**")
             )
1997 2002 2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.7.3 Column Width

We can modify the column widths using,

cols_width(): Set the widths of columns with the following arguments:

  • data
  • .list = list2(…)

Manual specifications of column widths can be performed using the cols_width() function.

We choose which columns get specific widths (in pixels, usually by use of the px() helper function). Width assignments are supplied in “…” through two-sided formulas, where the left-hand side defines the target columns and the right-hand side is a single width value in pixels. This is similar to the case_when() function.

# Cell width

population_for_select_countries_3y |>
  gt(rowname_col = "country",
    groupname_col = "continent") |>
  cols_width(c(x2007) ~ px(120),
             c(x2002) ~ px(100),
             everything() ~ px(70)
             )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

Note that:

  • The … is covered in the Introduction to R - Control Flow and Functions section 5.7

  • The structure is very similar to the case when function which is covered in the the Introduction to R - Control Flow and Functions section 4.4.

3.7.4 Moving Columns

We can move columns using

cols_move(): Move one or more columns with the following arguments:

  • data
  • columns
  • after

On those occasions where you need to move columns this way or that way, we can make use of the cols_move() function. While it’s true that the movement of columns can be done upstream of gt’s API, it is much easier and less error prone to use the function provided here.

The movement procedure here takes one or more specified columns (in the columns argument) and places them to the right of a different column (the after argument). The ordering of the columns to be moved is preserved, as is the ordering of all other columns in the table.

# Moving Columns

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  cols_move(columns = c(x2002, x1997),
            after = c(x2007)
            )
x2007 x2002 x1997
Oceania
Australia 20434176 19546792 18565243
New Zealand 4115771 3908037 3676187
Americas
Brazil 190010647 179914212 168546719
United States 301139947 287675526 272911760
Asia
China 1318683096 1280400000 1230075000
India 1110396331 1034172547 959000000
Africa
Egypt 80264543 73312559 66134291
Nigeria 135031164 119901274 106207839
Europe
Germany 82400996 82350671 82011073
Turkey 71158647 67308928 63047647

3.7.5 Hiding Columns

We can hide columns using

cols_hide(): Hide one or more columns with the following arguments:

  • data
  • columns

The cols_hide() function allows us to hide one or more columns from appearing in the final output table. While it’s possible and often desirable to omit columns from the input table data before introduction to the gt() function, there can be cases where the data in certain columns is useful (as a column reference during formatting of other columns) but the final display of those columns is not necessary.

# Hiding  Columns

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  cols_hide(columns = c(x1997, x2002)
            )
x2007
Oceania
Australia 20434176
New Zealand 4115771
Americas
Brazil 190010647
United States 301139947
Asia
China 1318683096
India 1110396331
Africa
Egypt 80264543
Nigeria 135031164
Europe
Germany 82400996
Turkey 71158647

3.8 Modifying Rows

3.8.1 Re-Ordering Rows

We can modify entire rows is the row_group_order():

row_group_order(): modify the display order of any row groups with the following arguments

  • data
  • groups
# modify the ordering of any row groups

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  row_group_order(groups = c("Africa",
                             "Asia",
                             "Americas",
                             "Europe",
                             "Oceania")
                  )
x1997 x2002 x2007
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771

3.8.2 Adding Summary Rows

There are two functions that will add rows to a gt table: summary_rows() and grand_summary_rows(). These are useful for adding groupwise and grand summary rows.

  • summary_rows() - Add groupwise summary rows using aggregation functions

  • grand_summary_rows() - Add grand summary rows using aggregation functions

summary_rows(): Add groupwise summary rows using aggregation functions with the following arguments:

  • data,
  • groups = NULL,
  • columns = TRUE,
  • fns,
  • missing_text = “—”,
  • formatter = fmt_number, …
# Add summary rows

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  summary_rows(groups = TRUE, # = `TRUE` works to target all groups
               columns = everything(),
               fns = list(my_total = "sum",
                          my_mean = "mean")
               )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
# Add summary rows

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  summary_rows(groups = TRUE, # = `TRUE` works to target all groups
               columns = everything(),
               fns = list(my_total = "sum",
                          my_mean = "mean"),
               formatter = fmt_number,
               decimals = 0)
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

grand_summary_rows(): Add grand summary rows using aggregation functions with the following arguments:

  • data
  • columns = TRUE
  • fns
  • missing_text = “—”
  • fmt = ~ (., …) …

Add grand summary rows to the gt table by using applying aggregation functions to the table data. The summary rows incorporate all of the available data, regardless of whether some of the data are part of row groups.

# Add grand summary rows
# Formating the numbers

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  grand_summary_rows(columns = c(x1997, x2002, x2007),
                     fns = list(my_min = "min",
                                my_max = "max",
                                my_avg = "mean"),
                     fmt = ~ fmt_number(., decimals = 0))
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
min 3,676,187 3,908,037 4,115,771
max 1,230,075,000 1,280,400,000 1,318,683,096
mean 297,017,576 314,849,055 331,363,532

3.9 Formatting Data

We can use the fmt_*() functions.

Some examples are shown below, refer to the documentation for a full list.

  • fmt_number()

  • fmt_date()

  • fmt_currency()

  • fmt_percent()

  • fmt_scientific()

We can also us the general fmt() which provides greater control in formatting raw data values than any of the specialized fmt_*().

3.9.1 Formatting Numbers

fmt_number(): Format numeric values with the following arguments:

  • data,
  • columns,
  • rows = NULL,
  • decimals = 2,
  • n_sigfig = NULL,
  • drop_trailing_zeros = FALSE,
  • drop_trailing_dec_mark = TRUE,
  • use_seps = TRUE,
  • scale_by = 1,
  • suffixing = FALSE,
  • pattern = “{x}”,
  • sep_mark = “,”,
  • dec_mark = “.”,
  • locale = NULL

You can format the numbers to have commas so they are easier to read.

# Format numbers

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  # Fomart numbers
  fmt_number(columns = c('x1997', 'x2002', 'x2007'),
             use_seps = TRUE,
             decimals = 0)
x1997 x2002 x2007
Oceania
Australia 18,565,243 19,546,792 20,434,176
New Zealand 3,676,187 3,908,037 4,115,771
Americas
Brazil 168,546,719 179,914,212 190,010,647
United States 272,911,760 287,675,526 301,139,947
Asia
China 1,230,075,000 1,280,400,000 1,318,683,096
India 959,000,000 1,034,172,547 1,110,396,331
Africa
Egypt 66,134,291 73,312,559 80,264,543
Nigeria 106,207,839 119,901,274 135,031,164
Europe
Germany 82,011,073 82,350,671 82,400,996
Turkey 63,047,647 67,308,928 71,158,647

You can also add a suffix like ‘M’ for millions.

# Format numbers
# Adding a suffix

population_for_select_countries_3y |>
  gt(rowname_col = "country",
     groupname_col = "continent") |>
  # Fomart numbers
  fmt_number(columns = c('x1997', 'x2002', 'x2007'),
             decimals = 2,
             suffixing = TRUE)
x1997 x2002 x2007
Oceania
Australia 18.57M 19.55M 20.43M
New Zealand 3.68M 3.91M 4.12M
Americas
Brazil 168.55M 179.91M 190.01M
United States 272.91M 287.68M 301.14M
Asia
China 1.23B 1.28B 1.32B
India 959.00M 1.03B 1.11B
Africa
Egypt 66.13M 73.31M 80.26M
Nigeria 106.21M 119.90M 135.03M
Europe
Germany 82.01M 82.35M 82.40M
Turkey 63.05M 67.31M 71.16M

Take some time to experiment with these functions when you come to make a production quality table.

3.10 Exercise

  1. Using the table from the previous exercise, right align the column text.

  2. Set the table width to be 100 and then modify the width of the columns to be the following

    • num should be 80
    • char should be 80
    • date should be 80
    • the rest of the columns to be 50
  3. Format the num column to have 2 decimal places and show the separator

  1. Using the table from the previous exercise, right align the column text.
# Align Columns

exibble |>
  gt(
    rowname_col = "row",
    groupname_col = "group"
  ) |>
  cols_align() # Align Columns
  1. Set the table width to be 100 and then modify the width of the columns to be the following

    • num should be 80
    • char should be 80
    • date should be 80
    • the rest of the columns to be 50
# Table width at 100%
# Column width

exibble |>
  gt(
    rowname_col = "row",
    groupname_col = "group"
  ) |>
  tab_options() |> # Table width at 100%
  cols_width() # Column width
  1. Using the table from the previous exercise, right align the column text.
# Align Columns

exibble |>
  gt(rowname_col = "row",
     groupname_col = "group") |>
  cols_align(columns = everything(), 
             # Align Columns
             align = "right")
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
  1. Set the table width to be 100 and then modify the width of the columns to be the following

    • num should be 80
    • char should be 80
    • date should be 80
    • the rest of the columns to be 50
# Table width at 100%
# Column width

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group") |>
  # Table width at 100%
  tab_options(table.width = pct(100)
              ) |> 
  # Column width
  cols_width(vars(num) ~ px(80),
             ends_with("r") ~ px(80),
             starts_with("date") ~ px(80),
             everything() ~ px(50)
             )
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440

For more information on table widths refer to table widths specifications

  1. Format the num column to have 2 decimal places and show the separator
# Formating numbers

exibble |>
  gt(rowname_col = "row",
    groupname_col = "group") |>
  # Formating numbers
  fmt_number(columns = vars(num),
             decimals = 2,
             use_seps = TRUE)
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
num char fctr date time datetime currency
grp_a
row_1 0.11 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.22 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 33.33 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 444.40 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5,550.00 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 777,000.00 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8,880,000.00 honeydew eight 2015-08-15 20:20 NA 0.440

3.11 Tab Style

We can customise our table using

tab_style(): Add custom styles to one or more cells with the following arguments:

  • data
  • style
  • locations

This is done with the help of the following functions:

  • cell_text()
  • cell_fill()
  • cell_borders()

For locations we use the cells_*() functions, just like in the tab_footnote() function.

3.11.1 Fonts

# tab style to change fonts

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_style(
    style = cell_text(font = "Times New Roman"),
    locations = cells_body()
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
# Bold font

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body()
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.11.2 Colours

# Colours based on a condition

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_style(
    style = cell_fill(color = "#E2BC22"),
    locations = cells_body(
      columns = c(x1997),
      rows = x1997 > 272911760 # Condition
    )
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.11.3 Combining styles together

# Multiple tab styles

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_style(
    style = cell_fill(color = "#3B7A9E"),
    locations = cells_body(
      columns = c(x1997),
      rows = x1997 > 3676187
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#a8bd3a"),
      cell_text(style = "italic")
    ),
    locations = cells_body(
      columns = c(x2002),
      rows = x2002 > 36761870
    )
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12 Table Option Functions

With the opt_*() functions, we have an easy way to set commonly-used table options. For example, we can modify the set of marks to use with footnotes, turn on row striping, change the alignment of the table header, and much more.

Important functions:

  • opt_footnote_marks()
  • opt_row_striping()
  • opt_align_table_header()
  • opt_all_caps()
  • opt_table_lines()
  • opt_table_outline()
  • opt_table_font()
  • opt_css()

Helpers for specifying fonts: - default_fonts()

3.12.1 Modify Footnotes

We can modify the footnote marks using,

opt_footnote_marks(): Modify the set of footnote marks with the following parameters:

  • data
  • marks

Either a vector of marks can be provided (including Unicode characters), or, a specific keyword could be used to signify a preset sequence.

# Alter footnote marks

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_footnote(
    footnote = "This is my footnote",
    locations = cells_column_labels(columns = ("x1997"))
  ) |>
  opt_footnote_marks(marks = "standard")
x1997* x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647
* This is my footnote

3.12.2 Row Striping

By default, a gt table does not have row striping enabled. However, this function allows us to easily enable or disable striped rows in the table body.

# Add row striping

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  opt_row_striping()
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12.3 Align Table Header

By default, a table header added to a gt table has center alignment for both the title and the subtitle elements. This function allows us to easily set the horizontal alignment of the title and subtitle to the left or right by using the “align” argument.

opt_align_table_header(): Option to align the table header with the following parameters:

  • data
  • align = c(“left”, “center”, “right”)
# Align table header left

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_header(
    title = "Table 1: Population of select countries over three Years",
    subtitle = "Population for 1997, 2002 and 2007"
  ) |>
  opt_align_table_header(align = "left")
Table 1: Population of select countries over three Years
Population for 1997, 2002 and 2007
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12.4 Capitalise

Sometimes an all-capitalized look is suitable for a table.

With the opt_all_caps() function, we can transform characters in the column labels, the stub, and in all row groups in this way (and there’s control over which of these locations are transformed).

opt_all_caps(): Option to use all caps in select table locations with the following parameters:

  • data,
  • all_caps = TRUE,
  • locations = c(“column_labels”, “stub”, “row_group”)
# Capitalise

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  opt_all_caps()
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12.5 Table Lines

The opt_table_lines() function sets table lines in one of three possible ways:

  • all possible table lines drawn (“all”)

  • no table lines at all (“none”)

  • resetting to the default line styles (“default”).

This is great if you want to start off with lots of lines and subtract just a few of them with tab_style(). Or, use it to start with a completely lineless table, adding individual lines as needed.

# Amend table lines

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  opt_table_lines(extent = "all")
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12.6 Table Outline

We can amend the table outline.

opt_table_outline(): Option to wrap an outline around the entire table with the following parameters:

  • data
  • style = “solid”
  • width = px(3)
  • color = “#D3D3D3”

This function puts an outline of consistent style, width, and color around the entire table. It’ll write over any existing outside lines so long as the width is larger that of the existing lines.

The default value of style (“solid”) will draw a solid outline, whereas a value of “none” will remove any present outline.

# Amend table outline

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  opt_table_outline(
    width = px(6),
    color = "black"
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.12.7 Fonts

The opt_table_font() function makes it possible to define a custom font for the entire gt table. The standard fallback fonts are still set by default but the font defined here will take precedence. You could still have different fonts in select locations in the table, and for that you would need to use tab_style() in conjunction with the cell_text() helper function.

opt_table_font(): Option to define a custom font for the table with the following parameters:

  • data
  • font
  • weight = NULL
  • style = NULL
  • add = TRUE
# Amend fonts for entire table

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  opt_table_font(font = "Times New Roman")
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.13 Tab Options

This section contains a ton of options we can amend. We can either use the common options with the opt_*() functions or we can use the tab_options.

tab_options(): Modify the table output options which can take over 100 parameters, check the documentation to see the numerous applications available to us.

3.13.1 Borders

# Style and borders

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_options( # change the column labels section
    column_labels.border.bottom.color = "black",
    column_labels.border.top.color = "black",
    column_labels.border.bottom.width = 3,
    column_labels.border.top.width = 3,

    # change the bottom of the body
    table_body.border.bottom.color = "black"
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.13.2 Table Width

Modify the table width (with table.width) to 100% (which spans the entire content width area).

# Make the width 100% using the pct() helper function


population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_options( # make the width 100%
    table.width = pct(100)
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.13.3 Background Colour

Modify the table’s background color (with table.background.color).

# Table background colour

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  tab_options(
    table.background.color = "#1380A1",
  )
x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.14 Creating a Style Function

Similar to ggplot themes we can create a function with our table layout and styles. We can use this function on multiple tables that we wish to have the same styles.

For a reminder on writing User Defined Functions (UDFs), refer to Control Flow, Loops and Functions in R.

# Creating a function to theme different tables, reducing code repetition

table_style_function <- function(table_data) {
  gt(table_data) |>
    tab_options( # change the column labels section
      column_labels.border.bottom.color = "black",
      column_labels.border.top.color = "black",
      column_labels.border.bottom.width = 3,
      column_labels.border.top.width = 3,
      table.width = pct(100),
      table.background.color = "white",

      # change the bottom of the body
      table_body.border.bottom.color = "black",
      heading.title.font.size = px(16),
    ) |>
    cols_align(align = "center") |>
    opt_align_table_header(align = "left") |>
    tab_style(
      style = cell_borders(
        sides = c("top", "bottom"),
        color = "white",
        weight = px(1)
      ),
      locations = cells_body(
        columns = everything(),
        rows = everything()
      )
    )
}

We can use apply our function either specifying the table data argument or using within a pipe.

# We can apply our function as an argument

population_for_select_countries_3y |>
  table_style_function()
country x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

We can then add additional formatting similar to way we did with ggplot.

# We can add additional formating

population_for_select_countries_3y |>
  table_style_function() |>
  tab_header(
    title = gt::md("**Table 1: Population of select countries over three years**"),
    subtitle = gt::md("*Population for 1997, 2002 and 2007*")
  )
Table 1: Population of select countries over three years
Population for 1997, 2002 and 2007
country x1997 x2002 x2007
Oceania
Australia 18565243 19546792 20434176
New Zealand 3676187 3908037 4115771
Americas
Brazil 168546719 179914212 190010647
United States 272911760 287675526 301139947
Asia
China 1230075000 1280400000 1318683096
India 959000000 1034172547 1110396331
Africa
Egypt 66134291 73312559 80264543
Nigeria 106207839 119901274 135031164
Europe
Germany 82011073 82350671 82400996
Turkey 63047647 67308928 71158647

3.15 Saving Tables

The gtsave() function makes it easy to save a gt table to a file. The function assumes the output file type by the extension provided in the output filename.

This will produce either an HTML, PDF, PNG, LaTeX, or RTF file.

gtsave(): Save a gt table as a file with the following parameters:

  • data
  • filename
  • path = NULL

trick is to name the file in a supported format (e.g., “gt_table.rtf” for an RTF file containing the table).

# Saving the table

population_for_select_countries_3y |>
  gt(
    rowname_col = "country",
    groupname_col = "continent"
  ) |>
  gtsave(filename = "test.png", path = "C:/My_RStudio/Workspace/test.png")

3.16 Exercise

  1. Recreate the table below
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440
# Change the column labels section
# Change the bottom of the bold
# Align columns
# Tab style to fill in grey
# Tab style to highlight to red

exibble |>
  gt(
    rowname_col = "row",
    groupname_col = "group"
  ) |>
  tab_options() |> # Change borders
  cols_align() |> # Align columns
  tab_style() |> # Tab style to fill in grey
  tab_style() |> # Tab style to highlight to red
  tab_style()
# Change the column labels section
# Change the bottom of the bod
# Align columns
# Tab style to fill in grey
# Tab style to highlight to red

exibble |>
  gt(
    rowname_col = "row",
    groupname_col = "group"
  ) |>
  tab_options( # change the column labels section
    column_labels.border.bottom.color = "black",
    column_labels.border.top.color = "black",
    column_labels.border.bottom.width = 3,
    column_labels.border.top.width = 3,
    table.width = pct(100),
    table.background.color = "white",

    # change the bottom of the body
    table_body.border.bottom.color = "black"
  ) |>
  cols_align(align = "center") |>
  tab_style(
    style = cell_borders(
      sides = c("top", "bottom"),
      color = "white"
    ),
    locations = cells_body(
      columns = everything(),
      rows = everything()
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "lightgrey"),
      cell_borders(
        side = c("left", "right"),
        color = "black",
        weight = px(2)
      )
    ),
    locations = cells_body(
      columns = c(currency)
    )
  ) |>
  tab_style(cell_text(color = "red"),
    locations = cells_body(
      columns = c(char),
      rows = char == "coconut"
    )
  )
num char fctr date time datetime currency
grp_a
row_1 1.111e-01 apricot one 2015-01-15 13:35 2018-01-01 02:22 49.950
row_2 2.222e+00 banana two 2015-02-15 14:40 2018-02-02 14:33 17.950
row_3 3.333e+01 coconut three 2015-03-15 15:45 2018-03-03 03:44 1.390
row_4 4.444e+02 durian four 2015-04-15 16:50 2018-04-04 15:55 65100.000
grp_b
row_5 5.550e+03 NA five 2015-05-15 17:55 2018-05-05 04:00 1325.810
row_6 NA fig six 2015-06-15 NA 2018-06-06 16:11 13.255
row_7 7.770e+05 grapefruit seven NA 19:10 2018-07-07 05:22 NA
row_8 8.880e+06 honeydew eight 2015-08-15 20:20 NA 0.440

3.17 Best Practice

For full guidance refer to the Tables section of the Analysis Function guidance hub.

  • The font type and size should match the font used in your publication. The font should be mono-spaced, with the same width for each character or digit, so that all the units align. Serifed fonts are usually harder to read for dyslexic readers, so we recommend that you use an accessible, sans serif font like Arial, Helvetica, Myriad Pro, Lucida Sans, Open Sans or Verdana.

  • Do not put numbers in bold as they will move from their correct alignment. Present the numbers close together in columns, not rows and we should always separate out thousands with commas to make them easier to read.

  • Always right-align headings and data in columns so that units, tens, hundreds and thousands are aligned and numbers of equal value are easily comparable.

  • Use the same measure across all variables where possible.

  • Use lines and shading sparingly. Use subtle colours or greyscale. Make sure the shading does not distract from the data. Shading can be used to track data across rows but also to highlight specific values.

  • All tables should have a title to make the purpose of the table clear, as well as a sub-heading which is more statistical/technical in nature.

  • Make your column titles clear. Try using boldface type or lines to offset them from the numbers and text in the body of the table. The headings should also state the units of the row/column unless the entire table adheres to the same unit.

  • Right-align numbers along the decimal place or comma. We might need to add zeros to maintain the alignment, but it’s worth it so the numbers are easier to read and scan.

An example table showing the median delay in registration for COVID-19 deaths and causes.

Example Table

Image taken from: (https://www.ons.gov.uk/peoplepopulationandcommunity/birthsdeathsandmarriages/deaths/bulletins/deathsinvolvingcovid19englandandwales/deathsoccurringinjune2020) Section 7.

We can recreate the table in the image above, data can be downloaded in the link above.

# Loading data

covid_table_data <- read_csv("./data/table_data.csv",
  skip = 3,
  n_max = 5
)

# Cleaning Column Names

covid_table_data <- janitor::clean_names(covid_table_data)
# Creating our table

covid_table_data |>
  gt() |>
  # header
  tab_header(title = md("Median registration delay, lower and upper quartiles, 
                 minimum and maximum delay for deaths occurring in England and Wales, March to June                     2020")) |>
  # Adding Source Notes <br> for the spaces
  tab_source_note(source_note = md("Source: Office for National Statistics – Deaths involving                       COVID-19 <br> <br>")) |>
  tab_source_note(source_note = md("Notes <br> <br>")) |>
  tab_source_note(source_note = "1. Figures are provisional.") |>
  tab_source_note(source_note = "2. Based on deaths occurring in March to June 2020 rather than                                         deaths registered in March to June 2020.") |>
  tab_source_note(source_note = "3. Including deaths registered up until 4 July 2020.") |>
  # Bold column labels
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels(everything())
  ) |>
  # Table lines
  opt_table_lines(extent = "default") |>
  # Align Header
  opt_align_table_header("left") |>
  cols_label(
    statistics_days = "Statistics(days)",
    all_causes_of_death = "All causes of death",
    deaths_involving_covid_19 = "Deaths involving COVID-19"
  ) |>

  # Column border labels
  tab_options(
    table.border.top.color = "white",
    table.border.bottom.color = "white",
    column_labels.border.top.width = px(3),
    column_labels.border.bottom.width = px(3),
    row_group.border.right.color = "white",
    row_group.border.left.width = "white"
  )
Median registration delay, lower and upper quartiles, minimum and maximum delay for deaths occurring in England and Wales, March to June 2020
Statistics(days) All causes of death Deaths involving COVID-19
Median registration delay 4 4
Lower quartile 2 2
Upper quartile 6 6
Minimum 0 0
Maximum 123 113
Source: Office for National Statistics – Deaths involving COVID-19

Notes

1. Figures are provisional.
2. Based on deaths occurring in March to June 2020 rather than deaths registered in March to June 2020.
3. Including deaths registered up until 4 July 2020.

4 Gt Table Resources

gt-workshop-2020

gt-function reference

10+ Guidelines for Better Tables in R

Functions and Themes for gt tables

5 Summary

Terrific work creating those tables! This was a long and complex chapter since there is so much formatting that can be done to tables at multiple levels (be they the table itself, at the cell level, header level and so on) so you deserve a much needed break after covering that!

That actually brings us to the end of the Reference Material, proceed to the Summary Chapter 7 for additional links/references as well as information on what the next best steps are.

Reuse

Open Government Licence 3.0