Localization (Python)#
raven-toolbox objects in raven_toolbox.localization.
Functions#
Function |
Summary |
|---|---|
Annotate a proteome's transporters from sequence, via the bundled |
|
Parse a per-gene transporter-annotation table into |
|
Build the compartmentalised model from a proposal (deep copy; original untouched). |
|
Apply a |
|
Assign |
|
Outcome of |
|
Merge several |
|
Calibration bin of a raw confidence (matches the benchmark's reported corroboration rates). |
|
Rank reaction placements and added transports by how likely each needs manual curation. |
|
ChEBI identifiers from a metabolite's annotation, normalised to |
|
Coarse substrate class(es) for a model metabolite, from a name-keyword heuristic. |
|
Per-metabolite transport cost from transporter evidence, ready to pass as |
|
Fetch |
|
Query the UniProtKB REST API for an organism's subcellular locations → normalised |
|
An extra medium the placement must also support, checked when certifying functionality. |
|
Parse a COMPARTMENTS (jensenlab.org) channel file into a normalised |
|
Parse DeepLoc 2 CSV output into a normalised |
|
Parse MULocDeep output into a normalised |
|
Load |
|
Parse a UniProtKB export into a normalised |
|
What |
|
Outcome of |
|
Per-gene compartment scores. |
|
Place a caller-specified set of reactions in compartments via MILP. |
|
Write a DeepLoc-2.1-ready FASTA of protein sequences for |
|
Outcome of |
|
Firmly place reactions in curator-chosen compartments and repair the model around the decision. |
|
Outcome of |
|
Ranked curation suggestions from |
|
The ChEBI graph + TCDB substrate table, with a graded metabolite→substrate match. |
|
Per-gene transporter evidence, from any source (Pfam/hmmer, TCDB/diamond, orthology, or a |
|
Rank the localisation calls in |
|
Write |
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
AssignmentProposalWith
certifiedset iff the materialised model reached the growth floor on every medium, andgrowthsgiving the biomass flux per medium ("__primary__"+ each condition name).statusis"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/fromdefault_compartment.added_reactions:ids of gap-fill reactions pulled from the
universalmodel 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:Trueiffapply_assignment+ FBA reaches the growth floor on every medium.growths:set by
assign_compartments: the materialised biomass flux per medium ("__primary__"plus eachGrowthConditionname), 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_transportersorannotate_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.SubstrateOntologyenabling 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 thechebiannotation).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-containedtransport_costmapping.
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, somediumfully 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).