Localization (Python)#

raven-toolbox objects in raven_toolbox.localization.

Functions#

Function

Summary

annotate_proteome

Annotate a proteome's transporters from sequence, via the bundled hmmsearch + diamond.

annotate_transporters

Parse a per-gene transporter-annotation table into TransporterAnnotation objects.

apply_assignment

Build the compartmentalised model from a proposal (deep copy; original untouched).

apply_localization

Apply a LocalizationProposal to model: move reactions, add the

assign_compartments

Assign reactions_to_relocate to compartments, certifying functionality with a real FBA.

AssignmentProposal

Outcome of assign_compartments.

combine_scores

Merge several LocalizationScores into a consensus by weighted sum.

confidence_bin

Calibration bin of a raw confidence (matches the benchmark's reported corroboration rates).

curation_priority

Rank reaction placements and added transports by how likely each needs manual curation.

default_metabolite_chebi

ChEBI identifiers from a metabolite's annotation, normalised to CHEBI:NNNN.

default_substrate_of

Coarse substrate class(es) for a model metabolite, from a name-keyword heuristic.

evidence_aware_transport_cost

Per-metabolite transport cost from transporter evidence, ready to pass as transport_cost.

fetch_protein_sequences

Fetch {gene_id: protein_sequence} from the UniProtKB REST API for an organism.

fetch_uniprot_localization

Query the UniProtKB REST API for an organism's subcellular locations → normalised

GrowthCondition

An extra medium the placement must also support, checked when certifying functionality.

load_compartments

Parse a COMPARTMENTS (jensenlab.org) channel file into a normalised

load_deeploc

Parse DeepLoc 2 CSV output into a normalised LocalizationScores.

load_mulocdeep

Parse MULocDeep output into a normalised LocalizationScores.

load_tc_substrates

Load tcdb_substrates.tsv -> {TC-ID: frozenset(ChEBI)} (auto-downloaded if path is None).

load_uniprot

Parse a UniProtKB export into a normalised LocalizationScores.

LocalizationProposal

What predict_localization proposes, before applying it.

LocalizationResult

Outcome of predict_localization (when apply=True).

LocalizationScores

Per-gene compartment scores. df is indexed by gene_id with one column per

predict_localization

Place a caller-specified set of reactions in compartments via MILP.

prepare_deeploc_input

Write a DeepLoc-2.1-ready FASTA of protein sequences for genes.

PreparedFasta

Outcome of prepare_deeploc_input.

relocate_reactions

Firmly place reactions in curator-chosen compartments and repair the model around the decision.

RelocationResult

Outcome of relocate_reactions: the repaired proposal plus a log of every change.

ReviewReport

Ranked curation suggestions from triage_localization (one row per flagged item).

SubstrateOntology

The ChEBI graph + TCDB substrate table, with a graded metabolite→substrate match.

TransporterAnnotation

Per-gene transporter evidence, from any source (Pfam/hmmer, TCDB/diamond, orthology, or a

triage_localization

Rank the localisation calls in proposal that most deserve manual curation (v1, no solver).

write_fasta

Write {id: sequence} to FASTA and return the file path(s).

Reference#

annotate_proteome#

annotate_proteome(proteome: str | Path, *, hmm_db: str | Path | None=None, tcdb_db: str | Path | None=None, substrates: str | Path | bool | None=None, threads: int=1)

Annotate a proteome's transporters from sequence, via the bundled hmmsearch + diamond.

Scans the proteome FASTA (headers = gene ids, as written by prepare_deeploc_input) against the transporter Pfam HMM database (hmmsearch --cut_ga) and the TCDB DIAMOND database (diamond blastp); both databases auto-download from toolbox-assets if not supplied. Pfam families and TCDB TC-families map to coarse substrate classes via raven_toolbox.localization.transporter_tables, and each gene's TCDB TC-IDs map to their curated substrate ChEBIs (substrates: a tcdb_substrates.tsv path, or False to skip that lookup). Returns gene → TransporterAnnotation (mechanism stays empty). Genes with no transporter hit are absent, so this doubles as a substrate_of-independent membrane screen.

annotate_transporters#

annotate_transporters(table: pd.DataFrame, *, gene_col: str='gene', confidence_col: str='confidence', families_col: str | None='families', substrate_col: str | None='substrate_classes', mechanism_col: str | None='mechanism', sep: str=';')

Parse a per-gene transporter-annotation table into TransporterAnnotation objects.

This is the bring-your-own path: the table can come from any tool (eggNOG-mapper, InterProScan, a web service); use annotate_proteome instead for the bundled hmmsearch/diamond back-ends. List columns (families, substrate_classes) may be real lists or sep-joined strings. A gene appearing on several rows keeps its highest-confidence annotation and the union of families / substrate classes.

apply_assignment#

apply_assignment(model: cobra.Model, proposal: AssignmentProposal, *, default_compartment: str='c', base_metabolite: Callable[[cobra.Metabolite], str] | None=None, universal: cobra.Model | None=None)

Build the compartmentalised model from a proposal (deep copy; original untouched).

default_compartment is the hub compartment star-topology transports route through. Pass the same base_metabolite and universal used in assign_compartments so the same compartment-agnostic keying is used (existing per-compartment metabolites are reused rather than duplicated) and the chosen gap-fill reactions are added.

apply_localization#

apply_localization(model: cobra.Model, proposal: LocalizationProposal, *, default_compartment: str='c')

Apply a LocalizationProposal to model: move reactions, add the

inter-compartment transports the proposal listed, and return (model_copy, added).

The returned model is a deep copy of the input (original left untouched). Moved reactions get their metabolites' compartment suffix swapped (e.g. A_c → A_m); new compartment-specific metabolite copies are added on demand. Each added transport is a passive diffusion M[default] ⇌ M[c] (RAVEN convention), named tr_<met>_<c>.

assign_compartments#

assign_compartments(model: cobra.Model, scores: LocalizationScores, reactions_to_relocate: Iterable[str], *, default_compartment: str='c', transport_cost: float | Mapping[str, float]=0.5, multi_compartment_penalty: float=0.5, transportable: Iterable[str] | None=None, base_metabolite: Callable[[cobra.Metabolite], str] | None=None, universal: cobra.Model | None=None, biomass_reaction: str | None=None, min_growth: float | None=None, growth_conditions: Iterable[GrowthCondition] | None=None, max_rounds: int=8, time_limit: float | None=None, prune_transports: bool=True, minimize_transports: bool=False, multi_localize: bool=False, multi_localize_threshold: float=0.7, multi_localize_eps: float=1e-06, multi_localize_loopless: bool=True)

Assign reactions_to_relocate to compartments, certifying functionality with a real FBA.

Placement is decided by a flux-free score MILP, and functionality is a separate, materialised-FBA-verified repair (see the module docstring and the compartment-assignment redesign <https://github.com/CompMetEng/raven-gecko-parity/blob/develop/docs/localization/localization_redesign.md>_ on raven-gecko-parity). Notable parameters:

transport_cost Accepted for signature compatibility but not used: the placement master is flux-free and score-only, and minimize_transports prunes transports by flux (pFBA), not by cost. multi_compartment_penalty Weight (default 0.5) that discourages the primary placement pass from spreading one gene's support across multiple compartments; higher values consolidate a gene's placement more aggressively. transportable Metabolites eligible for a cross-compartment transport, by base id. None (default) allows every metabolite touched by a relocatable reaction; pass a narrower set to forbid transports for the rest (those pools are then confinement-repaired by co-location instead). max_rounds Budget on placement-tightening rounds (confinement repair + growth-failure feedback). The common genome-scale case (everything transportable) certifies in one round. prune_transports Drop transports blocked in every certification medium (sound; on by default). minimize_transports After certifying, prune the transport (and gap-fill) set to the reactions that carry flux in a parsimonious FBA solution (_minimize_transports). Sound by construction (a zero-flux reaction can be removed without changing the solution) and fast (one LP-scale pFBA), it gives a large honest reduction (yeast-GEM: ~1260 -> ~280 transports) while staying certified — re-checked against every medium, falling back to the un-minimised set if a growth-condition need was pruned, so it is never non-functional. Off by default (adds one pFBA + re-certification). multi_localize: After certifying, add sound multi-compartment placements (_enrich_multilocalization): propose, per reaction, a second compartment its gene has DeepLoc evidence for (score >= multi_localize_threshold), materialise all candidates, and keep only those a single loopless FVA shows can carry flux >= multi_localize_eps in a biomass-supporting solution. Keeping the placement master mono-localised (tractable at genome scale) and gating each second compartment with one loopless FVA is what makes multi-localisation scale: an FVA that a dead duplicate cannot pass does the soundness check no in-MILP activity binary could afford at this size. Off by default (adds one FVA).

**Caveat (over-inclusive).** The gate — DeepLoc score above threshold *and* able to carry flux
— is *sound* (no dead duplicate survives) but not *precise*: many reactions can carry some flux
in a second compartment without being biologically dual, so at genome scale a large fraction is
multi-placed (yeast-GEM: ~700 of ~2300 at the default threshold). It recovers the
DeepLoc-visible dual enzymes (e.g. HTS1/VAS1/GLR1) but not those dual by post-translational
retro-translocation (FUM1, ACO1), whose second compartment carries no DeepLoc signal — catching
those needs a functionality-driven candidate source (a metabolite required in the second
compartment), which is future work. Treat this as a scalable, sound *lower bound* on
multi-localisation, not a precise dual-localisation caller.

multi_localize_threshold: DeepLoc score a gene needs in a second compartment for that placement to be proposed (default 0.7). Higher = fewer, more confident duplicates, but too high loses genuine duals (yeast-GEM: 0.9 drops HTS1/VAS1/GLR1; 0.7 keeps them). multi_localize_eps: Flux magnitude a proposed duplicate must reach in the FVA to be kept (default 1e-6). multi_localize_loopless: Use loopless FVA (default True) so a duplicate active only in a futile cycle is not kept — closing the internal-cycle caveat of the old in-MILP mode. Set False for a faster, cycle-permissive check.

Returns

AssignmentProposal

With certified set iff the materialised model reached the growth floor on every medium, and growths giving the biomass flux per medium ("__primary__" + each condition name). status is "certified" or "uncertified" (or a solver status if the master failed).

AssignmentProposal#

Outcome of assign_compartments.

Attributes

placements:

reaction_id -> [compartments] the reaction is assigned to (one or more).

gene_compartments:

gene_id -> [compartments].

added_transports:

(base_metabolite_id, compartment) pairs needing a transport to/from default_compartment.

added_reactions:

ids of gap-fill reactions pulled from the universal model to keep the compartmentalised network functional.

unplaced_reactions:

relocate-set reactions with no scored gene support (placed by function only).

objective:

placement-master objective (localisation score − transport − multi-penalty − gap-fill cost).

min_growth:

the growth floor enforced.

status:

solver status ("optimal", "infeasible", ...) plus "certified" / "uncertified" — the placement's functionality was (or was not) confirmed by a real FBA on the materialised model.

certified:

True iff apply_assignment + FBA reaches the growth floor on every medium.

growths:

set by assign_compartments: the materialised biomass flux per medium ("__primary__" plus each GrowthCondition name), so the caller can see how far an uncertified placement fell short without re-running FBA.

combine_scores#

combine_scores(sources: list[LocalizationScores], *, weights: list[float] | None=None)

Merge several LocalizationScores into a consensus by weighted sum.

Genes and compartments are the union across sources; absent entries count as 0. Scores are summed per (gene, compartment) (optionally weighted) and the result is per-gene normalised (best -> 1.0), so a compartment supported by several sources is reinforced relative to a lone-source call. Use this to fuse complementary evidence (e.g. DeepLoc + curated UniProt + COMPARTMENTS) instead of trusting one reference — robust when no single source is authoritative (two curated sources agree only ~90% on yeast-GEM).

confidence_bin#

confidence_bin(p: float)

Calibration bin of a raw confidence (matches the benchmark's reported corroboration rates).

curation_priority#

curation_priority(model: cobra.Model, proposal: AssignmentProposal, scores: LocalizationScores, *, base_metabolite: Callable[[cobra.Metabolite], str] | None=None, default_compartment: str='c', universal: cobra.Model | None=None, currency_metabolites: Iterable[str] | None=None, check_essential: bool=True, essential_candidate_threshold: float=0.2, min_growth: float | None=None, include_curated: bool=False)

Rank reaction placements and added transports by how likely each needs manual curation.

Parameters mirror assign_compartments where they overlap (model is the draft that was placed; pass the same base_metabolite/universal). Returns a pandas.DataFrame sorted by priority (descending), one row per (reaction, compartment) placement and per flagged added transport, with columns target (id), type ("placement"/"gapfill"/"transport"), compartment, priority (0-1), flags (the firing signal names, strongest first), action (the concrete thing to check), n_affected and affected. Dual-localised reactions yield one row per compartment. Surface the top few percent.

n_affected/affected give the one-hop coupling of each row — the other placed reactions that curating this one would touch, because they share a non-currency metabolite with it at the same compartment (for a placement) or depend on the transported species at either end of the bridge (for a transport). n_affected is the full count (sort by it to find high-impact curations that warrant care or a batched review); affected lists up to a dozen ids, the ones also flagged in this table first. The coupling is not transitive — each coupled reaction has its own blast radius.

check_essential runs a single-reaction-deletion essentiality check (an FBA per candidate) on the already-flagged placements only (base priority >= essential_candidate_threshold) and multiplies their score by 1 + 0.6 when removing them drops the materialised model's growth to near zero. Essentiality is measured against the applied model's own achievable growth, not the assignment's heuristic growth floor, and is skipped entirely if the materialised model cannot beat that floor (where the test could not discriminate) — pass min_growth to override the floor used for this comparison (default: the proposal's own floor). Set check_essential=False to skip the only non-cheap signal.

currency_metabolites extends the built-in currency/cofactor set (ATP, NAD(P)H, H+, H2O, CoA, Pi, ...) that is excluded from the topology signals — pass ids or names for any organism-specific pool that should also count as freely available.

A reaction whose localization confidence has been marked curated (via raven_toolbox.confidence.mark_curated, e.g. after a curator settled its placement) is dropped from the queue, closing the score -> review -> curate loop so a settled reaction stops resurfacing. Pass include_curated=True to score it anyway.

default_metabolite_chebi#

default_metabolite_chebi(metabolite: cobra.Metabolite)

ChEBI identifiers from a metabolite's annotation, normalised to CHEBI:NNNN.

The model-side input to the ChEBI substrate match; pass as metabolite_chebi to evidence_aware_transport_cost. Returns the empty set for an un-annotated metabolite (which then falls back to the coarse class match).

default_substrate_of#

default_substrate_of(metabolite: cobra.Metabolite)

Coarse substrate class(es) for a model metabolite, from a name-keyword heuristic.

A dependency-free first pass over transporter_tables.COARSE_CLASSES: it matches the metabolite's name against curated keyword lists, so (S)-malate → carboxylate, D-glucose → sugar, NADPH → nucleotide + cofactor_vitamin. Pass it as substrate_of to evidence_aware_transport_cost. It does not derive classes from a metabolite's ChEBI/KEGG annotation; an unmatched metabolite returns the empty set — no substrate support, so the parsimony prior stands (a safe default).

evidence_aware_transport_cost#

evidence_aware_transport_cost(model: cobra.Model, annotation: Mapping[str, TransporterAnnotation], gene_compartments: Mapping[str, Iterable[str]], *, substrate_of: Callable[[cobra.Metabolite], Iterable[str]] | None=None, ontology: SubstrateOntology | None=None, metabolite_chebi: Callable[[cobra.Metabolite], Iterable[str]] | None=None, sibling_weight: float=0.0, base_cost: float=0.5, base_metabolite: Callable[[cobra.Metabolite], str] | None=None)

Per-metabolite transport cost from transporter evidence, ready to pass as transport_cost.

For each (compartment-agnostic) metabolite m::

evidence(m) = max over transporter genes g of  g.confidence * substrate_match(g, m),
              restricted to genes localised to a compartment where m occurs (membrane gate).
cost(m)     = base_cost * (1 - evidence(m))

substrate_match is 1.0 for a coarse substrate-class overlap and, when an ontology is given, a graded 0..1 ChEBI roll-up (metabolite ChEBI → the gene's curated substrate ChEBI) that also evidences metabolites the coarse classifier misses. With neither substrate_of nor ontology the match is compartment-only (every carrier at the right membrane counts).

Parameters

model:

the model to cost; every compartment-agnostic metabolite base in it gets an entry in the result.

annotation:

gene -> transporter evidence, as returned by annotate_transporters or annotate_proteome.

gene_compartments:

gene -> predicted compartment ids (the reliable DeepLoc compartment calls). A carrier at compartment X is taken to support transports across X's boundary.

substrate_of:

metabolite -> its coarse substrate class(es) (e.g. default_substrate_of); a class overlap with the gene is full-credit evidence.

ontology:

a .substrate_ontology.SubstrateOntology enabling the finer, graded ChEBI-substrate match on top of (and filling the gaps of) the coarse classes.

metabolite_chebi:

metabolite -> its ChEBI id(s) for that match (default: default_metabolite_chebi, which reads the chebi annotation).

sibling_weight:

if > 0, also credit a metabolite that is a chemical relative of a curated substrate (shares a near ChEBI ancestor, not under it) at this fraction of a direct hit — trades specificity for recall (0 = off, the default).

base_cost:

the flat cost for an unsupported transport (recovers today's constant behaviour when no gene supports the metabolite).

base_metabolite:

metabolite -> the compartment-agnostic key to group it under (default: strip a trailing _<compartment> from the id).

Returns

dict[str, float]

every metabolite base -> cost (unsupported metabolites map to base_cost), so the result is a self-contained transport_cost mapping.

fetch_protein_sequences#

fetch_protein_sequences(organism: int | str, *, genes: Iterable[str] | None=None, id_field: str='gene_oln', reviewed: bool=True, extra_query: str | None=None, min_length: int=1, timeout: float=120.0)

Fetch {gene_id: protein_sequence} from the UniProtKB REST API for an organism.

organism is a UniProt organism/taxon id (e.g. 559292 for S. cerevisiae S288C). id_field picks which identifier becomes the key — "gene_oln" (ordered locus, the ORF name like YNR001C) matches yeast-GEM gene ids; also "accession" or "gene_primary". A UniProt entry listing several ids for the field maps each of them to its sequence.

genes restricts the result to those gene ids (others are still fetched but dropped); None returns every entry. reviewed=True keeps only curated Swiss-Prot entries; extra_query is ANDed into the UniProt query. min_length drops sequences shorter than it (DeepLoc 2.1 needs ≥ 10 aa). The query mirrors fetch_uniprot_localization.

fetch_uniprot_localization#

fetch_uniprot_localization(organism: int | str, *, compartment_map: Mapping[str, str]=DEFAULT_COMPARTMENT_MAP, id_field: str='gene_oln', reviewed: bool=True, extra_query: str | None=None, timeout: float=120.0)

Query the UniProtKB REST API for an organism's subcellular locations → normalised

LocalizationScores (no manual export needed).

organism is a UniProt organism/taxon id (e.g. 559292 for S. cerevisiae S288C). id_field picks which identifier becomes the gene id — "gene_oln" (ordered locus, the ORF name like YNR001C) matches yeast-GEM; also "accession" or "gene_primary". reviewed=True restricts to curated Swiss-Prot entries; extra_query is ANDed into the UniProt query string. Parsing matches load_uniprot.

GrowthCondition#

An extra medium the placement must also support, checked when certifying functionality.

assign_compartments materialises the placement and confirms it reaches min_growth by a real FBA not only on the primary medium but on each GrowthCondition medium too — so a transport or placement has to serve every condition that needs it, which keeps alternative-carbon-source pathways (respiration, β-oxidation, the glyoxylate cycle, ...) genuinely connected rather than stranded in flux-dead islands that single-medium growth cannot see.

Parameters

name:

Short label for the condition, used in the per-condition growth report. Distinct per condition.

medium:

exchange_reaction_id -> max uptake (>= 0). Exchanges not listed are closed for uptake, so medium fully specifies the environment (carbon source and supplements).

min_growth:

Biomass floor this condition must reach on the materialised model.

load_compartments#

load_compartments(path: str | Path, *, compartment_map: Mapping[str, str] | None=None, min_confidence: float=0.0)

Parse a COMPARTMENTS (jensenlab.org) channel file into a normalised

LocalizationScores.

Targets the tidy *_<channel>_full.tsv layout (no header): column 1 = protein/gene id, column 4 = GO compartment term name, last column = confidence score. Rows are aggregated per (gene, compartment) by the maximum confidence; min_confidence drops weak annotations (the integrated/text-mining channels score 0–5).

The per-organism files (e.g. yeast_compartment_integrated_full.tsv) are at https://download.jensenlab.org/ — fetch them there if the COMPARTMENTS Downloads web page is unavailable.

load_deeploc#

load_deeploc(path: str | Path, *, compartment_map: Mapping[str, str] | None=None, min_confidence: float=0.0, membrane_split: Mapping[str, str] | None=None, membrane_threshold: float=0.5, keep_raw_confidence: bool=False, normalise: bool=True)

Parse DeepLoc 2 CSV output into a normalised LocalizationScores.

DeepLoc 2's per-protein CSV has Protein_ID, Localizations, Signals then one probability column per compartment. The first column is the gene id; non-numeric metadata columns (Localizations, Signals) are dropped automatically.

min_confidence drops genes whose top compartment probability (before per-gene normalisation) is below it — DeepLoc's probability is well calibrated, so low-confidence calls are unreliable and best left to other evidence (benchmark: ~67% corroborated below 0.7 vs ~97% above 0.9).

membrane_split (e.g. {"m": "mm"}) routes an organelle's probability to its membrane sub-compartment when the protein is membrane-associated (1 - P(Soluble) >= membrane_threshold, using DeepLoc's Soluble column), else it stays in the lumen. Keys/values are your model's compartment ids (post-compartment_map). Only the mitochondrial split (m/mm) is supported by the evidence (matrix-vs-membrane AUC ~0.92); DeepLoc does not separate ER lumen from membrane, so do not add er/erm. See the DeepLoc yeast-GEM benchmark <https://github.com/CompMetEng/raven-gecko-parity/blob/develop/docs/localization/deeploc_yeast_benchmark.md>_ on raven-gecko-parity.

keep_raw_confidence=True attaches the per-gene pre-normalisation top probability to LocalizationScores.raw_confidence (normalisation otherwise forces every top to 1.0). It is the strongest signal for triage_localization.

normalise=False keeps DeepLoc's raw probabilities instead of rescaling every gene's best compartment to 1.0. The raw probabilities are calibrated (a 0.97 call is far more reliable than a 0.40 one), so leaving them un-normalised lets a downstream assignment weight confident genes more heavily; the default True preserves the RAVEN parseScores convention and keeps multi-source scales comparable. See the DeepLoc normalisation benchmark <https://github.com/CompMetEng/raven-gecko-parity/blob/develop/docs/localization/deeploc_normalisation_benchmark.md>_ on raven-gecko-parity.

load_mulocdeep#

load_mulocdeep(path: str | Path, *, compartment_map: Mapping[str, str] | None=None, id_column: str | None=None, sep: str | None=None, min_confidence: float=0.0)

Parse MULocDeep output into a normalised LocalizationScores.

Expects a wide table: a protein/gene id column plus one numeric probability column per major subcellular compartment (MULocDeep predicts 10). sep=None auto-detects the delimiter; pass id_column if the id is not the first column. min_confidence works as in load_deeploc.

load_tc_substrates#

load_tc_substrates(path: str | Path | None=None)

Load tcdb_substrates.tsv -> {TC-ID: frozenset(ChEBI)} (auto-downloaded if path is None).

load_uniprot#

load_uniprot(path: str | Path, *, compartment_map: Mapping[str, str]=DEFAULT_COMPARTMENT_MAP, id_column: str | None=None, location_column: str | None=None, sep: str='\t')

Parse a UniProtKB export into a normalised LocalizationScores.

UniProt's curated Subcellular location [CC] annotation is qualitative — a set of compartments, not probabilities — so each annotated compartment gets score 1.0 (a multi-location protein lands in several). The annotation text is scanned for the labels in compartment_map (which therefore doubles as the vocabulary), mapping them to your model's compartment ids; evidence {ECO:…} braces and Note=… free text are stripped first so incidental compartment mentions there are ignored.

Export from https://rest.uniprot.org with fields=accession,gene_oln,cc_subcellular_location (TSV). Pick id_column to match your model's gene ids — for yeast-GEM that is the ordered-locus column (Gene Names (ordered locus), the ORF id like YNR001C), not the default accession.

LocalizationProposal#

What predict_localization proposes, before applying it.

All DataFrames have one row per item. Use this with apply=False to preview changes; pass it back to apply_localization to commit, or diff against a curator's expectations.

LocalizationResult#

Outcome of predict_localization (when apply=True).

LocalizationScores#

Per-gene compartment scores. df is indexed by gene_id with one column per

compartment id; values are floats (higher = stronger evidence for that compartment).

Genes absent from df and NaN entries are treated as "no signal" by raven_toolbox.localization.predict_localization (uniform prior contribution).

raw_confidence (optional) is a per-gene Series of the predictor's pre-normalisation top probability — the loaders normalise every gene's best compartment to 1.0, which discards how confident the call was. load_deeploc populates it with keep_raw_confidence=True so downstream consumers (notably raven_toolbox.localization.triage_localization) can tell a 0.97 call from a 0.40 one.

predict_localization#

predict_localization(model: cobra.Model, scores: LocalizationScores, reactions_to_relocate: Iterable[str], *, default_compartment: str='c', transport_cost: float | Mapping[str, float]=0.5, multi_compartment_penalty: float=0.5, apply: bool=True, mip_gap: float | None=None, time_limit: float | None=None)

Place a caller-specified set of reactions in compartments via MILP.

Returns a LocalizationProposal (when apply=False) or a LocalizationResult (when apply=True).

reactions_to_relocate: the reaction ids to (re-)place. Everything else stays where it is. Boundary reactions and existing multi-compartment transports passed in this set are silently filtered out (always pinned). Pass an empty set or a list of zero non-boundary reactions to no-op.

transport_cost: either a scalar (same cost per added transport) or a mapping {metabolite_id_base: cost} (where the base id strips the compartment suffix, e.g. "glc__D" matches "glc__D_c"/"glc__D_e"). Negative costs favour adding the transport.

Multi-compartment gene scoring (default behaviour): a gene contributes its predictor score in each compartment it lands in; the highest-scoring compartment is "free", each additional compartment costs multi_compartment_penalty. A secondary compartment is only worth picking when its score (typically lower than the primary) still exceeds the penalty — no hard cutoff, just an explicit score-vs-penalty trade-off. Set multi_compartment_penalty very large for effectively mono-localised genes.

time_limit: cap the MILP in seconds (None = no cap). For genome-scale models with more than ~5000 gene-associated reactions or with ambiguous localisation scores, consider time_limit=900 (15 min) to prevent runaway solves on hard instances. MATLAB RAVEN's predictLocalization uses the same number as its default search budget, but for a different kind of process — it solves this problem with simulated annealing, not a MILP, so its maxTime isn't a solver cutoff with an optimality guarantee the way this one is.

prepare_deeploc_input#

prepare_deeploc_input(genes: Iterable[str] | cobra.Model, organism: int | str, path: str | Path, *, max_records_per_file: int | None=500, min_length: int=10, **fetch_kwargs)

Write a DeepLoc-2.1-ready FASTA of protein sequences for genes.

genes is an iterable of gene ids, or a cobra model (its .genes ids are used) — for yeast-GEM those are the ORF names that line up with id_field="gene_oln". Sequences are fetched from UniProtKB (see fetch_protein_sequences; pass e.g. reviewed=, id_field= through fetch_kwargs). The FASTA is chunked at max_records_per_file (default 500 — the DeepLoc 2.1 web-server limit; pass None for the standalone tool which has no limit). min_length drops sequences below DeepLoc's 10-aa minimum.

Returns a PreparedFasta with the file path(s) and the genes that had no reviewed sequence, so you can chase those separately. Run DeepLoc 2.1 on the file(s), then read its CSV with load_deeploc — the Protein_ID column will be these gene ids.

PreparedFasta#

Outcome of prepare_deeploc_input.

relocate_reactions#

relocate_reactions(model: cobra.Model, proposal: AssignmentProposal, decisions: dict[str, str], *, base_metabolite: Callable[[cobra.Metabolite], str] | None=None, default_compartment: str='c', biomass_reaction: str | None=None, min_growth: float | None=None, universal: cobra.Model | None=None, move_gene_siblings: bool=True, prune_orphan_transports: bool=True)

Firmly place reactions in curator-chosen compartments and repair the model around the decision.

decisions maps reaction ids to the compartment the curator wants them in. Returns a RelocationResult with the repaired proposal and a log of every secondary change (gene-linked co-moves, transports added/removed, and the growth certificate). model and proposal are the draft and its current assignment (as returned by assign_compartments); pass the same base_metabolite/universal.

move_gene_siblings (default True) co-moves any other reaction whose entire gene set now points to a single moved-to compartment — enforcing "one enzyme, one compartment" — and reports them; isozyme-shared reactions (genes split across compartments) are left in place. prune_orphan_transports removes a transport of species b at a reaction's old compartment when nothing else uses b there any more. The transport repair only touches species of the moved reactions.

RelocationResult#

Outcome of relocate_reactions: the repaired proposal plus a log of every change.

moved maps every relocated reaction id to (from_compartment, to_compartment) (the curator's pins and any gene-linked co-moves); co_moved lists just the reactions moved as a gene-consistency consequence. transports_added/transports_removed are (metabolite_base, compartment) tuples. growth_before/growth_after are the materialised model's objective value before and after; certified is True when the relocated model still meets the growth floor.

ReviewReport#

Ranked curation suggestions from triage_localization (one row per flagged item).

SubstrateOntology#

The ChEBI graph + TCDB substrate table, with a graded metabolite→substrate match.

Build one with load (downloads/caches both artefacts) and pass it to .transport_evidence.evidence_aware_transport_cost. Reachability closures are memoised, so scoring a whole model reuses each metabolite ChEBI's roll-up.

TransporterAnnotation#

Per-gene transporter evidence, from any source (Pfam/hmmer, TCDB/diamond, orthology, or a

hand-curated table). confidence is a 0..1 strength; substrate_classes are coarse shared classes (e.g. "amino_acid", "sugar", "organic_acid") used to match a transporter to a metabolite it can plausibly carry; substrate_chebi are the specific curated substrate ChEBIs (from TCDB), for the finer ChEBI-ontology match.

triage_localization#

triage_localization(proposal: object, scores: LocalizationScores, *, model: cobra.Model | None=None, sources: Mapping[str, LocalizationScores] | None=None, raw_confidence: pd.Series | None=None, compartment_trust: Mapping[str, float] | None=None, confidence_threshold: float=0.7, margin_threshold: float=0.15, entropy_threshold: float=0.7, weights: Mapping[str, float] | None=None, top_n: int | None=None)

Rank the localisation calls in proposal that most deserve manual curation (v1, no solver).

proposal is a localisation/assignment proposal exposing gene_compartments (gene -> [compartments]) and unplaced_reactions — both LocalizationProposal and assignCompartments' AssignmentProposal qualify. scores is the table that drove it.

raw_confidence (a gene -> top-probability Series) powers the strongest signal; if omitted it is read from scores.raw_confidence (set by load_deeploc(keep_raw_confidence=True)), and if that is also absent the confidence signal is skipped. sources (the pre-fusion per-source LocalizationScores, e.g. {"DeepLoc": ..., "UniProt": ...}) enables the source-conflict signal — combine_scores destroys it. model (a cobra model) adds a reactions column listing the reactions each gene gates. compartment_trust overrides DEEPLOC_COMPARTMENT_TRUST.

confidence_threshold, margin_threshold, and entropy_threshold set the cutoffs that trigger the low-confidence, borderline, and diffuse signals respectively (see the module docstring for what each signal means). weights controls how much each signal counts toward the blended uncertainty score. top_n caps how many rows are returned. Returns a ReviewReport ranked by uncertainty.

write_fasta#

write_fasta(sequences: Mapping[str, str], path: str | Path, *, max_records_per_file: int | None=None, wrap: int=60)

Write {id: sequence} to FASTA and return the file path(s).

Headers are the dict keys verbatim (no leading >). FASTA truncates a header at the first space and many tools key results on that token, so ids must not contain whitespace — a ValueError is raised otherwise (it would silently corrupt the id↔result mapping).

With max_records_per_file set (e.g. 500 for the DeepLoc 2.1 web server), records are split across <stem>_001<suffix>, <stem>_002<suffix>, … and every path is returned; otherwise a single file is written at path. wrap sets the residues-per-line (≤ 0 = one line per sequence).