Three structural components frame the data in your table: the Table Header introduces it with a title and optional subtitle, the Table Footer anchors it with source notes or other supplementary information, and the Stub provides a left-hand column of row identifiers. All three are added using the tab_*() family of methods.
A well-structured frame transforms a bare data grid into something self-explanatory. Without these components, your reader has to look outside the table for context: what is this data? Where did it come from? What do the rows represent? Adding a header, footer, and stub answers those questions inside the table itself, so it can stand on its own in a report, slide deck, or web page.
Adding Source Notes
A source note can be added to the table’s Footer through use of the tab_source_note() method. It works in the same way as tab_header() (it also allows for Markdown inputs) except it can be called multiple times. Each invocation results in the addition of a source note.
# Display the `islands_tbl` data with a heading and two source notes
(
gt_tbl
.tab_source_note(
source_note = "Source: The World Almanac and Book of Facts, 1975, page 406."
)
.tab_source_note(
source_note = md("Reference: McNeil, D. R. (1977) *Interactive Data Analysis*. Wiley.")
)
)
| Large Landmasses of the World 🌐 |
| The top ten largest are presented |
| name |
size |
| Africa |
11506 |
| Antarctica |
5500 |
| Source: The World Almanac and Book of Facts, 1975, page 406. |
| Reference: McNeil, D. R. (1977) Interactive Data Analysis. Wiley. |
Source notes are pretty valuable because they provide provenance and credibility. Academic and business audiences might expect to know where data comes from. Including that information directly in the table saves readers from hunting through surrounding text. Source notes are also a good place for methodological caveats or disclaimers that apply to the entire table.
With just a few method calls, we have added essential context to the table. The title and subtitle tell the reader what data is being presented, and the source notes provide attribution. Together, these components frame the table body and help your audience understand the data at a glance.
The Stub: Row Labels
The Stub is good to have whenever the first column of your data serves as an identifier rather than a measured value. Moving identifiers into the stub visually separates “what this row is about” from “what was measured”, making the table easier to scan. If your rows represent people, places, time periods, or categories, those labels would go nicely in the stub.
The Stub is the area to the left of the table body that typically contains row labels and may also contain row group labels. Those subparts can be grouped in a sequence of row groups. The Stub Head provides a location for a label that describes the Stub (and could also be used to describe the column labels). The Stub is optional since there are cases where it wouldn’t be useful (the display tables presented earlier looked just fine without one).
An easy way to generate a Stub part is by specifying a stub column in the GT() class with the rowname_col= argument. This will signal to Great Tables that the named column should be used as the stub, using the contents of that column to make row labels. Let’s add a stub with our islands dataset by using rowname_col= in the call to GT:
GT(islands_mini).tab_stub(rowname_col="name")
|
size |
| Africa |
11506 |
| Antarctica |
5500 |
| Asia |
16988 |
| Australia |
2968 |
| Axel Heiberg |
16 |
| Baffin |
184 |
| Banks |
23 |
| Borneo |
280 |
| Britain |
84 |
| Celebes |
73 |
Notice that the landmass names are now placed to the left? That’s the Stub. Notably, there is a prominent border to the right of it but there’s no label above the Stub. We can change this and apply what’s known as a stubhead label through use of the tab_stubhead() method:
(
GT(islands_mini)
.tab_stub(rowname_col="name")
.tab_stubhead(label="landmass")
)
| landmass |
size |
| Africa |
11506 |
| Antarctica |
5500 |
| Asia |
16988 |
| Australia |
2968 |
| Axel Heiberg |
16 |
| Baffin |
184 |
| Banks |
23 |
| Borneo |
280 |
| Britain |
84 |
| Celebes |
73 |
A very important thing to note here is that the table now has one column. Before, when there was no Stub, two columns were present (with the Column Labels of "name" and "size") but now column number 1 (the only column remaining) is size.
Row Groups
Grouping is most useful when your data has a natural categorical structure and you want readers to compare within and across categories. Rather than forcing the reader to mentally sort the rows, row groups do that work up front.
Let’s incorporate row groups into the display table. This divides rows into groups, creating row groups, and results in a display of a row group labels right above each group. This can be easily done with a table containing row labels and the key is to use the groupname_col= argument of the GT class. Here we will create three row groups (with row group labels "continent", "country", and "subregion") to have a grouping of rows.
island_groups = islands.head(10).assign(group = ["subregion"] * 2 + ["country"] * 2 + ["continent"] * 6)
(
GT(island_groups)
.tab_stub(rowname_col="name", groupname_col="group")
.tab_stubhead(label="landmass")
)
| landmass |
size |
| subregion |
| Africa |
11506 |
| Antarctica |
5500 |
| country |
| Asia |
16988 |
| Australia |
2968 |
| continent |
| Axel Heiberg |
16 |
| Baffin |
184 |
| Banks |
23 |
| Borneo |
280 |
| Britain |
84 |
| Celebes |
73 |
The table now groups its rows by continent, country, and subregion, with labels appearing above each group. Row groups make it much easier for readers to scan and compare related entries.
GT Convenience Arguments
Rather than using the tab_stub() method, the GT(rowname_col=..., groupname_col=...) arguments provide a quick way to specify row names and groups.
GT(island_groups, rowname_col="name", groupname_col="group")
|
size |
| subregion |
| Africa |
11506 |
| Antarctica |
5500 |
| country |
| Asia |
16988 |
| Australia |
2968 |
| continent |
| Axel Heiberg |
16 |
| Baffin |
184 |
| Banks |
23 |
| Borneo |
280 |
| Britain |
84 |
| Celebes |
73 |
The stub provides a clear organizational framework for your data by separating identifiers from values. Whether you simply need named rows or a fully grouped hierarchy, the combination of rowname_col=, groupname_col=, and tab_stubhead() gives you precise control over how readers navigate your table.