Foundations of
Data Science

Spring 2026 | Data 2 (399)
Jeffrey M. Girard | Lecture 06b

Roadmap

  • Understand the structural anatomy of a gt table
  • Build a table step-by-step (Headers, Stubs, and Spanners)
  • Format and style data for clear communication

Table Anatomy

The gt Package

  • gt stands for the “Grammar of Tables”
    • It provides an intuitive pipeline for building display tables
  • Works perfectly with the tidyverse and pipes (|>)
  • Helps transition from exploring data to communicating results

Parts of a Table

Building Blocks

  • Prep with dplyr: Filter, select, and arrange data
  • Initialize gt(): Define stubs (rowname_col) and groups (groupname_col).
  • Build Structure:
    • tab_header() for titles
    • cols_label() for clean names
    • tab_spanner() for column groups
    • tab_footnote() for definitions
    • tab_source_note() for data source

Step 1: The Base Table

# Filter aggressively for a compact table
my_cars <- gtcars |> 
  filter(
    mfr == "Mercedes-Benz" | mfr == "Rolls-Royce"
  ) |> 
  select(mfr, model, hp, trq, year, bdy_style, msrp) |> 
  arrange(mfr, model)

# Create the base table
table1 <- 
  my_cars |> 
  gt(
    groupname_col = "mfr",
    rowname_col = "model"
  )
table1

Step 1: The Base Table

hp trq year bdy_style msrp
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350

Step 2: The Header

# Adding a title and subtitle
table2 <- 
  table1 |> 
  tab_header(
    title = md("**Modern High-Performance Cars**"),
    subtitle = "Comparing Mercedes-Benz and Rolls-Royce"
  )
table2

Step 2: The Header

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
hp trq year bdy_style msrp
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350

Step 3: Column Labels

# Renaming columns
table3 <- 
  table2 |> 
  cols_label(
    hp = "Horsepower",
    trq = "Torque",
    year = "Year",
    bdy_style = "Style",
    msrp = "MSRP"
  )
table3

Step 3: Column Labels

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Horsepower Torque Year Style MSRP
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350

Step 4: Spanners

# Grouping columns with spanners
table4 <- 
  table3 |> 
  tab_spanner(
    label = "Performance",
    columns = c(hp, trq)
  ) |> 
  tab_spanner(
    label = "Configuration",
    columns = c(year, bdy_style)
  )
table4

Step 4: Spanners

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350

Step 5: The Footer

# Adding context to the bottom of the table
table5 <- 
  table4 |> 
  tab_source_note(
    source_note = md("Data from the *gtcars* dataset (v0.2.0.5).")
  ) |> 
  tab_footnote(
    footnote = "Manufacturer's suggested retail price.",
    locations = cells_column_labels(
      columns = msrp
    )
  )
table5

Step 5: The Footer

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP1
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350
1 Manufacturer's suggested retail price.
Data from the gtcars dataset (v0.2.0.5).

Table Styling

Formatting and Styling

  • Formatting only alters appearance
  • Does not alter underlying values
  • Data Values: fmt_number() (decimals) and fmt_currency() (money)
  • Alignment: cols_align() adjusts text justification
  • Aesthetics: tab_style() applies visuals
    • Location helpers (e.g., cells_row_groups()) target specific cells

Step 6: Column Alignment

# Aligning the data columns to the center
table6 <- 
  table5 |> 
  cols_align(
    align = "center",
    columns = c(hp, trq, year, bdy_style)
  )
table6

Step 6: Column Alignment

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP1
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe 129900
SL-Class 329 354 2016 convertible 85050
Rolls-Royce
Dawn 563 575 2016 convertible 335000
Wraith 624 590 2016 coupe 304350
1 Manufacturer's suggested retail price.
Data from the gtcars dataset (v0.2.0.5).

Step 7: Formatting Data

# Formatting the MSRP column as currency
table7 <- 
  table6 |> 
  fmt_currency(
    columns = msrp,
    decimals = 0
  )
table7

Step 7: Formatting Data

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP1
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe $129,900
SL-Class 329 354 2016 convertible $85,050
Rolls-Royce
Dawn 563 575 2016 convertible $335,000
Wraith 624 590 2016 coupe $304,350
1 Manufacturer's suggested retail price.
Data from the gtcars dataset (v0.2.0.5).

Step 8: Styling Elements

# Making the manufacturer group labels bold and colored
table8 <- 
  table7 |> 
  tab_style(
    style = list(
      cell_text(weight = "bold"),
      cell_fill(color = "lightgray")
    ),
    locations = cells_row_groups()
  )
table8

Step 8: Styling Elements

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP1
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe $129,900
SL-Class 329 354 2016 convertible $85,050
Rolls-Royce
Dawn 563 575 2016 convertible $335,000
Wraith 624 590 2016 coupe $304,350
1 Manufacturer's suggested retail price.
Data from the gtcars dataset (v0.2.0.5).

Putting It All Together

gtcars |> 
  filter(
    mfr == "Mercedes-Benz" | mfr == "Rolls-Royce"
  ) |> 
  select(mfr, model, hp, trq, year, bdy_style, msrp) |> 
  arrange(mfr, model) |> 
  gt(
    groupname_col = "mfr",
    rowname_col = "model"
  ) |> 
  tab_header(
    title = md("**Modern High-Performance Cars**"),
    subtitle = "Comparing Mercedes-Benz and Rolls-Royce"
  ) |> 
  cols_label(
    hp = "Horsepower",
    trq = "Torque",
    year = "Year",
    bdy_style = "Style",
    msrp = "MSRP"
  ) |> 
  tab_spanner(
    label = "Performance",
    columns = c(hp, trq)
  ) |> 
  tab_spanner(
    label = "Configuration",
    columns = c(year, bdy_style)
  ) |> 
  tab_source_note(
    source_note = md("Data from the *gtcars* dataset (v0.2.0.5).")
  ) |> 
  tab_footnote(
    footnote = "Manufacturer's suggested retail price.",
    locations = cells_column_labels(
      columns = msrp
    )
  ) |> 
  cols_align(
    align = "center",
    columns = c(hp, trq, year, bdy_style)
  ) |> 
  fmt_currency(
    columns = msrp,
    decimals = 0
  ) |> 
  tab_style(
    style = list(
      cell_text(weight = "bold"),
      cell_fill(color = "lightgray")
    ),
    locations = cells_row_groups()
  )

Putting It All Together

Modern High-Performance Cars
Comparing Mercedes-Benz and Rolls-Royce
Performance
Configuration
MSRP1
Horsepower Torque Year Style
Mercedes-Benz
AMG GT 503 479 2016 coupe $129,900
SL-Class 329 354 2016 convertible $85,050
Rolls-Royce
Dawn 563 575 2016 convertible $335,000
Wraith 624 590 2016 coupe $304,350
1 Manufacturer's suggested retail price.
Data from the gtcars dataset (v0.2.0.5).