We are really excited about developing the Great Tables package because we believe it’ll make great-looking display tables possible in Python. Though it’s still early days for the project/package, you can do good things with it today! The most recent version of Great Tables is in PyPI. You can install it by using:
pip install great_tables
In this short post, we’ll take a look at a few examples that focus on the more common table-making use cases. We’ll show you how to:
configure the structure of the table
format table-cell values
integrate source notes
add styling to targeted table cells
use features from Polars to make it all better/nicer
Alright! Let’s get right into it.
A Basic Table
Let’s get right to making a display table with Great Tables. The package has quite a few datasets and so we’ll start by making use of the very small, but useful, exibble dataset. After importing the GT class and that dataset, we’ll introduce that Pandas table to GT().
from great_tables import GT, exibble# Create a display table with the `exibble` datasetgt_tbl = GT(exibble)# Now, show the gt tablegt_tbl
num
char
fctr
date
time
datetime
currency
row
group
0.1111
apricot
one
2015-01-15
13:35
2018-01-01 02:22
49.95
row_1
grp_a
2.222
banana
two
2015-02-15
14:40
2018-02-02 14:33
17.95
row_2
grp_a
33.33
coconut
three
2015-03-15
15:45
2018-03-03 03:44
1.39
row_3
grp_a
444.4
durian
four
2015-04-15
16:50
2018-04-04 15:55
65100.0
row_4
grp_a
5550.0
five
2015-05-15
17:55
2018-05-05 04:00
1325.81
row_5
grp_b
fig
six
2015-06-15
2018-06-06 16:11
13.255
row_6
grp_b
777000.0
grapefruit
seven
19:10
2018-07-07 05:22
row_7
grp_b
8880000.0
honeydew
eight
2015-08-15
20:20
0.44
row_8
grp_b
That looks pretty good! Indeed, it is a basic table but we really didn’t really ask for much either. What we did get was an HTML table containing column labels and all of the body cells. You’ll probably be wanting a bit more, so, let’s look at how we can incorporate more table components and perform cell data formatting in the upcoming examples.
More Complex Tables
Let’s take things a bit further and create a table with the included gtcars dataset. Great Tables provides a large selection of methods and they let you refine the table display. They were designed so that you can easily create a really presentable and beautiful table visualization.
For this next table, we’ll incorporate a Stub component and this provides a place for the row labels. Groupings of rows will be generated through categorical values in a particular column (we just have to cite the column name for that to work). We’ll add a table title and subtitle with tab_header(). The numerical values will be formatted with the fmt_integer() and fmt_currency() methods. Column labels will be enhanced via cols_label() and a source note will be included through use of the tab_source_note() method. Here is the table code, followed by the table itself.
from great_tables import GT, md, htmlfrom great_tables.data import gtcarsgtcars_mini = gtcars[["mfr", "model", "year", "hp", "trq", "msrp"]].tail(10)( GT(gtcars_mini, rowname_col="model", groupname_col="mfr") .tab_spanner(label=md("*Performance*"), columns=["hp", "trq"]) .tab_header( title=html("Data listing from <strong>gtcars</strong>"), subtitle=html("A <span style='font-size:12px;'>small selection</span> of great cars."), ) .cols_label(year="Year Produced", hp="HP", trq="Torque", msrp="Price (USD)") .fmt_integer(columns=["year", "hp", "trq"], use_seps=False) .fmt_currency(columns="msrp") .tab_source_note(source_note="Source: the gtcars dataset within the Great Tables package."))
Data listing from gtcars
A small selection of great cars.
Year Produced
Performance
Price (USD)
HP
Torque
Mercedes-Benz
AMG GT
2016
503
479
$129,900.00
SL-Class
2016
329
354
$85,050.00
Tesla
Model S
2017
259
243
$74,500.00
Porsche
718 Boxster
2017
300
280
$56,000.00
718 Cayman
2017
300
280
$53,900.00
911
2016
350
287
$84,300.00
Panamera
2016
310
295
$78,100.00
McLaren
570
2016
570
443
$184,900.00
Rolls-Royce
Dawn
2016
563
575
$335,000.00
Wraith
2016
624
590
$304,350.00
Source: the gtcars dataset within the Great Tables package.
With the six different methods applied, the table looks highly presentable! The rendering you’re seeing here has been done through Quarto (this entire site has been generated with quartodoc). If you haven’t yet tried out Quarto, we highly recommend it!
For this next example we’ll use the airquality dataset (also included in the package; it’s inside the data submodule). With this table, two spanners will be added with the tab_spanner() method. This method is meant to be easy to use, you only need to provide the text for the spanner label and the columns associated with the spanner. We also make it easy to move columns around. You can use cols_move_to_start() (example of that below) and there are also the cols_move_to_end() and cols_move() methods.
from great_tables.data import airqualityairquality_mini = airquality.head(10).assign(Year=1973)( GT(airquality_mini) .tab_header( title="New York Air Quality Measurements", subtitle="Daily measurements in New York City (May 1-10, 1973)", ) .cols_label( Ozone=html("Ozone,<br>ppbV"), Solar_R=html("Solar R.,<br>cal/m<sup>2</sup>"), Wind=html("Wind,<br>mph"), Temp=html("Temp,<br>°F"), ) .tab_spanner(label="Date", columns=["Year", "Month", "Day"]) .tab_spanner(label="Measurement", columns=["Ozone", "Solar.R", "Wind", "Temp"]) .cols_move_to_start(columns=["Year", "Month", "Day"]))
New York Air Quality Measurements
Daily measurements in New York City (May 1-10, 1973)
Date
Measurement
Solar R., cal/m2
Year
Month
Day
Ozone, ppbV
Wind, mph
Temp, °F
1973
5
1
41.0
7.4
67
190.0
1973
5
2
36.0
8.0
72
118.0
1973
5
3
12.0
12.6
74
149.0
1973
5
4
18.0
11.5
62
313.0
1973
5
5
14.3
56
1973
5
6
28.0
14.9
66
1973
5
7
23.0
8.6
65
299.0
1973
5
8
19.0
13.8
59
99.0
1973
5
9
8.0
20.1
61
19.0
1973
5
10
8.6
69
194.0
That table looks really good, and the nice thing about all these methods is that they can be used in virtually any order.
Formatting Table Cells
We didn’t want to skimp on formatting methods for table cells with this early release. There are 12 fmt_*() methods available right now:
fmt_number(): format numeric values
fmt_integer(): format values as integers
fmt_percent(): format values as percentages
fmt_scientific(): format values to scientific notation
fmt_currency(): format values as currencies
fmt_bytes(): format values as bytes
fmt_roman(): format values as Roman numerals
fmt_date(): format values as dates
fmt_time(): format values as times
fmt_datetime(): format values as datetimes
fmt_markdown(): format Markdown text
fmt(): set a column format with a formatting function
We strive to make formatting a simple task but we also want to provide the user a lot of power through advanced options and we ensure that varied combinations of options works well. For example, most of the formatting methods have a locale= argument. We want as many users as possible to be able to format numbers, dates, and times in ways that are familiar to them and are adapted to their own regional specifications. Now let’s take a look at an example of this with a smaller version of the exibble dataset:
We support hundreds of locales, from af to zu! While there are more formatting methods yet to be added, the ones that are available all work exceedingly well.
Using Styles within a Table
We can use the tab_style() method in combination with loc.body() and various style.*() functions to set styles on cells of data within the table body. For example, the table-making code below applies a yellow background color to the targeted cells.
Aside from style.fill() we can also use style.text() and style.borders() to focus the styling on cell text and borders. Here’s an example where we perform several types of styling on targeted cells (the key is to put the style.*() calls in a list).
Column Selection with Polars (and How It Helps with Styling)
Styles can also be specified using Polars expressions. For example, the code below uses the Temp column to set color to "lightyellow" or "lightblue".
import polars as plfrom great_tables import GT, from_column, style, locfrom great_tables.data import airqualityairquality_mini = pl.from_pandas(airquality.head())# A Polars expression defines color based on values in `Temp`fill_color_temp = ( pl.when(pl.col("Temp") >70) .then(pl.lit("lightyellow")) .otherwise(pl.lit("lightblue")))# Pass `fill_color_temp` to the `color=` arg of `style.fill()`( GT(airquality_mini) .tab_style( style=style.fill(color=fill_color_temp), locations=loc.body("Temp") ))
Ozone
Solar_R
Wind
Temp
Month
Day
41.0
190.0
7.4
67
5
1
36.0
118.0
8.0
72
5
2
12.0
149.0
12.6
74
5
3
18.0
313.0
11.5
62
5
4
None
None
14.3
56
5
5
We can deftly mix and match Polars column selectors and expressions. This gives us great flexibility in selecting specific columns and rows. Here’s an example of doing that again with tab_style():
It feels great to use the conveniences offered by Polars and we’re excited about how far we can take this!
Where We’re Going with Great Tables
We’re obviously pretty encouraged about how Great Tables is turning out and so we’ll continue to get useful table-making niceties into the package. We welcome any and all feedback, so get in touch with us: