From 5926c96653130719fdb21941a61276a9a9852b3a Mon Sep 17 00:00:00 2001 From: Eduard Kerkhoven Date: Sat, 18 Jul 2026 23:50:27 +0200 Subject: [PATCH] =?UTF-8?q?confidence:=20Thiele-Palsson=20score=20mapping?= =?UTF-8?q?=20+=20verified=20ECO=20ids=20(=C2=A710.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add thiele_palsson_score(reaction): the Thiele & Palsson reconstruction confidence score (0-4) derived from the gene_association facet's basis (gpr+literature -> 3, gpr -> 2, no-gpr -> 1), the facet that captures reaction-inclusion evidence. Grounded in the correspondences already asserted in score_gene_association_confidence. Respects the study doc's two cautions: the map runs forward only (basis -> score), so the ambiguous Thiele-Palsson 2 is never reverse-mapped to an evidence class, and curated is left unmapped (a curator's assertion does not name its evidence class). The ECO evidence-class ids in §9 were each verified against EBI's ECO (OLS) - ECO:0000044 sequence similarity, ECO:0000015 mutant phenotype, ECO:0000002 direct assay - and the assertion-method term ECO:0000305 is explicitly excluded as it is not an evidence class. Docs: §9 gains the concrete basis -> score -> ECO table; §10.3 marked mapping-shipped. Tests for the mapping and the end-to-end basis path. --- docs/studies/confidence_tracking.md | 65 ++++++++++++++++++----------- src/raven_toolbox/confidence.py | 26 ++++++++++++ tests/test_confidence.py | 26 ++++++++++++ 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/docs/studies/confidence_tracking.md b/docs/studies/confidence_tracking.md index 8d69da4..5c2b434 100644 --- a/docs/studies/confidence_tracking.md +++ b/docs/studies/confidence_tracking.md @@ -210,35 +210,48 @@ separate save step — the record serialises with the model. ## 9. Standards alignment (for the paper) -Map the categorical `level` onto the established **Thiele & Palsson reconstruction confidence score (0–4)** so -it is familiar to modellers and reviewers, and reference **ECO** (Evidence & Conclusion Ontology) terms where a -facet maps to an evidence class. Two cautions carried forward from the design work: +The **Thiele & Palsson reconstruction confidence score (0–4)** classifies a reaction by the *type* of +evidence for including it. It therefore maps from the `gene_association` facet — the one that measures +reaction-inclusion evidence — via its `basis`, **not** from a facet's `level` (a support-strength band) or +the `overall` score. `raven_toolbox.confidence.thiele_palsson_score(reaction)` returns it: + +| `gene_association` basis | Thiele-Palsson | ECO evidence class | +|---|---|---| +| `gpr+literature` | 3 — experimental / genetic | assign from the cited evidence, e.g. `ECO:0000015` (mutant phenotype) or `ECO:0000002` (direct assay); not inferable from the presence of a citation alone | +| `gpr` | 2 — sequence data | `ECO:0000044` sequence similarity evidence | +| `no-gpr` | 1 — modelling inference | — | +| `curated` | set by the curator's own evidence | set by the curator's own evidence | + +Two cautions govern the mapping: - Thiele & Palsson's table assigns score **2 to two different evidence classes** (physiological data *and* - sequence data), so a recorded 2 does not determine an evidence class and must not be mapped to one. -- Assertion-method ECO terms (e.g. "inferred by curator") are not evidence classes and must not be used as - such. Any ECO id must be checked against the ontology before it reaches the paper — a wrong ontology id is - worse than an omitted one. The SBO ids used above were each verified against EBI's SBO: `SBO:0000629` - biomass production, `SBO:0000395` encapsulating process, `SBO:0000630` ATP maintenance, `SBO:0000672` - spontaneous reaction, `SBO:0000655` transport reaction. + sequence data), so the mapping only runs *forward* (basis → score); a recorded 2 does not determine an + evidence class and must not be mapped back to one. `curated` is likewise left unmapped — a curator's + assertion does not, on its own, name the evidence class it rests on. +- Assertion-method ECO terms are **not** evidence classes and must not be used as such: `ECO:0000305` + "curator inference used in manual assertion" describes *how* a call was made, not the evidence for it. Any + ECO id must be checked against the ontology before it reaches the paper — a wrong ontology id is worse than + an omitted one. Every ECO id above was verified against EBI's ECO (OLS): `ECO:0000044` sequence similarity + evidence, `ECO:0000015` mutant phenotype evidence, `ECO:0000002` direct assay evidence. The SBO ids used + earlier were likewise verified: `SBO:0000629` biomass production, `SBO:0000395` encapsulating process, + `SBO:0000630` ATP maintenance, `SBO:0000672` spontaneous reaction, `SBO:0000655` transport reaction. ## 10. What is left -The **facet set is closed**: `localization`, `equation` and `gene_association` are shipped, and no further -facet is planned. What remains is finishing the work *around* them. +facet is planned. The wiring (§10.1) and the standards mapping (§10.3) are shipped; what remains is +validating beyond one model (§10.2) and the paper write-up. -### 10.1 Wire the facets together +### 10.1 Wire the facets together — shipped -- **`annotate_confidence(model, facets=[...])`** — one call that runs every applicable scorer, instead of - making a caller know the three scorer names and their argument shapes. `localization` needs a proposal and - a score table while the other two need only the model, so the umbrella must skip a facet whose inputs are - absent rather than fail — the same abstain-rather-than-guess rule the scores themselves follow. -- **Let `curation_priority` read the record.** Today it re-derives localisation evidence from scratch and - cannot see that a curator already settled a placement, so a `mark_curated` reaction keeps surfacing in the - review queue. Skipping facets at `level == "curated"` closes the loop between the two tools: score → review - → curate → *stop being asked about it*. This is the single change that makes curation feel finished. -- **Point the SBO precondition at its remedy.** The scorers warn on a model with no SBO terms, but do not say - that `raven_toolbox.annotation.add_sbo_terms(model)` is the fix. The warning should name it. +- **`annotate_confidence(model, proposal=..., scores=...)`** runs every applicable scorer in one call and + returns `{facet: count}`, so a caller need not know the three scorer names and their argument shapes. + `localization` runs only when a proposal and score table are given; a facet whose inputs are absent is + skipped rather than failed — the same abstain-rather-than-guess rule the scores themselves follow. +- **`curation_priority` reads the record.** A reaction a curator has settled with `mark_curated` is dropped + from the review queue (`include_curated=False`), closing the loop between the two tools: score → review → + curate → *stop being asked about it*. +- **The SBO precondition names its remedy.** The no-SBO-terms warning points at + `raven_toolbox.annotation.add_sbo_terms(model)`. ### 10.2 Validate beyond one model @@ -250,7 +263,9 @@ are calibrated or merely yeast-shaped, and would exercise the branches yeast can gene-rubric-vs-`Confidence Level` check only replicates on a model that records that note, so its absence elsewhere is itself worth reporting. -### 10.3 Standards alignment for the paper +### 10.3 Standards alignment for the paper — mapping shipped -§9 above: map `level` onto Thiele & Palsson 0–4, and attach ECO terms where a facet maps to an evidence -class — with the two cautions recorded there. The SBO half is already done and verified. +§9 above. The Thiele-Palsson mapping is shipped as `raven_toolbox.confidence.thiele_palsson_score` +(`gene_association` basis → 0–4), and its ECO evidence-class ids are verified against OLS. What remains is +paper-side, not code: assigning the specific ECO class for `gpr+literature` and `curated` reactions from the +actual cited/curator evidence (which the facet cannot infer), and the write-up itself. diff --git a/src/raven_toolbox/confidence.py b/src/raven_toolbox/confidence.py index 4a8326a..ca6fc40 100644 --- a/src/raven_toolbox/confidence.py +++ b/src/raven_toolbox/confidence.py @@ -68,6 +68,7 @@ "score_gene_association_confidence", "score_localization_confidence", "set_confidence", + "thiele_palsson_score", ] _KEY = "raven_confidence" @@ -597,6 +598,31 @@ def score_gene_association_confidence(model, *, overwrite_curated: bool = False, return n +#: ``gene_association`` facet ``basis`` -> Thiele & Palsson reconstruction confidence score (0-4). A +#: homology-derived GPR is sequence evidence (2); a GPR with a literature citation is +#: experimental/genetic (3); a reaction that should have a catalyst but has none is a modelling +#: inference (1). ``curated`` is deliberately absent: a curator's assertion does not, on its own, name +#: the *evidence class* it rests on (the score's ``basis`` is ``"curator"``), so its Thiele-Palsson +#: class must be set from the evidence the curator used, not inferred here (see the study doc §9). +_THIELE_PALSSON_FROM_GA_BASIS = {"gpr+literature": 3, "gpr": 2, "no-gpr": 1} + + +def thiele_palsson_score(reaction: cobra.Reaction) -> int | None: + """The reaction's Thiele & Palsson reconstruction confidence score (0-4), or ``None``. + + Derived from the ``gene_association`` facet's ``basis`` (:func:`score_gene_association_confidence`), + the facet that captures reaction-inclusion evidence: ``gpr+literature`` -> 3 (experimental/genetic), + ``gpr`` -> 2 (sequence), ``no-gpr`` -> 1 (modelling). Returns ``None`` when the facet is absent (the + reaction was not scored, or ``gene_association`` does not apply to it) or ``curated`` (whose evidence + class the curator must name — see the module note and the study doc §9). The ``localization`` and + ``equation`` facets are quality checks, not Thiele-Palsson evidence classes, and are not consulted. + """ + entry = get_confidence(reaction).facets.get("gene_association") + if entry is None: + return None + return _THIELE_PALSSON_FROM_GA_BASIS.get(entry.basis) + + def annotate_confidence( model: cobra.Model, *, diff --git a/tests/test_confidence.py b/tests/test_confidence.py index ecfed9b..fff5876 100644 --- a/tests/test_confidence.py +++ b/tests/test_confidence.py @@ -19,6 +19,7 @@ score_gene_association_confidence, score_localization_confidence, set_confidence, + thiele_palsson_score, ) from raven_toolbox.localization import AssignmentProposal from raven_toolbox.localization.scores import LocalizationScores @@ -487,3 +488,28 @@ def test_sbo_warning_names_add_sbo_terms(): m = _model() # carries no SBO terms with pytest.warns(UserWarning, match=r"add_sbo_terms"): score_equation_confidence(m) + + +# --------------------------------------------------------------------------- Thiele-Palsson mapping + +def test_thiele_palsson_score_maps_gene_association_basis(): + m = _model() + r = m.reactions.r1 + for basis, expected in (("gpr+literature", 3), ("gpr", 2), ("no-gpr", 1)): + set_confidence(r, "gene_association", ConfidenceEntry(0.6, basis=basis)) + assert thiele_palsson_score(r) == expected + # curated: the evidence class is the curator's to name, not inferred here -> None + mark_curated(r, facet="gene_association") + assert thiele_palsson_score(r) is None + # no gene_association facet at all (an exempt boundary reaction) -> None + assert thiele_palsson_score(m.reactions.EX_A) is None + + +def test_thiele_palsson_score_end_to_end(): + m = _model() + score_gene_association_confidence(m) + assert get_confidence(m.reactions.r1).facets["gene_association"].basis == "gpr" + assert thiele_palsson_score(m.reactions.r1) == 2 # sequence-only GPR + m.reactions.r1.annotation["pubmed"] = ["12345"] + score_gene_association_confidence(m) + assert thiele_palsson_score(m.reactions.r1) == 3 # GPR + literature