coeftable
Lightweight, report-ready summary tables for estimates with uncertainty. Renders inline forest plots, builds on great_tables HTML output, and works with pandas, polars, or pyarrow frames.
Installation
uv add coeftable
Quick start
The one-line form declares a table with a single estimate column:
import polars as pl
import coeftable as ct
df = pl.DataFrame(
{
"metric": ["Revenue", "Latency"],
"est": [3.4, 0.5],
"lb": [1.2, -1.0],
"ub": [5.7, 2.0],
}
)
ct.CoefTable(df, rows="metric", estimate="est", ci=("lb", "ub"))
A CoefTable renders itself in marimo, Jupyter, and any other _repr_html_-aware viewer —
leave it as the last expression in a cell, no extra call needed. Outside a notebook, use
.gt() to reach the underlying great_tables
object: table.gt().as_raw_html() for an HTML string, table.gt().save("t.png") for an
image, table.gt().tab_options(...) to keep styling with great_tables' own API.
Experiment table
Build a complete experiment results table with multiple estimates, a forest plot column, grouped row sections, nested variants, and direction hints:
import polars as pl
import coeftable as ct
experiment = pl.DataFrame(
{
"area": ["Core", "Core", "Ops", "Ops"],
"metric": ["Revenue", "Revenue", "Latency", "Latency"],
"variant": ["B", "C", "B", "C"],
"att": [12400.0, -3100.0, 40.0, 120.0],
"att_lb": [4200.0, -9800.0, -80.0, 45.0],
"att_ub": [20600.0, 3600.0, 160.0, 195.0],
"rel": [3.4, -1.2, 0.5, 2.0],
"rel_lb": [1.2, -4.0, -1.0, 0.8],
"rel_ub": [5.7, 1.6, 2.0, 3.2],
}
)
(
ct.CoefTable(experiment, rows="metric", nest="variant", groups="area")
.estimate("Lift Amount", "att", ci=("att_lb", "att_ub"), fmt=ct.Number(compact=True))
.estimate("Lift %", "rel", ci=("rel_lb", "rel_ub"), fmt=ct.Percent(signed=True))
.forest("Lift Plot", of="Lift %", ref=0.0, symmetric=True)
.header("Experiment Results", "Example Experiment")
.with_direction({"Latency": "lower_is_better"})
)
Comparing methods
Use split_columns to compare multiple methods side by side. Each value in the
split column produces its own set of estimate / forest columns:
import polars as pl
import coeftable as ct
methods = pl.DataFrame(
{
"metric": ["Revenue", "Revenue", "Latency", "Latency"],
"method": ["A", "B", "A", "B"],
"est": [3.4, 3.1, 0.5, 0.6],
"lb": [1.2, 1.0, -1.0, -0.8],
"ub": [5.7, 5.2, 2.0, 2.1],
}
)
(
ct.CoefTable(
methods, rows="metric", split_columns="method", estimate="est", ci=("lb", "ub")
)
.header("Cohort Revenue by Method")
)
Theming
Four built-in themes are available from coeftable.theme:
from coeftable.theme import BLUE, COLORBLIND, DEFAULT, MONO, TEXTUAL
DEFAULT # Alias for TEXTUAL -- what CoefTable uses if you don't set a theme
TEXTUAL # Minimal, publication-style: muted colours, light chrome
BLUE # The original blue-grey palette
COLORBLIND # Colourblind-safe palette
MONO # Grayscale for mono journals
Apply one with .with_theme(...):
table.with_theme(BLUE)
Customise a theme with dataclasses.replace:
from dataclasses import replace
my_theme = replace(BLUE, favorable="#0072B2")
Use with_direction to mark rows where lower values are favourable (reusing
the df frame from Quick start):
table = (
ct.CoefTable(df, rows="metric", estimate="est", ci=("lb", "ub"))
.with_direction({"Latency": "lower_is_better"})
)
Data shape
coeftable expects a dataframe where every row is a single comparison. The
resolution logic maps pairs of upper / lower bound columns to each estimate, so
your data should be wide in triples — one point-estimate column and (when
applicable) its lower and upper bound columns — rather than in long format with
a parameter column.
Dimensions:
rows— the label for each row in the table (e.g. a metric name).nest— an optional secondary label stacked below each row.groups— an optional column whose values produce section headers.split_columns— an optional column whose values produce repeated column groups side by side, useful for comparing methods.
Metadata
Release files for coeftable 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| coeftable-0.1.0.tar.gz | 157.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| coeftable-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 180.1 kB
Release files / coeftable-0.1.0.tar.gz
| Download URL | coeftable-0.1.0.tar.gz |
|---|---|
| Size | 157.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c49a1d6bf349bebe2f8e332d179cc07abe273df66ab2512174dd947b38afaa94
|
|
BLAKE2b-256 checksum How to use checksums |
21507e852b873a1e43cd4fe561140b9c73a6d4bce30296792f7b76d7043ec09a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency logRelease files / coeftable-0.1.0-py3-none-any.whl
| Download URL | coeftable-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1680a6def746a2d50e65b5ca9f1b94a4ec3e1294c18cf830c68010e1860ef8d8
|
|
BLAKE2b-256 checksum How to use checksums |
39be5fa018cfa0eb1342531731486b819f1ecfa04195c38e15d97b8103b9883a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency log