norm: preserve canonical OBO prefix case in obo_handle (fixes ~503k dangling edges in monarch-kg)#40
Open
kevinschaper wants to merge 1 commit into
Conversation
`obo_handle` strips `OBO:` from CURIEs like `OBO:FBbt_00000001` and
rebuilds them as `FBbt:00000001`. The final step was uppercasing the
prefix portion with `.upper()`:
new_id = f"{split_id[0].upper()}:{split_id[1]}"
The accompanying comment ("Capitalize the prefix") describes the intent,
but `.upper()` is the wrong operation: it preserves the case for OBO
namespaces that are conventionally all-uppercase (HP, MONDO, UBERON,
CL, ...) but breaks any mixed-case OBO prefix.
In practice this caused every Drosophila and C. elegans gene-expression
edge in monarch-kg to dangle, because:
- phenio.json source has canonical IRIs like
http://purl.obolibrary.org/obo/FBbt_NNNNNNNN.
- KGX (which lacks an explicit prefix entry for FBbt / WBbt in its
default JSON-LD context) falls back to writing them as
`OBO:FBbt_NNNNNNNN`.
- This function then `.upper()`-ed the prefix, producing `FBBT:` and
`WBBT:` — neither of which is the canonical OBO Foundry CURIE for
those namespaces (`FBbt:`, `WBbt:`).
- Downstream consumers like the Alliance gene-to-expression ingest
look up nodes by the canonical form, so all ~503k Drosophila +
C. elegans gene-expression edges per monarch-kg release went to
the dangling-edge pile.
Drop the `.upper()` and preserve the case as it appears in the OBO
PURL local-name segment. Behaviour is unchanged for all-uppercase OBO
prefixes (HP/MONDO/UBERON/CL/...) — the `.upper()` was a no-op for those
already — and now correct for mixed-case prefixes (FBbt/WBbt/ZFA/XAO/...).
Add regression tests asserting both behaviours.
Verified end-to-end on a synthetic input through `clean_and_normalize_graph`:
before: OBO:FBbt_00000001 → FBBT:00000001
after: OBO:FBbt_00000001 → FBbt:00000001
HP / MONDO / UBERON / CL unchanged.
This was referenced Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
universalizer/norm.py::obo_handleconvertsOBO:<prefix>_<id>CURIEs into<prefix>:<id>form. The final step.upper()s the prefix portion:The comment describes the intent ("Capitalize"), but
.upper()is the wrong operation. It's a no-op for OBO namespaces whose canonical CURIE is all-uppercase (HP, MONDO, UBERON, CL, ...) but it silently breaks any mixed-case OBO prefix.What this caused in the wild
phenio.json has 372k canonical
http://purl.obolibrary.org/obo/FBbt_NNNNNNNNURIs (and 44kWBbt_URIs). KGX's default JSON-LD context doesn't have explicit entries forFBbtorWBbt, so it falls back to the genericOBO:prefix and writesOBO:FBbt_NNNNNNNN. universalizer then.upper()-ed the prefix, producingFBBT:NNNNNNNN— which is not the canonical OBO Foundry CURIE for fly anatomy.Downstream consumers (e.g. monarch-kg's Alliance gene-to-expression ingest) look up nodes by the canonical mixed-case form, so every Drosophila + C. elegans gene-expression edge went to the dangling-edge pile in every monarch-kg release — about 503,187 edges per release (
alliance_gene_to_expression_edges: 397,914 FBbt + 105,273 WBbt, ~99.7% of that ingest's anatomy-side danglers).Fix
Drop the
.upper()and preserve the case that came in from the OBO PURL local-name segment. Behaviour for all-uppercase OBO prefixes is unchanged (.upper()was a no-op for them); behaviour for mixed-case prefixes (FBbt, WBbt, ZFA, XAO, ...) is now correct.Tests added
A
TestObohandleclass with:test_uppercase_prefix_unchanged: HP / MONDO / UBERON / CL still produce the expected uppercase CURIEs (regression guard).test_mixed_case_prefix_preserved: FBbt / WBbt come out canonical.Both pass.
Verification (end-to-end through
clean_and_normalize_graph)Related
.upper()in this file runs regardless of which prefixmap context is passed.FBbt:/WBbt:nodes and the 503k dangling edges will resolve automatically — no consumer-side workaround needed.