Use attaviz.table() to create a publication table from a pandas DataFrame. It supplies Attaviz typography, colors, alignment, and spacing, then returns a native Great TablesGT object. Chain Great Tables methods to format values, rename columns, and add footnotes.
Start with one row per observation and keep numbers numeric. These illustrative observations include a missing completion rate to show how missing data appears. The final table expression displays the result in a notebook or Quarto page.
import pandas as pdimport attavizparticipation = pd.DataFrame({"country": ["Indonesia", "Malaysia", "Philippines"],"participants": [12345, 8900, 6750],"completion": [0.876, 0.923, None],"funding": [1250000, 920000, 740000],})table = attaviz.table( participation, title="Program participation by country", subtitle="Illustrative counts, completion rates, and funding", source="Source: illustrative example data. N/A indicates unavailable data.",)table
Program participation by country
Illustrative counts, completion rates, and funding
country
participants
completion
funding
Indonesia
12345
0.876
1250000
Malaysia
8900
0.923
920000
Philippines
6750
N/A
740000
Source: illustrative example data. N/A indicates unavailable data.
The table preserves row order and numeric precision. Missing cells display as N/A; numeric columns align right and text columns align left. Include the “Source:” prefix yourself. Tables use their own title, subtitle, and source arguments; the chart frame() helper does not apply to tables.
Format counts, percentages, and currency
Use the original column names in formatting methods, even after changing their display labels with cols_label():
Illustrative counts, completion rates, and funding
Country
Participants
Completion
Funding (USD)
Indonesia
12,345
87.6%
$1,250,000
Malaysia
8,900
92.3%
$920,000
Philippines
6,750
N/A
$740,000
Source: illustrative example data. N/A indicates unavailable data.
The first row now shows 12,345, 87.6%, and $1,250,000. Percent formatting expects fractions: store 0.876 for 87.6%. If your data already contains 87.6, use .fmt_percent(columns="completion", decimals=1, scale_values=False). For ordinary decimal values, use .fmt_number(columns="column_name", decimals=1).
These methods change the display while retaining the underlying values. D3 number/date strings are for Altair charts; use Great Tables formatting methods for tables.
Keep related columns close
Tables size to their content instead of stretching across the page. Put the record identifier first and related measures next to each other. Use native cols_width() to give longer labels enough room while keeping numeric columns compact. Headers and body text wrap without ellipses or hover-only disclosure:
formatted.cols_label( completion="Completed the program (%)",).cols_width( country="150px", participants="120px", completion="140px", funding="140px",)
Program participation by country
Illustrative counts, completion rates, and funding
Country
Participants
Completed the program (%)
Funding (USD)
Indonesia
12,345
87.6%
$1,250,000
Malaysia
8,900
92.3%
$920,000
Philippines
6,750
N/A
$740,000
Source: illustrative example data. N/A indicates unavailable data.
Give each column enough room for its longest formatted value. Setting every column width in pixels fixes the table to those widths; headers can still wrap. A narrow page scrolls the table horizontally instead of hiding data. For a table that should fill its container, use formatted.tab_options(table_width="100%").
Row density and wide tables
The default uses 6 px vertical cell padding, 9 px horizontal padding, and 1.5 line height. Wrapped body text aligns to the top, and column headings follow the same horizontal alignment as their values. Open Sans and tabular numerals keep numeric columns readable without switching fonts.
Use subtle striping when it helps readers follow a row across many columns. opt_row_striping() uses Attaviz’s light grey. This example removes the internal horizontal rules so the table uses one main row-tracking aid:
Illustrative participation, completion, duration, and funding
Country
Eligible people
Participants
Completion (%)
Duration (months)
Funding (USD)
Indonesia
18,000
12,345
87.6%
12
$1,250,000
Malaysia
12,000
8,900
92.3%
18
$920,000
Philippines
9,500
6,750
N/A
12
$740,000
Source: illustrative example data. N/A indicates unavailable data.
Keep the default light rules for smaller tables. You can adjust density with native .tab_options(data_row_padding="9px") when wrapped text needs more room; rows grow with their content rather than being forced to a fixed height.
Group columns and explain a value
Add a spanner above related columns and a footnote on the completion heading:
from great_tables import locpublication_table = ( formatted .tab_spanner(label="Program results", columns=["participants", "completion"]) .tab_footnote("Completion is the share of participants who finished the program.", locations=loc.column_labels(columns="completion"), ))publication_table
Program participation by country
Illustrative counts, completion rates, and funding
Country
Program results
Funding (USD)
Participants
Completion1
Indonesia
12,345
87.6%
$1,250,000
Malaysia
8,900
92.3%
$920,000
Philippines
6,750
N/A
$740,000
Source: illustrative example data. N/A indicates unavailable data.
1 Completion is the share of participants who finished the program.
Use loc.body(columns="completion", rows=[2]) instead to attach a footnote to the third row’s completion cell. Great Tables uses zero-based row positions.
Prepare row order and index labels
Sort or filter the DataFrame before passing it to table(). The DataFrame index is omitted; if it contains labels you need, turn it into a column first:
ranked = participation.set_index("country").sort_values("participants", ascending=False)attaviz.table( ranked.reset_index(), title="Countries ordered by participation",).fmt_integer(columns="participants")
Countries ordered by participation
country
participants
completion
funding
Indonesia
12,345
0.876
1250000
Malaysia
8,900
0.923
920000
Philippines
6,750
N/A
740000
Export and share
To save the completed table, run these commands in your notebook or script:
HTML export creates a standalone page and needs no browser. You can also get HTML as a string with publication_table.as_raw_html() for embedding in another page.
data must be a non-empty pandas DataFrame with unique string column names. Attaviz copies it, preserving row order and omitting the index.
title, subtitle, and source are optional plain text. A subtitle requires a title. Titles, subtitles, source notes, column names, and unformatted text cells are escaped as plain text.
Styling uses Attaviz’s medium typography and spacing, dark text on white, left-aligned headings, content-based widths, wrapping text, and subtle horizontal rules. Quarto table processing is disabled to preserve these defaults when embedding the table in documentation. Open Sans must be available in the viewing environment for an exact font match; otherwise a sans-serif fallback is used.
The WBG guide supplies the typography and colors; horizontal rules and other table layout choices are Attaviz defaults. These are static publication tables; sorting and pagination are outside this helper’s defaults.