great_tables
  • Get Started
  • Examples
  • Reference
  • Blog

On this page

  • Nanoplots, small interactive plots in your table
  • Adding reference lines and highlighted areas
  • Simple bars
  • Customizing with nanoplot_options()
  • Wrapping up

Great Tables v0.4.0: Nanoplots and More

Author

Rich Iannone

Published

March 19, 2024

The recent v0.4.0 release of Great Tables contains nanoplots as a major new feature. So, in this post I’ll concentrate on showing you all the things you can do with nanoplots. What are nanoplots? Well, with nanoplots you can do this:

Show the code
from great_tables import GT, md
from great_tables.data import illness
import polars as pl

illness_mini = (
    pl.from_pandas(illness)
    .head(10)
    .select(
        "test", values=pl.concat_str(pl.exclude("test", "units"), separator=" ", ignore_nulls=True)
    )
    .slice(1, 9)
)

(
    GT(illness_mini, rowname_col="test")
    .fmt_nanoplot(columns="values")
    .tab_header(md("Partial summary of daily tests<br>performed on YF patient"))
    .tab_stubhead(label=md("**Test**"))
    .cols_label(values=md("*Progression*"))
    .cols_align(align="center", columns="values")
    .tab_source_note(source_note="Measurements from Day 3 through to Day 8.")
)
Partial summary of daily tests
performed on YF patient
Test Progression
WBC
30.34.005.264.269.9210.524.830.319.04.0010.0
Neutrophils
27.22.004.874.727.9218.222.127.216.62.008.00
RBC
5.982.685.725.984.234.834.122.683.324.005.50
Hb
16075153135126115758795120160
PLT
30025.667.038.627.426.274.136.225.6100300
ALT
12.8K9.0012.8K12.6K6.43K4.26K1.62K6735129.0050.0
AST
23.7K15.023.7K21.4K14.7K8.69K2.19K1.14K78215.040.0
TBIL
1630117144137158127105163018.8
DBIL
144071.410594.614411883.612606.80
Measurements from Day 3 through to Day 8.

While the emphasis here will be on the aforementioned nanoplots feature, the last two releases (v0.3.1 and v0.4.0) overall gave us a nice collection of improvements which includes:

  • the fmt_nanoplot() method for adding nanoplots to your table
  • improved HTML table representations in different code environments
  • integration of Polars selectors in the columns= arg of all formatting (fmt_*()) methods
  • the save() method for saving a GT table as an image file
  • rendering a GT table as an HTML string though as_raw_html()

Now let’s dive into the wonderful world of nanoplots!

Nanoplots, small interactive plots in your table

Nanoplots are small yet information-laden plots that fit nicely into table cells. They are interactive, allowing for more information to be shown on hovering (or through touch when that interaction is available). Nanoplots try to show individual data points with reasonably good visibility (space is limited, this is going in a table after all!) and the plot representations change depending on the data fed into them.

We can generate nanoplots via the fmt_nanoplot() method. Let’s make two nanoplots of the two different available plot types: "line" and "bar":

random_numbers_df = pl.DataFrame(
    {
        "i": range(1, 5),
        "lines": [
            "20 23 6 7 37 23 21 4 7 16",
            "2.3 6.8 9.2 2.42 3.5 12.1 5.3 3.6 7.2 3.74",
            "-12 -5 6 3.7 0 8 -7.4",
            "2 0 15 7 8 10 1 24 17 13 6",
        ],
    }
).with_columns(bars=pl.col("lines"))

(
    GT(random_numbers_df, rowname_col="i")
    .fmt_nanoplot(columns="lines", plot_type="line")
    .fmt_nanoplot(columns="bars", plot_type="bar")
)
lines bars
1
3742023673723214716
3702023673723214716
2
12.12.302.306.809.202.423.5012.15.303.607.203.74
12.102.306.809.202.423.5012.15.303.607.203.74
3
8.00−12.0−12.0−5.006.003.7008.00−7.40
8.00−12.0−12.0−5.006.003.7008.00−7.40
4
2402015781012417136
2402015781012417136

As can be seen, the method accepts bundles of values per cell that are formatted as strings (with spaces between each of the values). You can also use Polars list columns as acceptable input.

Adding reference lines and highlighted areas

