ek.metrics#
Reference-based (offline) metrics: the score() side of ek.
A Metric compares one prediction to one gold reference and
returns a Score. The right metric is a function of the output
object type – a string wants CER/WER, a record wants field-level F1, a table
wants TEDS/GriTS, a typed graph wants a cost-weighted edit distance. This package
holds the concrete metrics and registers each under the "metrics" namespace so
the score() facade can dispatch by type or by name.
Shipping in core (zero/low-dependency): StringMetric (CER/WER, via
jiwer when present) and FieldMetric (record/dict field F1). The richer
metrics ship behind the [metrics] extra and import their backends lazily, so
constructing or registering them never pulls a heavy dependency:
AnlsMetric– ANLS / ANLS* (nested-JSON), viaanls_star.SpanF1Metric– span/slot P/R/F1 viaseqevalANDnervaluate, under an explicit, requiredMatchScheme.TedsMetric/GritsMetric– table-structure metrics (TEDS/TEDS-Struct, GriTS-Top/GriTS-Con) with astructure_onlytoggle.TypedGraphMetric– the flagship cost-weighted typed graph edit distance.
See misc/docs/ek_02 (the reference-based decision table and gotchas).
Classes
|
CER ( |
|
Per-field precision/recall/F1 for two dict records. |
|
Cost-weighted, type-aware typed-graph edit distance as a |
|
A typed graph: typed nodes plus directed typed edges between them. |
|
A node in a typed graph: an id, a node-type tag, and its field values. |
|
A directed, typed relation between two node ids. |
|
ANLS / ANLS* (nested-JSON) similarity as a |
|
Entity/slot precision/recall/F1 under an explicit |
|
The (backend, scheme) contract a span/slot F1 is computed under. |
|
TEDS / TEDS-Struct as a |
|
GriTS-Top / GriTS-Con as a |
|
A table as an ordered list of rows of |
|
One logical table cell: its text plus its row/column span (>=1 each). |
- class ek.metrics.AnlsMetric(*, threshold=0.5)[source]#
Bases:
objectANLS / ANLS* (nested-JSON) similarity as a
Metric.Handles a bare string (classic ANLS) or an arbitrarily nested dict/list/tuple/
Nonestructure (ANLS*); the backend dispatches on type.- Parameters:
threshold (
float) – ANLS zeroes a per-leaf similarity below this value before averaging (the classic ANLS 0.5 cut tolerates minor OCR/spelling noise).anls_starexposes no parameter for this – it reads a class constantANLSTree.THRESHOLD(default0.5) – so the metric applies the threshold by setting that constant around the call and restoring it. Caveat: this mutates a process-global, so it is not thread-safe; concurrentAnlsMetriccalls with different thresholds in the same process can interfere. Defaults to0.5.
- aggregate(scores)[source]#
Corpus ANLS = mean of per-item ANLS (each is already length-normalized).
Unlike CER/WER (which accumulate global edit counts) or field-F1 (micro-TP/ FP/FN), ANLS is defined as the average normalized similarity over the evaluation set – the “A” in ANLS – so the corpus statistic is the plain mean of the per-item scores.
- Return type:
- class ek.metrics.Cell(text='', rowspan=1, colspan=1)[source]#
Bases:
objectOne logical table cell: its text plus its row/column span (>=1 each).
- class ek.metrics.FieldMetric(*, canonicalizer=None)[source]#
Bases:
objectPer-field precision/recall/F1 for two dict records.
- Parameters:
canonicalizer – Optional
str -> strapplied to string values before comparison (e.g. casefold/whitespace folding).
- class ek.metrics.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.MatchScheme(*values)[source]#
-
The (backend, scheme) contract a span/slot F1 is computed under.
The two backends legitimately disagree (seqeval ignores other-type tags at the tag level; nervaluate includes them), so the scheme is always explicit. The
SEQEVAL_*members consume BIO/IOBES tag sequences; the others consume SemEval-2013 span lists via nervaluate.- EXACT = 'exact'#
exact span boundaries, type ignored.
- Type:
nervaluate exact
- PARTIAL = 'partial'#
boundary overlap (half credit), type ignored.
- Type:
nervaluate partial
- SEQEVAL_CONLL = 'seqeval_conll'#
seqeval, CoNLL
conlleval-compatible entity F1 (the default seqeval mode).
- SEQEVAL_STRICT = 'seqeval_strict'#
span boundaries AND type must match, keyed to a scheme.
- Type:
seqeval strict mode
- STRICT = 'strict'#
exact span boundaries AND correct type.
- Type:
nervaluate SemEval-2013 strict
- TYPE = 'type'#
correct type with at least some span overlap.
- Type:
nervaluate type
- class ek.metrics.SpanF1Metric(scheme=None, *, tagging_scheme='IOB2')[source]#
Bases:
objectEntity/slot precision/recall/F1 under an explicit
MatchScheme.- Parameters:
scheme (
Optional[MatchScheme]) – REQUIRED. The (backend, scheme) contract – there is deliberately no default, because seqeval and nervaluate disagree and a bare F1 is ambiguous (misc/docs/ek_02§2.2). Pass aMatchScheme.tagging_scheme (
str) – ForMatchScheme.SEQEVAL_STRICTonly: the BIO/IOBES tagging scheme name (e.g."IOB2","IOBES","BILOU") that strict-mode entity decoding is keyed to. Defaults to"IOB2".
- Raises:
TypeError – if
schemeis omitted (the whole point of this metric).
- aggregate(scores)[source]#
Micro-averaged F1 over the corpus (sum the raw counts, then divide once).
seqeval schemes carry TP/FP/FN; nervaluate schemes carry COR/INC/PAR/MIS/SPU (PAR worth half). Both micro-average by summing counts globally – never a mean of per-document F1s. The two backends’ counts are not commensurable, so a corpus mixing them is rejected rather than silently dropping one.
- Return type:
- class ek.metrics.StringMetric(mode='cer', *, canonicalizer=None)[source]#
Bases:
objectCER (
mode='cer') or WER (mode='wer') as aMetric.- Parameters:
mode (
str) –"cer"(character) or"wer"(word) error rate.canonicalizer – Optional
str -> strapplied to both sides before scoring (seeek.canonicalize). Usually supplied by the facade instead.
- class ek.metrics.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.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:
- class ek.metrics.TypedEdge(src, dst, edge_type='')[source]#
Bases:
objectA directed, typed relation between two node ids.
- class ek.metrics.TypedGraph(nodes=(), edges=())[source]#
Bases:
objectA typed graph: typed nodes plus directed typed edges between them.
- class ek.metrics.TypedGraphMetric(grammar=None, weights=None, *, canonicalizer=None, timeout=10.0, max_nodes=60)[source]#
Bases:
objectCost-weighted, type-aware typed-graph edit distance as a
Metric.- Parameters:
grammar (
Optional[GraphGrammar]) – Layer-AGraphGrammarsupplying cost weights (may also be passed per-call to__call__).weights (
Optional[Callable[[GraphGrammar,TypeRef],float]]) – OptionalCostWeightoverriding the schema weights.canonicalizer – Optional
str -> strapplied to field values before the equality check (the facade passesnormalize=through here); a schema’s per-fieldFieldSpec.normalizertakes precedence for the fields it names.timeout (
float) – Seconds budget for the (NP-hard) GED search; on timeout the best cost found so far is used (an approximation). Default 10.0.
- class ek.metrics.TypedNode(node_id, node_type, fields=<factory>)[source]#
Bases:
objectA node in a typed graph: an id, a node-type tag, and its field values.
Modules
ANLS / ANLS* (nested-JSON) similarity, wrapping |
|
Field-level metric for record / dict-shaped extractions. |
|
The flagship metric: cost-weighted, type-aware distance over a typed graph. |
|
Span / slot precision-recall-F1 over two backends that legitimately disagree. |
|
String metrics: Character/Word Error Rate (CER/WER). |
|
Table-structure metrics: TEDS / TEDS-Struct and GriTS (structure-only toggle). |