great-tables
great-tables copied to clipboard
feat: implement the `fmt_bar()` function
Hello team,
I found that the bar example is quite inspiring for what we could achieve with Great Tables.
However, it would be great to have a native function like fmt_bar() to handle this directly, rather than manipulating it manually on the dataframe side.
I did a quick experiment, and the implementation seems quite straightforward:
# _formats.py
def create_bar(
prop_fill: float,
max_width: int,
height: int,
background_color1: str,
background_color2: str,
) -> str:
"""Create divs to represent prop_fill as a bar."""
width = round(max_width * prop_fill, 2)
px_width = f"{width}px"
return f"""\
<div style="width: {max_width}px; background-color: {background_color1};">\
<div style="height:{height}px;width:{px_width};background-color:{background_color2};"></div>\
</div>\
"""
def fmt_bar(
self: GTSelf,
columns: SelectExpr = None,
rows: int | list[int] | None = None,
max_width: int = 75,
height: int = 25,
background_color1: str = "lightgray",
background_color2: str = "green",
) -> GTSelf:
pf_format = partial(
fmt_bar_context,
data=self,
max_width=max_width,
height=height,
background_color1=background_color1,
background_color2=background_color2,
)
return fmt_by_context(self, pf_format=pf_format, columns=columns, rows=rows)
def fmt_bar_context(
x: Any,
data: GTData,
max_width: int,
height: int,
background_color1: str,
background_color2: str,
context: str,
) -> str:
x_formatted = create_bar(
x,
max_width=max_width,
height=height,
background_color1=background_color1,
background_color2=background_color2,
)
return x_formatted
That said, I’m also aware that introducing such a function might lead to more incoming requests for custom CSS styling around this.
So I have two thoughts on how we could approach it:
-
We implement a small set of fundamental functions, such as
fmt_bar()and potentially a few other core utilities. More advanced or highly customized functions would be left to third-party packages (we might also need to exposefmt_by_context()). In the future, we could allow third-party functions to be mounted toGTvia aregistermechanism or applied throughGT.pipe(). -
We don't include this feature directly in our project. Instead, we demonstrate the capability through documentation — for example, in the
GT.fmt()section. We could rewrite the progress bar example usingGT.fmt()like this:
import polars as pl
from great_tables import GT
df = (
pl.DataFrame({"n": [0.21, 0.82, 0.72, 0.56, 0.42, 0.77]})
.with_columns(pl.col("n").alias("perc"))
.with_row_index()
)
def create_bar(
prop_fill: float,
max_width: int,
height: int,
background_color1: str,
background_color2: str,
) -> str:
"""Create divs to represent prop_fill as a bar."""
width = round(max_width * prop_fill, 2)
px_width = f"{width}px"
return f"""\
<div style="width: {max_width}px; background-color: {background_color1};">\
<div style="height:{height}px;width:{px_width};background-color:{background_color2};"></div>\
</div>\
"""
(
GT(df)
.fmt(
lambda x: create_bar(x, 75, 20, "lightgray", "green"),
columns="perc",
rows=pl.col("index").mod(2).eq(0),
)
.fmt(
lambda x: create_bar(x, 75, 20, "lightgray", "lightblue"),
columns="perc",
rows=pl.col("index").mod(2).eq(0).not_(),
)
)
If the team aligns with either of these options, I would be happy to contribute a PR for it!