posit-dev / great-tables

Make awesome display tables using Python.
https://posit-dev.github.io/great-tables/
MIT License
1.42k stars 48 forks source link

Implement `GT.pipe()` and `GT.pipes()` #363

Open jrycw opened 1 month ago

jrycw commented 1 month ago

Related to #353.

After reviewing the pipe() methods from Pandas and Polars, I propose adding GT.pipe() and GT.pipes() in this PR.

GT.pipe()

GT.pipe() will function similarly to Pandas and Polars, receiving a function along with optional positional and keyword arguments. An example is provided below:

import polars as pl
from great_tables import GT, loc, style
from great_tables.data import towny

towny_mini = pl.from_pandas(towny).head(10)
columns = ["land_area_km2", "density_2021"]
colors = ["lightgray", "lightblue"]

def tbl_style(gtbl: GT, columns: list[str], colors: list[str]) -> GT:
    for column, color in zip(columns, colors):
        gtbl = gtbl.tab_style(
            style=style.fill(color=color),
            locations=loc.body(columns=column, rows=pl.col(column).eq(pl.col(column).max())),
        )
    return gtbl

(
    GT(
        towny_mini[["name", "land_area_km2", "density_2021"]],
        rowname_col="name",
    ).pipe(tbl_style, columns, colors)
)

GT.pipes()

GT.pipe() serves as a convenient helper method for chaining multiple pre-built functions (or using partial). An example is provided below:

from functools import partial
import polars as pl
from great_tables import GT, loc, style
from great_tables.data import towny

towny_mini = pl.from_pandas(towny).head(10)
columns = ["land_area_km2", "density_2021"]
colors = ["lightgray", "lightblue"]

def tbl_style(gtbl: GT, column: str, color: str) -> GT:
    return gtbl.tab_style(
        style=style.fill(color=color),
        locations=loc.body(columns=column, rows=pl.col(column).eq(pl.col(column).max())),
    )

(
    GT(
        towny_mini[["name", "land_area_km2", "density_2021"]],
        rowname_col="name",
    ).pipes(
        *[partial(tbl_style, column=column, color=color) for column, color in zip(columns, colors)]
    )
)

Table

Both methods yield the table as follows: image

Assistance Required

I hope the API design makes sense, and could the team please review my docs to ensure they render correctly? I'm relatively new to quarto.

codecov-commenter commented 1 month ago

Codecov Report

Attention: Patch coverage is 87.50000% with 1 lines in your changes are missing coverage. Please review.

Project coverage is 86.07%. Comparing base (e3649d2) to head (7e4a167). Report is 12 commits behind head on main.

:exclamation: Current head 7e4a167 differs from pull request most recent head 3d7152d

Please upload reports for the commit 3d7152d to get more accurate results.

Files Patch % Lines
great_tables/_pipe.py 83.33% 1 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #363 +/- ## ========================================== + Coverage 85.90% 86.07% +0.17% ========================================== Files 41 42 +1 Lines 4328 4331 +3 ========================================== + Hits 3718 3728 +10 + Misses 610 603 -7 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

jrycw commented 1 month ago

I'm not sure if it's a good idea to support passing a list into pipes(). If we add two lines to pipes(), like:

def pipes(self: "GT", *funcs: Callable["GT", "GT"] | list[Callable["GT", "GT"]]) -> "GT":
    if isinstance(funcs[0], list) and len(funcs) == 1:  # edited
        funcs = funcs[0]
    for func in funcs:
        self = pipe(self, func)
    return self

Then, we can do:

(
    GT(
        towny_mini[["name", "land_area_km2", "density_2021"]],
        rowname_col="name",
    ).pipes(
        [partial(tbl_style, column=column, color=color) for column, color in zip(columns, colors)]
    )
)

@machow and @rich-iannone , could you share your thoughts on this idea with me?

jrycw commented 1 month ago

I've updated .pipes() to accept a list of functions in the latest commit. We can decide which commit to use later.

jrycw commented 1 month ago

@machow thanks for introducing me to toolz. It's new to me, and it looks like it has a lot of great features. Sure, let's remove the .pipes() method. I've reviewed the preview docs and made some adjustments. Other than that, it looks good to me. What do you think?