great-tables
great-tables copied to clipboard
Add a lazy form of GT
(From pairing with @schloerke)
Currently, when applying pieces like formatting (e.g. with GT.fmt_number()), the formatting is not applied right away, but just before rendering. Delaying applying things like formatting is important because it opens the possibility of separating two activities:
- declaring the table output you want (e.g. formatting, styling)
- feeding in data
For example, plotnine doesn't apply anything until it goes to render the plot, so its more like a recipe:
from plotnine import *
from plotnine.data import mtcars
# specify the plot (w/o data)
p = ggplot(aes("cyl", "mpg")) + geom_point()
# feed in data for plot
mtcars >> p
# feed in slightly different data
mtcars.head() >> p
Potential solutions
- Change existing GT: make current methods all behave lazily (still can do some eager validation if user has passed data)
- Wrap existing GT: Use the data decorator pattern to create class that simply records method calls.
Benefits to tools like Shiny
shiny is a dashboarding tool.
Before laziness
from shiny.express import input, render, ui
from great_tables import GT, exibble, style, loc
def create_table(data) -> GT:
return (
GT(data)
.tab_style(style.fill("yellow"), loc.body("num", lambda df: df.num > 10))
)
@render.data_frame
def frame():
data = exibble.head()
# NOTE: this DataGrid(<GT object>) currently unsupported, just imagine it is
return render.DataGrid(create_table(data), editable=True)
Note that shiny DataGrid only sees the eagerly computed GT object (incl styles). However, if an edit is made, shiny wants to recompute that GT "pattern", as if create_table() were called over the edited object. For example, if you updated a row in num to be greater than 10, it should become yellow.
After laziness
In order for shiny to be able to re-apply the changes, it would need a method like GT.reapply(some_data) that would run the lazy GT pattern on some_data.
from shiny.express import input, render, ui
from great_tables import GT, exibble, style, loc
@render.data_frame
def frame():
data = exibble.head()
gt = (
GT(data)
.tab_style(style.fill("yellow"), loc.body("num", lambda df: df.num > 10))
)
# gt now has .reapply method, to rerender on passed in data
return render.DataGrid(gt, editable=True)
Example
The same code should display styles, formatting as if they were computed on the edited data. For example...
- I use
.fmt_number("num", decimals = 1) - A row of num starts as 1.234, but gets rendered as 1.2
- I edit that row from 1.2 to 5.678
- Shiny re-runs the GT pattern, and updates that row value to 5.7
- note that currently shiny prohibits editing the same cell until you see the updated table
- however, editing a cell could produce changes in any part of the table