It’s possible to add in a reference line and a reference area to individual plots. These may be useful to highlight a particular statistic (e.g., median or minimum value) or a bounded region of interest (e.g., the area between the first and third quartiles). Here is an example of how to use these options via the reference_line= and reference_area= arguments:

(
    GT(random_numbers_df, rowname_col="i")
    .fmt_nanoplot(
        columns="lines",
        reference_line="mean",
        reference_area=["min", "q1"]
    )
    .fmt_nanoplot(
        columns="bars",
        plot_type="bar",
        reference_line="max",
        reference_area=["max", "median"])
)
lines bars
1
16.43742023673723214716
37.03742023673723214716
2
5.6212.12.302.306.809.202.423.5012.15.303.607.203.74
12.112.12.302.306.809.202.423.5012.15.303.607.203.74
3
−0.968.00−12.0−12.0−5.006.003.7008.00−7.40
8.008.00−12.0−12.0−5.006.003.7008.00−7.40
4
9.362402015781012417136
24.02402015781012417136

Having a reference line and/or area can be valuable in different situations. We make it easy by allowing you to mix-and-match numeric values and a set of keywords (these are: "mean", "median", "min", "max", "q1", "q3", "first", or "last").

Simple bars

We can also have single-value bar plots and line plots. These will run in the horizontal direction and such plots are meant for easy value comparisons (which works great in tables). To make this work, give fmt_nanoplot() a column of numeric values. The following example shows how fmt_nanoplot() can be used to create single-value bar and line plots.

single_vals_df = pl.DataFrame(
    {
        "i": range(1, 6),
        "bars": [4.1, 1.3, -5.3, 0, 8.2],
        "lines": [12.44, 6.34, 5.2, -8.2, 9.23]
    }
)

(
    GT(single_vals_df, rowname_col="i")
    .fmt_nanoplot(columns="bars", plot_type="bar")
    .fmt_nanoplot(columns="lines", plot_type="line")
)
bars lines
1
4.10
12.4
2
1.30
6.34
3
−5.30
5.20
4
0
−8.20
5
8.20
9.23

Notice that there is some light interactivity available here as well! When hovering over a plotted bar or line the data value will appear.

Customizing with nanoplot_options()

We provide a lot of options for customizing your nanoplots. With the nanoplot_options() helper function, it’s possible to change the look and feel for a set of nanoplots. The options= argument of fmt_nanoplot() is where you’d need to invoke that helper function. Some possibilities for customization include determining which nanoplot elements are present, changing the sizes and colors of different elements, and a whole lot more! Here’s an example where both line- and bar-based nanoplots retain their basic compositional elements, but their appearance is quite different.

from great_tables import nanoplot_options

(
    GT(random_numbers_df)
    .fmt_nanoplot(
        columns="lines",
        options=nanoplot_options(
            data_point_radius=8,
            data_point_stroke_color="black",
            data_point_stroke_width=3,
            data_point_fill_color="white",
            data_line_type="straight",
            data_line_stroke_color="green",
            data_line_stroke_width=5,
            data_area_fill_color="green",
            show_data_area=False,
        ),
    )
    .fmt_nanoplot(
        columns="bars",
        plot_type="bar",
        options=nanoplot_options(
            data_bar_stroke_color="brown",
            data_bar_fill_color="yellow",
            data_bar_negative_stroke_color="black",
            data_bar_negative_fill_color="blue",
        ),
    )
)
i lines bars
1
3742023673723214716
3702023673723214716
2
12.12.302.306.809.202.423.5012.15.303.607.203.74
12.102.306.809.202.423.5012.15.303.607.203.74
3
8.00−12.0−12.0−5.006.003.7008.00−7.40
8.00−12.0−12.0−5.006.003.7008.00−7.40
4
2402015781012417136
2402015781012417136

We want you to have a lot of creative control for how these tiny plots are displayed. So, when you need it, nanoplot_options() is there for you!

Wrapping up

We’re always excited to offer new and exciting features that make summary tables fun and useful. The new nanoplots functionality is something we will continue to iterate on since there is definitely room for plotting innovation in tables for display. And there’s a lot more to nanoplots than these examples can show. For much more information on this, check out the Get Started guide on nanoplots. Please let us know through GitHub Issues whether you ran into problems with this (or any other) feature, or, if you have suggestions for improvement!