The historical background about tabular displays of quantitative information is very interesting. I imagine it must have been fun think deeply about this problem.<p>Unfortunately, the API design in the example is just not very good:<p><pre><code> (
GT(simple_table, rowname_col='Name')
.tab_header(title='Names, Addresses, and Characteristics of Remote Correspondents')
.tab_stubhead(label=md('*Name*'))
...
)
</code></pre>
I'm uncertain if it's trying to mimic something in another language like R (or some grammar of graphics thing or D3.js.) Hopefully, it's not trying to mimic the look of long, chained `pandas.DataFrame` operations (because it misses the point of why those look the way it does.)<p>Of course, for ad hoc, in-a-notebook, cut-and-paste/written-from-scratch use, the API design doesn't really matter that match. Usually, users will readily memorise the required incantations then fiddle with the result until they get what they want or they give up.<p>It's probably the case that for most tools that produce visual outputs, a majority of users are creating things in this style. (There are, e.g., millions of casual Matplotlib users out there.) But programmatic use is not too far off. Tools that produce visual outputs (even those as formally rigidly at display tables,) are often subject to consistency requirements, which directly implies programmatic use.<p>So, when I discover that my colleagues and I have six tables across three notebooks that need a consistent look, and I decide to interact with this tool programmatically, am I expected to write…?<p><pre><code> def standard_table(source, /, rowname_col, header_title, stubhead_label, weight_columns):
return (
GT(source, rowname_col=rowname_col)
.tab_header(title=header_title)
.tab_stubhead(label=md(f"*{stubhead_label}*"))
.fmt_integer(columns=weight_columns, pattern="{x} lbs")
...
)
standard_table(simple_table, rowname_col='Name', header_title='Names, Addresses, and Characteristics of Remote Correspondents', stubhead_label='Name', weight_columns='Weight')
</code></pre>
Or maybe…?<p><pre><code> def format_table(weight_columns):
return (
tbl
.tab_stubhead(label=md(f"*{tbl.stubhead.label}*")) # what if not present?
.fmt_integer(columns=weight_columns, pattern="{x} lbs")
...
)
format_table(
GT(simple_table, rowname_col='Name')
.tab_header(title='Names, Addresses, and Characteristics of Remote Correspondents')
.tab_stubhead(label='Name')
...
)
</code></pre>
Or maybe…?<p><pre><code> class StandardTable(GT):
def tab_stubhead(self, *a, **kw):
# inspect.signature.bind(...) # ...
return super().tab_stubhead(*a, **kw)
StandardTable(...)
</code></pre>
These aren't great options. The API design is just not very good.