Phenotype Matching Needs Structure
Flat keyword overlap treats every finding as equally informative and ignores the ontology that connects a specific phenotype to its ancestors.
phenorank traverses a compact HPO graph, computes information content, rewards exact coverage, handles excluded findings, and shows every candidate's score.
The Demo
MONDO:001 ranks first at 0.6334, 0.2554 ahead of the runner-up, and stays first in all three leave-one-phenotype-out trials, so the ranking is stable rather than merely high.
The bundled ontology is deliberately small and deterministic. Clinical use needs current HPO releases, curated disease annotations, inheritance, variant evidence, and expert review.
The Exclusion Logic Was Eliminating Valid Candidates
Excluded findings matter as much as observed ones in a differential, so I stress-tested that half specifically. The contradiction check fired if the patient's excluded term was an ancestor of a disease's predicted phenotype, or if the disease's phenotype was an ancestor of the excluded term. Only the first is a real contradiction, where the disease specifically predicts something inside the excluded category.
The second direction zeroed out any disease that merely predicted a broader category than the patient's specific excluded finding. A disease annotated only with "abnormality of the nervous system" was scored zero because the patient ruled out "seizure," which happens to sit under that umbrella, even though the disease's own annotation never claimed it. For a tool whose job is prioritizing candidates, silently eliminating a legitimate one is the worst available failure. Keeping only the correct direction fixed it, and the demo's designed contradiction is still caught identically.
One Patient Ranked Correctly Is an Anecdote
Auditing this page, I noticed every number on it came from a single patient. So I simulated patients drawn from each disease's own annotations, with the noise a real clinic produces: findings not recorded, findings recorded at a parent term, and unrelated findings.
Top-1 accuracy is 1.000 on clean patients, 0.485 under realistic noise against a chance rate of 0.200, and 0.175 under harsh noise. That last row is at chance. It's a real limit of ranking 5 diseases over a 12-term ontology, and it's documented rather than tuned away.
The more useful finding is about the margin. Phenotypes drawn at random, where there is no right answer at all, produce a mean top-two margin of 0.149, slightly larger than the 0.133 real patients average under realistic noise. So the margin is not a confidence signal. The demo's 0.2554 says the winner won by that much; it does not say the ranking can be trusted. The leave-one-out stability line and the per-candidate scores are the parts worth reading. 13 tests pass across Python 3.10 through 3.13.
Research Basis
Recent benchmarking recommends phenotype-driven tools such as Exomiser and Genomiser as components of rare-disease prioritization workflows. Read the rare-disease prioritization recommendations.
Try It
No dependencies:
git clone https://github.com/ahmeddoghri/phenorank && cd phenorank
pip install -e .
phenorank demo # one patient, ranked and stability-checked
python -m phenorank.simulate # 600 simulated patients: clean, realistic, harsh
phenorank serve # workbench on :8090