ek.metrics.tables#
Table-structure metrics: TEDS / TEDS-Struct and GriTS (structure-only toggle).
Two complementary table-recognition metrics from misc/docs/ek_02 §3.1:
TEDS (Tree-Edit-Distance-based Similarity; Zhong et al., PubTabNet, IBM, arXiv 1911.10683) renders a table as an HTML tree and scores
1 - EditDist(Ta, Tb) / max(|Ta|, |Tb|). It captures both structure and cell content. TEDS-Struct (thestructure_only=Truetoggle) ignores cell text to isolate structure, since “taking OCR errors into account may lead to an unfair comparison due to the different OCR models used by various TSR methods”.GriTS (Grid Table Similarity; Smock et al., Microsoft, arXiv 2203.12555) scores the predicted table directly as a 2-D matrix and yields precision/recall over the most-similar common sub-grid. Its GriTS-Top (topology, structure-only) and GriTS-Con (content) variants share the
structure_onlytoggle.
Licensing note (skills/ek-dev-licensing): the obvious wrapper target,
table-recognition-metric, pulls in the GPL Levenshtein package as a hard
runtime dependency (from Levenshtein import distance in its source) – a
scanner-invisible copyleft landmine that would fail the CI license gate. So TEDS is
implemented clean-room here on the MIT apted engine (already in
[metrics]), and GriTS is a dependency-free pure-Python implementation of the
2D-MSS heuristic. Both follow the precedent of reimplementing Krippendorff’s alpha in
pure Python rather than quarantining a copyleft dependency.
Input shapes#
pred and gold are Table objects, or anything the constructors
accept: an HTML str (<table>...</table>; via Table.from_html()) or a
2-D grid – a list of rows of cell strings, where None marks a cell spanned over
by a neighbour (via Table.from_grid()). The facade and registry coerce HTML
strings automatically.
Example
>>> from ek.metrics.tables import TedsMetric, GritsMetric, Table
>>> a = "<table><tr><td>a</td><td>b</td></tr></table>"
>>> b = "<table><tr><td>a</td><td>c</td></tr></table>"
>>> round(TedsMetric()(a, b).value, 3) # one cell-content edit
0.75
>>> TedsMetric(structure_only=True)(a, b).value # same structure -> 1.0
1.0
>>> GritsMetric(structure_only=True)(a, b).value # identical grid topology
1.0
Classes
|
One logical table cell: its text plus its row/column span (>=1 each). |
|
GriTS-Top / GriTS-Con as a |
|
A table as an ordered list of rows of |
|
TEDS / TEDS-Struct as a |
- class ek.metrics.tables.Cell(text='', rowspan=1, colspan=1)[source]#
Bases:
objectOne logical table cell: its text plus its row/column span (>=1 each).
- class ek.metrics.tables.GritsMetric(*, structure_only=False)[source]#
Bases:
objectGriTS-Top / GriTS-Con as a
Metric(pure-Python, P/R aware).- Parameters:
structure_only (
bool) – WhenTrue, score cell topology only (GriTS-Top); whenFalse(default), average topology with cell-content similarity (GriTS-Con).
- aggregate(scores)[source]#
Corpus GriTS F1: pool overlap and per-table cell counts, then divide once.
GriTS precision = overlap / |pred cells| and recall = overlap / |gold cells|; the corpus statistic micro-averages by summing the numerators and denominators globally – never a mean of per-table F1s.
- Return type:
- class ek.metrics.tables.Table(rows=())[source]#
Bases:
objectA table as an ordered list of rows of
Cells (the logical cells).rows[i]is the i-th row’s logical cells in reading order; spans are carried on eachCell. Usefrom_html()/from_grid()to build one, andas_grid()for the dense 2-D occupancy view GriTS scores.- as_grid()[source]#
Expand spans into a dense 2-D grid; a spanned position repeats its cell.
Each physical grid position holds the (shared)
Cellcovering it, so two tables with the same topology have grids of the same shape – the view GriTS aligns position-by-position.
- classmethod coerce(obj)[source]#
Coerce a
Table, an HTML string, or a 2-D grid into a Table.- Return type:
- class ek.metrics.tables.TedsMetric(*, structure_only=False)[source]#
Bases:
objectTEDS / TEDS-Struct as a
Metric(clean-room onapted).- Parameters:
structure_only (
bool) – WhenTrue, ignore cell text and score structure alone (TEDS-Struct), isolating table-structure accuracy from OCR noise. Defaults toFalse(full TEDS: structure + content).
- aggregate(scores)[source]#
Corpus TEDS = mean of per-table TEDS (the PubTabNet/OmniDocBench convention). Each per-table TEDS is already node-count-normalized, so the reported corpus statistic is the average, not a pooled edit-rate. The pooled
1 - sum(dist)/sum(nodes)is available from the per-itemdetailif a size-weighted variant is wanted instead.- Return type: