posit-dev / great-tables Goto Github PK
View Code? Open in Web Editor NEWEasily generate information-rich, publication-quality tables from Python
Home Page: https://posit-dev.github.io/great-tables/
License: MIT License
Easily generate information-rich, publication-quality tables from Python
Home Page: https://posit-dev.github.io/great-tables/
License: MIT License
I've run example on README.md and nothing happens. Is the table shown on screen or saved on some file?
Let's double check everywhere that html and md could go is rendered correctly (and add tests).
We tested one case by snapshot testing create_body_component_h
. That could be a good way to test the other places html() and md() could be used.
Make md()
work with tab_header()
. Example:
import gt
from gt import exibble, countrypops, gtcars, md, html
from siuba import *
res = gtcars >> select(_.mfr, _.model, _.msrp) >> _.head(5)
gt.GT(res).tab_header(
title=gt.md("Data listing from **gtcars**"), subtitle=gt.md("`gtcars` is an R dataset")
)
Similar to #13, we need to make the html()
work with tab_header()
(and other functions where new labels are accepted, eventually). Example:
import gt
from gt import exibble, countrypops, gtcars, md, html
from siuba import *
res = gtcars >> select(_.mfr, _.model, _.msrp) >> _.head(5)
gt.GT(res).tab_header(
title=html("Data listing from <strong>gtcars</strong>"),
subtitle=html("From <span style='color:red;'>gtcars</span>"),
)
It looks like from this failed docs build that the only call to GT.cols_align()
might be in a docstring. Open to track us adding tests of .cols_align()
.
@gshotwell mentioned it could be cool to create a shiny template for reporting clinical trials. Opening this issue to track creating some docs around this kind of thing.
Let's try writing some docs in this order:
Are there any good examples of clinical reports, etc.. written for R gt?!
Right now, we leave Pandas' read_csv()
to its own devices but we should really specify the column types.
From this page: https://posit-dev.github.io/great-tables/reference/GT.cols_move.html
import great_tables as gt
countrypops_mini = gt.countrypops.loc[gt.countrypops["country_name"] == "Japan"][
["country_name", "year", "population"]
].tail(5)
(
gt.GT(countrypops_mini)
.cols_move(
columns="population",
after="country_name"
)
)
Example on website has commented code
Navigate to examples page. Observe the code for Populations of Oceania's Countries
If commented code is not relevant to the example, it should be removed.
Can I render add latex to headers and notes within an md() context?
Since we can use polars selectors, like polars.selectors.all()
, we need to remove stub variables from selection. Otherwise, we will erroneously try to include them in things like spanners.
quartodoc is able to cross reference mentions of a function to its API documentation. It calls this functionality interlinks. Let's add interlinks to the docs, so we can get nice links between things!
https://machow.github.io/quartodoc/get-started/interlinks.html
Edit: polars support is complete! This issue now tracks only the removing of pandas as a mandatory dependency. This is largely a code cleanup activity, since the machinery to make pandas optional is already in great tables.
This may take a bit of work, but using the approach in https://github.com/machow/databackend, we should be able to make pandas and polars optional dependencies.
There is a data decorator pattern used in _tbl_data.py
that wraps a dataframe to provide standard methods (e.g. for getting column names). We should be able to use generic functions, so gt can call things like get_column_names
directly on any DataFrame.
Related to #34, I've implemented the path for using polars selectors, but need to add more tests of its usage. It seems like aspects of selection can be consolidated in places (e.g. the handling of columns
in the cols_*
functions).
Most functions that take a selection argument do a bit of custom checking, and these checks seem largely the same across functions..!
Let's implement tab_options()
down the road, and for now remove from the reference, so it doesn't trip people up (There is some quartodoc work needed to get it displaying well in the docs).
We could use the None
default to mean all and allow []
to mean no selection for a particular axis. If both are []
we should no-op any operation that relies on location targeting.
The following parts of the API need to be available for the public release:
Create Table
GT
Create or Modify Parts
tab_header()
tab_stubhead()
tab_spanner()
tab_source_note()
tab_options()
Format Data
fmt_number()
fmt_integer()
fmt_scientific()
fmt_percent()
fmt_currency()
fmt_bytes()
fmt_roman()
fmt_markdown()
fmt()
Modify Columns
cols_align()
cols_label()
cols_move_to_start()
cols_move_to_end()
cols_move()
Helper Functions
md()
html()
px()
pct()
Table Option Functions
opt_footnote_marks()
opt_row_striping()
opt_align_table_header()
opt_all_caps()
Datasets
Export Functions
as_raw_html()
When the table has row groups, rows tend to lose their original ordering due to migration of rows to the different groups. This makes using a row index value in a rows
argument less recommended (you have to know the pre-shuffled index). Using the row ID is the recommended way, however, this fails and it seems that the row ID maps to the wrong row. Here is an example of that using tab_style()
:
from great_tables import GT, md, style, loc
from great_tables.data import towny
towny_mini = towny[["name", "website", "land_area_km2", "csd_type"]].tail(4)
(
GT(towny_mini, rowname_col="name", groupname_col="csd_type")
.tab_style(
style=style.fill(color="Beige"),
locations=loc.body(columns="land_area_km2", rows=["Woodstock"]),
)
)
Currently, great tables includes over a dozen datasets in its .data
submodule:
# all datasets live in submodule
from great_tables.data import airquality, exibble, towny
# the exibble dataset can be fetched from the top-level
# which allows us to quickly churn out examples
from great_tables import exibble
However, these datasets are pandas DataFrames, so polars users need to convert them:
import polars as pl
pl.from_dataframe(exibble)
This isn't too bad. But maybe it could be better? This issue will discuss various ways we could approach loading data for both pandas and polars users.
This is mostly me thinking out loud about different options, without a strong opinion on an approach yet ๐ .
pl.from_pandas()
to convert.set_options(data_frame = pl.DataFrame)
.set_options(data_frame="polars")
.exibble(pl.DataFrame)
, orexibble("polars")
, orexibble() # uses set_options() to get DataFrame
from great_tables.data.polars import airquality
, ORfrom great_tables.data_pl import airquality
, ORfrom some_data_package import airquality
For example, if data is simply imported and set_options()
is used, then people will need to do some code in-between imports. This feels a cludgy.
Here's an example:
from great_tables import set_options
set_options(data_frame = "polars")
from great_tables.data import airquality
At the same time, calling functions gets kind of annoying:
from great_tables import GT, set_options
from great_tables.data import airquality
set_options(data_frame = "polars")
# annoying to call, but imports can be up top
# you can change the option and call airquality again
# to get data based on the current data_frame option
df_airquality = airquality()
# maybe the workaround is calling inside GT
GT(airquality())
I'm not sure how to implement something like set_options()
and a data fetcher like airquality()
? Is there a way to type it, so tools like pyright know that when an option is set to a specific value, that airquality() returns a specific type of DataFrame?
from great_tables import set_options
from great_tabels.data import airquality
set_options(data_frame = "polars")
airquality.<tab> # shows polars methods
I like the idea of us having a great_tables.data_pl
submodule, or even a separate data package for datasets, but am curious what seems most useful to folks!
tab_source_note
disappears when text is rendered as markdown
using
.tab_source_note(
source_note = "Source: The World Almanac and Book of Facts, 1975, page 406."
)
renders the text correctly
but adding
.tab_source_note(
source_note = md("Reference: McNeil, D. R. (1977) *Interactive Data Analysis*. Wiley.")
)
Does not render the markdown and hides the text from the footnotes section
from great_tables import GT, md, html
from great_tables.data import islands
islands_mini = islands.head(10)
(
GT(islands_mini, rowname_col = "name")
.tab_header(
title = "Large Landmasses of the World",
subtitle = "The top ten largest are presented"
)
.tab_source_note(
source_note = "Source: The World Almanac and Book of Facts, 1975, page 406."
)
.tab_source_note(
source_note = md("Reference: McNeil, D. R. (1977) *Interactive Data Analysis*. Wiley.")
)
. tab_stubhead(label = "landmass")
)
Should render footnote with markdown when used in conjunction with the md
tag
This code is also displayed on the examples page
Automatic alignment of columns should occur during data ingest. This is made optional by the auto_align
argument in the GT class (set to True by default).
Remove support for 3.7 (very old, not tested) and consider which recent versions to include). Look at https://numpy.org/neps/nep-0029-deprecation_policy.html for guidance on this.
Requires set up of CodeCov through a separate workflow file.
Adding a hyperlink to a table is a frequent need. This is possible with great tables
. Suggest adding an example to the documentation to illustrate.
Row groups can be defined in the GT class by setting a value on the "group_col"
argument. Currently, it can be registered within the relevant object but the renderer responsible for body rows doesnโt have this implementation.
Adding any code after the great-tables code in Quarto fails to render the great-tables table
use the following code:
from great_tables import GT
from great_tables.data import countrypops
import polars as pl
import polars.selectors as cs
# Get vectors of 2-letter country codes for each region of Oceania
countries = {
"Australasia": ["AU", "NZ"],
"Melanesia": ["NC", "PG", "SB", "VU"],
"Micronesia": ["FM", "GU", "KI", "MH", "MP", "NR", "PW"],
"Polynesia": ["PF", "WS", "TO", "TV"],
}
# a dictionary mapping region to country (e.g. AU -> Australasia)
region_to_country = {
region: country for country, regions in countries.items() for region in regions
}
keep_rows = countrypops.country_code_2.isin(list(region_to_country)) & countrypops.year.isin(
[2000, 2010, 2020]
)
# Create a gt table based on a preprocessed `countrypops`
wide_pops = (
pl.from_pandas(countrypops)
.filter(
pl.col("country_code_2").is_in(list(region_to_country))
& pl.col("year").is_in([2000, 2010, 2020])
)
.with_columns(pl.col("country_code_2").replace(region_to_country).alias("region"))
.pivot(index=["country_name", "region"], columns="year", values="population")
.sort("2020", descending=True)
)
(
GT(wide_pops, rowname_col="country_name", groupname_col="region")
.tab_header(title="Populations of Oceania's Countries in 2000, 2010, and 2020")
.tab_spanner(label="Total Population", columns=cs.all())
.fmt_integer()
)
print("extra lines")
Should show the table followed by the print statement
Currently,
the data from gt is loaded whenever gt is imported.
We should be able to get lazy looking using one of these approaches
__getattr__
on the init modulegt/data/__init__.py
import pandas as pd
__all__ = (
"countrypops",
"sza",
)
countrypops: pd.DataFrame
def __getattr__(k):
# lazy loading code goes here
if k == "countrypops":
print("Loading countrypops")
...
...
The one downside is that if we gt/__init__.py
imports from gt.data
, then this will import the data. In this case we'd also need to add __getattr__
handling in gt/__init__.py
, which is starting to feel a bit much.
(In this case, we could also require people always import from gt.data
, rather than top-level gt
itself.)
Another common approach is that the data module contains functions, that when called load the data.
E.g. it would look like...
from gt.data import load_exibble
exibble = load_exibble()
This is commonly used to delay downloading the data.
Hello there ๐
I was trying to use great_tables
in conjunction with shiny
, and ended up figuring out a combination that works, something like ui.HTML(gt.GT(df)._repr_html())
. However, it would be awesome if Shiny was a little smarter about handling GT objects.
Here's a reprex, using great-tables==0.1.0 and shiny==0.6.0.
from shiny import App, render, ui
import great_tables as gt
app_ui = ui.page_fluid(
ui.output_ui("txt"),
)
def server(input, output, session):
@output
@render.ui
def txt():
return ui.HTML(gt.GT(gt.exibble)._repr_html_())
app = App(app_ui, server)
Does not render table markdown correctly when columns are formatted with GT.fmt_markdown
import pandas as pd
from great_tables import GT, md
from faker import Faker
import random
fake = Faker()
markdown_syntax = [
"# Heading 1",
"## Heading 2",
"### Heading 3",
"This is *italic* text.",
"This is **bold** text.",
"A [Click me](https://posit-dev.github.io/great-tables/articles/intro.html).",
"- List item 1\n- List item 2\n- List item 3",
"1. Ordered item 1\n2. Ordered item 2\n3. Ordered item 3",
"```python\nprint('Hello, World!')\n```",
"""
| Column 1 | Column2 |
| ----------- | ----------- |
| Row 1 Column 1 | Row 1 Column 2 |
| Row 2 Column 1 | Row 2 Column 2 |
""",
"๐
",
"I need to highlight these ==very important words==.",
"H~2~O",
"X^2^",
"- [x] Write the press release",
"![](https://www.google.com/logos/doodles/2023/seasonal-holidays-2023-6753651837110165.2-6752733080612634-cst.gif)",
"![](https://i.giphy.com/3oEjI4sFlp73fvEYgw.webp)",
"![](https://www.socialpilot.co/wp-content/uploads/2023/02/gif.gif)",
"""
> Blockquotes can be nested. Add a >> in front of the paragraph you want to nest.
>
>> Blockquotes can be nested. Add a >> in front of the paragraph you want to nest.
""",
"""
1. First item
2. Second item
3. Third item
1. Indented item
2. Indented item
4. Fourth item
""",
]
df = pd.DataFrame({'rendered markdown': markdown_syntax})
df['unrendered markdown'] = df['rendered markdown']
df['name'] = [fake.name() for _ in range(len(df))]
df['email'] = [fake.email() for _ in range(len(df))]
df['age'] = [fake.random_int(18, 99) for _ in range(len(df))]
df['is student'] = [fake.boolean() for _ in range(len(df))]
df['grade'] = [random.choice(['A', 'B', 'C', 'D', 'F']) for _ in range(len(df))]
df['gpa'] = [random.uniform(2.0, 4.0) for _ in range(len(df))]
df['date joined'] = [fake.date_this_decade() for _ in range(len(df))]
df_sorted_desc = df.sort_values(by='grade', ascending=True)
(
GT(df_sorted_desc,rowname_col="name", groupname_col="grade")
.fmt_markdown(columns="rendered markdown")
.fmt_date(columns="date joined", date_style="m_day_year")
.tab_spanner(
label=md("<b><i>Personal Information</b></i>"),
columns=["name", "email", "age", "grade", "gpa"],
)
.fmt_number(columns=["gpa"], decimals=2)
.tab_stubhead(label="Students grouped by their grades")
)
Observe the cell where table markdown is rendered
Should render table markdown as a table.
I'll look into it..!
from great_tables import GT
from great_tables.data import exibble
gt_ex = GT(exibble)
# works
gt_ex.fmt_number(columns="num", decimals=1, scale_by=1/1000, pattern="{x}K")
# errors because num is now strings
gt_ex.fmt_number(columns="num", decimals=1, scale_by=1/1000, pattern="{x}K")
When there is a table stub (using rowname_col
), the spanner label is set improperly. And the column labels are also affected. Code to reproduce:
import great_tables as gt
gtcars_mini = gt.gtcars[["model", "year", "hp", "trq"]].head(5)
gt.GT(gtcars_mini, rowname_col="model").tab_spanner(label=gt.md("*spanner*"), columns=["hp", "trq"])
Currently, we use setup.py, setup.cfg, and setup.toml. Let's switch to only using the last one, and move cfg stuff in there.
For any cells containing URLs, we should have a fmt_url()
method to make them navigable links. We should be careful to employ the method only on column containing URL text (though it is probably the user's responsibility to ensure this and that there are, for example, no URLs as part of a larger block of text).
We could expose options for how the links should be styled. They can be of the conventional style (with underlines and text coloring that sets it apart from other text), or, they can appear to be button-like (with a surrounding box that can be filled with a color).
URLs in data cells could be detected in two ways:
[label](URL)
For the second case the URL is also used as the label but there is the option to use the label
argument to modify that text.
The py-shiny docs use a tool called shinylive to run examples in the browser, via WASM. Let's use this issue to track what might be needed to use great_tables in shinylive.
The biggest challenge AFAICT is that great_tables depends on libsass-python, python bindings wrapping a C++ library.
I have no idea how any of this works. This is me supplicating WASM wizards with my bad ideas:
Currently the stub column can be defined in the GT class. However, itโs not yet visible in the output HTML table. The renderer that handles body rows and column labels needs to be revised to implement this table location.
A relatively simple table rendering yields a TypeError. Here's code that results in the error:
from great_tables import GT, md
from great_tables.data import constants
constants_mini = constants[["name", "value", "uncert", "sf_value", "sf_uncert", "units"]].tail(6)
GT(constants_mini, rowname_col="name", groupname_col="sf_value")
Results in the error:
TypeError: can only concatenate str (not "numpy.int64") to str
E.g. tab_spanner("name", ["some_nonmatching_name"])
From @rich-iannone , there are a number of places where authors are mentioned, let's have the both of us there
It would be fantastic if there was an option to export tables to LaTeX as this would unlock a wide range of further uses in presentations, papers, and reports. For prior art, the original gt package in R does this, and pandas has a table export option.
Thanks for the great work on great tables!
We need to ensure that tab_style()
works to produce inline cell styles. While a lot of the groundwork on this has already been done, the remaining work will focus on the HTML rendering side.
Currently, because GT is a dataclass, it just displays full representations of its attributes in the console. Because its attributes are also dataclasses, they do the same, so things get a bit unwieldy:
e.g.
GT(_tbl_data= x color
0 1 red
1 2 blue, _body=<great_tables._gt_data.Body object at 0x163d31be0>, _boxhead=Boxhead([ColInfo(var='x', type=<ColInfoTypeEnum.default: 1>, column_label='x', column_align='right', column_width=None), ColInfo(var='color', type=<ColInfoTypeEnum.default: 1>, column_label='color', column_align='left', column_width=None)]), _stub=Stub([RowInfo(rownum_i=0, group_id=None, rowname=None, group_label=None, built=False), RowInfo(rownum_i=1, group_id=None, rowname=None, group_label=None, built=False)]), _row_groups=[], _group_rows=GroupRows([]), _spanners=Spanners([]), _heading=Heading(title=None, subtitle=None, preheader=None), _stubhead=None, _source_notes=[], _footnotes=[], _styles=[StyleInfo(locname='data', locnum=5, grpname=None, colname='x', rownum=0, colnum=None, styles=[CellStyleText(color='red', font=None, size=None, align=None, v_align=None, style=None, weight=None, stretch=None, decorate=None, transform=None, whitespace=None)]), StyleInfo(locname='data', locnum=5, grpname=None, colname='x', rownum=1, colnum=None, styles=[CellStyleText(color='blue', font=None, size=None, align=None, v_align=None, style=None, weight=None, stretch=None, decorate=None, transform=None, whitespace=None)])], _locale=<great_tables._gt_data.Locale object at 0x163d31130>, _formats=[], _options=Options(table_id=OptionsInfo(scss=False, category='table', type='value', value=None), table_caption=OptionsInfo(scss=False, category='table', type='value', value=None), table_width=OptionsInfo(scss=True, category='table', type='px', value='auto'), table_layout=OptionsInfo(scss=True, category='table', type='value', value='fixed'), table_margin_left=OptionsInfo(scss=True, category='table', type='px', value='auto'), table_margin_right=OptionsInfo(scss=True, category='table', type='px', value='auto'), table_background_color=OptionsInfo(scss=True, category='table', type='value', value='#FFFFFF'), table_additional_css=OptionsInfo(scss=False, category='table', type='values', value=None), table_font_names=OptionsInfo(scss=False, category='table', type='values', value=['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Helvetica Neue', 'Fira Sans', 'Droid Sans', 'Arial', 'sans-serif']), table_font_size=OptionsInfo(scss=True, category='table', type='px', value='16px'), table_font_weight=OptionsInfo(scss=True, category='table', type='value', value='normal'), table_font_style=OptionsInfo(scss=True, category='table', type='value', value='normal'), table_font_color=OptionsInfo(scss=True, category='table', type='value', value='#333333'), table_font_color_light=OptionsInfo(scss=True, category='table', type='value', value='#FFFFFF'), table_border_top_include=OptionsInfo(scss=False, category='table', type='boolean', value=True), table_border_top_style=OptionsInfo(scss=True, category='table', type='value', value='solid'), table_border_top_width=OptionsInfo(scss=True, category='table', type='px', value='2px'), table_border_top_color=OptionsInfo(scss=True, category='table', type='value', value='#A8A8A8'), table_border_right_style=OptionsInfo(scss=True, category='table', type='value', value='none'), table_border_right_width=OptionsInfo(scss=True, category='table', type='px', value='2px'), table_border_right_color=OptionsInfo(scss=True, category='table', type='value', value='#D3D3D3'), table_border_bottom_include=OptionsInfo(scss=False, category='table', type='boolean', value=True), table_border_bottom_style=OptionsInfo(scss=True, category='table', type='value', value='solid'), table_border_bottom_width=OptionsInfo(scss=True, category='table', type='px', value='2px'), table_border_bottom_color=OptionsInfo(scss=True, category='table', type='value', value='#A8A8A8'), table_border_left_style=OptionsInfo(scss=True, category='table', type='value', value='none'), table_border_left_width=OptionsInfo(scss=True, category='table', type='px', value='2px'), table_border_left_color=OptionsInfo(scss=True, category='table', type='value', value='#D3D3D3'), heading_background_color=OptionsInfo(scss=True, category='heading', type='value', value=None), heading_align=OptionsInfo(scss=True, category='heading', type='value', value='center'), heading_title_font_size=OptionsInfo(scss=True, category='heading', type='px', value='125%'), heading_title_font_weight=OptionsInfo(scss=True, category='heading', type='value', value='initial'), heading_subtitle_font_size=OptionsInfo(scss=True, category='heading', type='px', value='85%'), heading_subtitle_font_weight=OptionsInfo(scss=True, category='heading', type='value', value='initial'), heading_padding=OptionsInfo(scss=True, category='heading', type='px', value='4px'), heading_padding_horizontal=OptionsInfo(scss=True, category='heading', type='px', value='5px'), heading_border_bottom_style=OptionsInfo(scss=True, category='heading', type='value', value='solid'), heading_border_bottom_width=OptionsInfo(scss=True, category='heading', type='px', value='2px'), heading_border_bottom_color=OptionsInfo(scss=True, category='heading', type='value', value='#D3D3D3'), heading_border_lr_style=OptionsInfo(scss=True, category='heading', type='value', value='none'), heading_border_lr_width=OptionsInfo(scss=True, category='heading', type='px', value='1px'), heading_border_lr_color=OptionsInfo(scss=True, category='heading', type='value', value='#D3D3D3'), column_labels_background_color=OptionsInfo(scss=True, category='column_labels', type='value', value=None), column_labels_font_size=OptionsInfo(scss=True, category='column_labels', type='px', value='100%'), column_labels_font_weight=OptionsInfo(scss=True, category='column_labels', type='value', value='normal'), column_labels_text_transform=OptionsInfo(scss=True, category='column_labels', type='value', value='inherit'), column_labels_padding=OptionsInfo(scss=True, category='column_labels', type='px', value='5px'), column_labels_padding_horizontal=OptionsInfo(scss=True, category='column_labels', type='px', value='5px'), column_labels_vlines_style=OptionsInfo(scss=True, category='table_body', type='value', value='none'), column_labels_vlines_width=OptionsInfo(scss=True, category='table_body', type='px', value='1px'), column_labels_vlines_color=OptionsInfo(scss=True, category='table_body', type='value', value='#D3D3D3'), column_labels_border_top_style=OptionsInfo(scss=True, category='column_labels', type='value', value='solid'), column_labels_border_top_width=OptionsInfo(scss=True, category='column_labels', type='px', value='2px'), column_labels_border_top_color=OptionsInfo(scss=True, category='column_labels', type='value', value='#D3D3D3'), column_labels_border_bottom_style=OptionsInfo(scss=True, category='column_labels', type='value', value='solid'), column_labels_border_bottom_width=OptionsInfo(scss=True, category='column_labels', type='px', value='2px'), column_labels_border_bottom_color=OptionsInfo(scss=True, category='column_labels', type='value', value='#D3D3D3'), column_labels_border_lr_style=OptionsInfo(scss=True, category='column_labels', type='value', value='none'), column_labels_border_lr_width=OptionsInfo(scss=True, category='column_labels', type='px', value='1px'), column_labels_border_lr_color=OptionsInfo(scss=True, category='column_labels', type='value', value='#D3D3D3'), column_labels_hidden=OptionsInfo(scss=False, category='column_labels', type='boolean', value=False), row_group_background_color=OptionsInfo(scss=True, category='row_group', type='value', value=None), row_group_font_size=OptionsInfo(scss=True, category='row_group', type='px', value='100%'), row_group_font_weight=OptionsInfo(scss=True, category='row_group', type='value', value='initial'), row_group_text_transform=OptionsInfo(scss=True, category='row_group', type='value', value='inherit'), row_group_padding=OptionsInfo(scss=True, category='row_group', type='px', value='8px'), row_group_padding_horizontal=OptionsInfo(scss=True, category='row_group', type='px', value='5px'), row_group_border_top_style=OptionsInfo(scss=True, category='row_group', type='value', value='solid'), row_group_border_top_width=OptionsInfo(scss=True, category='row_group', type='px', value='2px'), row_group_border_top_color=OptionsInfo(scss=True, category='row_group', type='value', value='#D3D3D3'), row_group_border_right_style=OptionsInfo(scss=True, category='row_group', type='value', value='none'), row_group_border_right_width=OptionsInfo(scss=True, category='row_group', type='px', value='1px'), row_group_border_right_color=OptionsInfo(scss=True, category='row_group', type='value', value='#D3D3D3'), row_group_border_bottom_style=OptionsInfo(scss=True, category='row_group', type='value', value='solid'), row_group_border_bottom_width=OptionsInfo(scss=True, category='row_group', type='px', value='2px'), row_group_border_bottom_color=OptionsInfo(scss=True, category='row_group', type='value', value='#D3D3D3'), row_group_border_left_style=OptionsInfo(scss=True, category='row_group', type='value', value='none'), row_group_border_left_width=OptionsInfo(scss=True, category='row_group', type='px', value='1px'), row_group_border_left_color=OptionsInfo(scss=True, category='row_group', type='value', value='#D3D3D3'), row_group_default_label=OptionsInfo(scss=False, category='row_group', type='value', value=None), row_group_as_column=OptionsInfo(scss=False, category='row_group', type='boolean', value=False), table_body_hlines_style=OptionsInfo(scss=True, category='table_body', type='value', value='solid'), table_body_hlines_width=OptionsInfo(scss=True, category='table_body', type='px', value='1px'), table_body_hlines_color=OptionsInfo(scss=True, category='table_body', type='value', value='#D3D3D3'), table_body_vlines_style=OptionsInfo(scss=True, category='table_body', type='value', value='none'), table_body_vlines_width=OptionsInfo(scss=True, category='table_body', type='px', value='1px'), table_body_vlines_color=OptionsInfo(scss=True, category='table_body', type='value', value='#D3D3D3'), table_body_border_top_style=OptionsInfo(scss=True, category='table_body', type='value', value='solid'), table_body_border_top_width=OptionsInfo(scss=True, category='table_body', type='px', value='2px'), table_body_border_top_color=OptionsInfo(scss=True, category='table_body', type='value', value='#D3D3D3'), table_body_border_bottom_style=OptionsInfo(scss=True, category='table_body', type='value', value='solid'), table_body_border_bottom_width=OptionsInfo(scss=True, category='table_body', type='px', value='2px'), table_body_border_bottom_color=OptionsInfo(scss=True, category='table_body', type='value', value='#D3D3D3'), data_row_padding=OptionsInfo(scss=True, category='data_row', type='px', value='8px'), data_row_padding_horizontal=OptionsInfo(scss=True, category='data_row', type='px', value='5px'), stub_background_color=OptionsInfo(scss=True, category='stub', type='value', value=None), stub_font_size=OptionsInfo(scss=True, category='stub', type='px', value='100%'), stub_font_weight=OptionsInfo(scss=True, category='stub', type='value', value='initial'), stub_text_transform=OptionsInfo(scss=True, category='stub', type='value', value='inherit'), stub_border_style=OptionsInfo(scss=True, category='stub', type='value', value='solid'), stub_border_width=OptionsInfo(scss=True, category='stub', type='px', value='2px'), stub_border_color=OptionsInfo(scss=True, category='stub', type='value', value='#D3D3D3'), stub_row_group_background_color=OptionsInfo(scss=True, category='stub', type='value', value=None), stub_row_group_font_size=OptionsInfo(scss=True, category='stub', type='px', value='100%'), stub_row_group_font_weight=OptionsInfo(scss=True, category='stub', type='value', value='initial'), stub_row_group_text_transform=OptionsInfo(scss=True, category='stub', type='value', value='inherit'), stub_row_group_border_style=OptionsInfo(scss=True, category='stub', type='value', value='solid'), stub_row_group_border_width=OptionsInfo(scss=True, category='stub', type='px', value='2px'), stub_row_group_border_color=OptionsInfo(scss=True, category='stub', type='value', value='#D3D3D3'), summary_row_padding=OptionsInfo(scss=True, category='summary_row', type='px', value='8px'), summary_row_padding_horizontal=OptionsInfo(scss=True, category='summary_row', type='px', value='5px'), summary_row_background_color=OptionsInfo(scss=True, category='summary_row', type='value', value=None), summary_row_text_transform=OptionsInfo(scss=True, category='summary_row', type='value', value='inherit'), summary_row_border_style=OptionsInfo(scss=True, category='summary_row', type='value', value='solid'), summary_row_border_width=OptionsInfo(scss=True, category='summary_row', type='px', value='2px'), summary_row_border_color=OptionsInfo(scss=True, category='summary_row', type='value', value='#D3D3D3'), grand_summary_row_padding=OptionsInfo(scss=True, category='grand_summary_row', type='px', value='8px'), grand_summary_row_padding_horizontal=OptionsInfo(scss=True, category='grand_summary_row', type='px', value='5px'), grand_summary_row_background_color=OptionsInfo(scss=True, category='grand_summary_row', type='value', value=None), grand_summary_row_text_transform=OptionsInfo(scss=True, category='grand_summary_row', type='value', value='inherit'), grand_summary_row_border_style=OptionsInfo(scss=True, category='grand_summary_row', type='value', value='double'), grand_summary_row_border_width=OptionsInfo(scss=True, category='grand_summary_row', type='px', value='6px'), grand_summary_row_border_color=OptionsInfo(scss=True, category='grand_summary_row', type='value', value='#D3D3D3'), footnotes_font_size=OptionsInfo(scss=True, category='footnotes', type='px', value='90%'), footnotes_padding=OptionsInfo(scss=True, category='footnotes', type='px', value='4px'), footnotes_padding_horizontal=OptionsInfo(scss=True, category='footnotes', type='px', value='5px'), footnotes_background_color=OptionsInfo(scss=True, category='footnotes', type='value', value=None), footnotes_margin=OptionsInfo(scss=True, category='footnotes', type='px', value='0px'), footnotes_border_bottom_style=OptionsInfo(scss=True, category='footnotes', type='value', value='none'), footnotes_border_bottom_width=OptionsInfo(scss=True, category='footnotes', type='px', value='2px'), footnotes_border_bottom_color=OptionsInfo(scss=True, category='footnotes', type='value', value='#D3D3D3'), footnotes_border_lr_style=OptionsInfo(scss=True, category='footnotes', type='value', value='none'), footnotes_border_lr_width=OptionsInfo(scss=True, category='footnotes', type='px', value='2px'), footnotes_border_lr_color=OptionsInfo(scss=True, category='footnotes', type='value', value='#D3D3D3'), footnotes_marks=OptionsInfo(scss=False, category='footnotes', type='values', value='numbers'), footnotes_multiline=OptionsInfo(scss=False, category='footnotes', type='boolean', value=True), footnotes_sep=OptionsInfo(scss=False, category='footnotes', type='value', value=' '), source_notes_padding=OptionsInfo(scss=True, category='source_notes', type='px', value='4px'), source_notes_padding_horizontal=OptionsInfo(scss=True, category='source_notes', type='px', value='5px'), source_notes_background_color=OptionsInfo(scss=True, category='source_notes', type='value', value=None), source_notes_font_size=OptionsInfo(scss=True, category='source_notes', type='px', value='90%'), source_notes_border_bottom_style=OptionsInfo(scss=True, category='source_notes', type='value', value='none'), source_notes_border_bottom_width=OptionsInfo(scss=True, category='source_notes', type='px', value='2px'), source_notes_border_bottom_color=OptionsInfo(scss=True, category='source_notes', type='value', value='#D3D3D3'), source_notes_border_lr_style=OptionsInfo(scss=True, category='source_notes', type='value', value='none'), source_notes_border_lr_width=OptionsInfo(scss=True, category='source_notes', type='px', value='2px'), source_notes_border_lr_color=OptionsInfo(scss=True, category='source_notes', type='value', value='#D3D3D3'), source_notes_multiline=OptionsInfo(scss=False, category='source_notes', type='boolean', value=True), source_notes_sep=OptionsInfo(scss=False, category='source_notes', type='value', value=' '), row_striping_background_color=OptionsInfo(scss=True, category='row', type='value', value='rgba(128,128,128,0.05)'), row_striping_include_stub=OptionsInfo(scss=False, category='row', type='boolean', value=False), row_striping_include_table_body=OptionsInfo(scss=False, category='row', type='boolean', value=False), container_width=OptionsInfo(scss=False, category='container', type='px', value='auto'), container_height=OptionsInfo(scss=False, category='container', type='px', value='auto'), container_padding_x=OptionsInfo(scss=False, category='container', type='px', value='0px'), container_padding_y=OptionsInfo(scss=False, category='container', type='px', value='10px'), container_overflow_x=OptionsInfo(scss=False, category='container', type='overflow', value='auto'), container_overflow_y=OptionsInfo(scss=False, category='container', type='overflow', value='auto'), page_orientation=OptionsInfo(scss=False, category='page', type='value', value='portrait'), page_numbering=OptionsInfo(scss=False, category='page', type='boolean', value=False), page_header_use_tbl_headings=OptionsInfo(scss=False, category='page', type='boolean', value=False), page_footer_use_tbl_notes=OptionsInfo(scss=False, category='page', type='boolean', value=False), page_width=OptionsInfo(scss=False, category='page', type='value', value='8.5in'), page_height=OptionsInfo(scss=False, category='page', type='value', value='11.0in'), page_margin_left=OptionsInfo(scss=False, category='page', type='value', value='1.0in'), page_margin_right=OptionsInfo(scss=False, category='page', type='value', value='1.0in'), page_margin_top=OptionsInfo(scss=False, category='page', type='value', value='1.0in'), page_margin_bottom=OptionsInfo(scss=False, category='page', type='value', value='1.0in'), page_header_height=OptionsInfo(scss=False, category='page', type='value', value='0.5in'), page_footer_height=OptionsInfo(scss=False, category='page', type='value', value='0.5in'), quarto_disable_processing=OptionsInfo(scss=False, category='quarto', type='logical', value=False), quarto_use_bootstrap=OptionsInfo(scss=False, category='quarto', type='logical', value=False)), _has_built=False)
Let's simplify the representation in the console a bit by:
Note that this likely isn't too critical, since ideally people are developing somewhere they can preview the HTML output (similar to developing a plot), but may be useful for debugging
It looks like the options API in gt/_options.py
does not have any tests yet. Opening this issue to track adding tests. I've refactored OptionsAPI out in #50, so may be useful to add tests just before merging or right after
We should just need to change occurrences of some_df[col][row] = some_value
with some_df.loc[row, col] = some_value
.
Note that in both cases the row is being specified using an index value (e.g. 0 is not position 0, but matches any 0 in the pandas index). To select by position, we'll need to use .iloc
. Weirdly, this then means that we'll need to select the column by position as well (e.g. 7
rather than a string name), but isn't too hard to do..!
Will clean up a bit, but was triggered by this code:
from great_tables import GT, countrypops
# Get vectors of 2-letter country codes for each region of Oceania
countries = {
"Australasia": ["AU", "NZ"],
"Melanesia": ["NC", "PG", "SB", "VU"],
"Micronesia": ["FM", "GU", "KI", "MH", "MP", "NR", "PW"],
"Polynesia": ["PF", "WS", "TO", "TV"],
}
# a dictionary mapping region to country (e.g. AU -> Australasia)
region_to_country = {
region: country
for country, regions in countries.items()
for region in regions
}
keep_rows = (
countrypops.country_code_2.isin(list(region_to_country))
& countrypops.year.isin([2000, 2010, 2020])
)
# Create a gt table based on a preprocessed `countrypops`
wide_pops = (countrypops[keep_rows]
.assign(region = lambda D: D.country_code_2.map(region_to_country))
.pivot_table(index = ["country_name", "region"], columns = "year", values = "population")
.reset_index()
.sort_values(2020, ascending = False)
)
(
GT(wide_pops, rowname_col = "country_name", groupname_col = "region")
.tab_header(title ="Populations of Oceania's Countries in 2000, 2010, and 2020")
.tab_spanner(
label = "Total Population",
columns = lambda colname: True
)
.fmt_integer()
)
Essentially, tab_spanner
correctly selects columns using the lambda, but then it passes those columns to cols_move. However, because some of the cols it selected are integers (e.g. 2000
), cols_move interprets these as column positions :/
It appears that when multiple styles are specified in a tab_style call, only the first appears.
from great_tables import GT
from great_tables.data import airquality
gt_air = GT(airquality.head())
gt_air.tab_style(
[style.fill("yellow"), style.borders(sides = "all")],
loc.body("Temp", [1, 2])
)
Note that if you switch the styles in the list around, it seems like only the first appears.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.