hpo-drift 
What did a new HPO release change for your phenotype terms — and by how much did your similarity scores move?
The Human Phenotype Ontology is updated regularly. Terms get renamed, obsoleted and merged — and, the part nobody notices, the hierarchy gets new edges. New edges change information content, and information content is what Resnik and Lin similarity are made of. So the same patient set, scored against two HPO releases, gives different numbers even if none of your terms were touched. hpo-drift shows you exactly that, for the term list you actually use, in about three seconds.
The headline result
Feb 2026 → Jun 2026, 14 paediatric-rheumatology terms. None of them changed label, status or direct is_a parents. And yet, with Seco intrinsic IC computed on the is_a graph under Phenotypic abnormality:
IC moved for 9 of 14 terms, and every pair with an informative common ancestor — 25 of 25 — moved in Lin and Resnik. The other 66 pairs share only the root and stay at 0. Ontology-wide the release added 469 terms, obsoleted 22, renamed 266 and rewired the is_a graph by +886 / −185 edges. That is what moved your numbers.
Consequence for any paper using HPO similarity: pin the release tag in Methods, match on IDs (266 labels changed in four months), and report how much the numbers depend on the release. hpo-drift gives you that sentence with real figures.
30-second start
pip install hpo-drift
# one term per line — HP IDs (recommended) or labels
hpo-drift report --old v2026-02-16 --new v2026-06-23 --terms my_terms.txt
active terms: 19389 → 19836 (added 469, obsoleted 22, renamed 266)
is_a edges: +886 / −185
term label status IC old → new
HP:0001701 Pericarditis unchanged 0.930 → 0.837
HP:0045073 Serositis unchanged 0.837 → 0.790
…
pair Lin old → new Δ MICA
Skin rash ↔ Psoriasiform dermatitis 0.667 → 0.621 −0.045 HP:0000951
…
Add --json for a machine-readable report. Releases are pulled from the official GitHub assets of obophenotype/human-phenotype-ontology; any tag like v2026-06-23 works and is cached under ~/.cache/hpo-drift.
Three things it does
1 · report — the drift itself
Per term: status (unchanged / renamed / obsoleted → replacement / merged / missing), label change, parents added or removed, IC before and after. Per pair: Resnik and Lin in both releases, the delta, and the most-informative common ancestor — so you can see why a pair moved. Plus the ontology-wide counts.
2 · lint — hygiene for a term list
hpo-drift lint --release v2026-06-23 --terms my_terms.txt
⚠️ Arthritis: matched by LABEL — labels get renamed; store the ID → HP:0001369
❌ Dactylitis: label not found (exact match on names/synonyms)
❌ HP:0002960: OBSOLETE → replaced_by HP:0025095
Exit code 1 on errors, so it works as a CI gate for a phenotype spreadsheet. Matching is exact on purpose: it reproduces the failure mode of a pipeline that matches by label. In the example set, "Dactylitis" does not exist as a single HPO term (it is split into finger and toe dactylitis) and "Macrophage activation syndrome" is a disease-level concept, not an HPO phenotype — both are reasons to store IDs, and reasons why fuzzy resolution must suggest, never auto-map.
3 · a monthly GitHub Action
.github/workflows/drift.yml fetches the latest release, compares it with your pinned one (PINNED_HPO) and uploads the report. Add a threshold on lin_delta from the JSON and it becomes a failing check.
How it works
flowchart LR
A["release tag<br/>v2026-02-16"] -->|hp.obo| C[parse: terms · is_a · alt_id · obsolete]
B["release tag<br/>v2026-06-23"] -->|hp.obo| C
C --> D["intrinsic IC<br/>Seco 2004"]
T[your term list] --> E[resolve IDs / labels]
E --> F[per-term status · parents · IC Δ]
D --> G[Resnik / Lin per pair · MICA · Δ]
F --> R[report · JSON · lint]
G --> R
IC is intrinsic (Seco et al. 2004: 1 − log(descendants+1)/log(N)), computed on the is_a graph only, with N and descendant counts taken inside the closure of a root — HP:0000118 Phenotypic abnormality by default (--root to change; inheritance, frequency and modifier branches are excluded, and the root's IC is exactly 0). Every report states the method, the root and N. Because this IC depends only on the graph, the drift measured here is caused purely by ontology edits, which is the effect this tool isolates. Annotation-based IC (from phenotype.hpoa) adds a second, independent source of drift and is the next option on the roadmap — then the two can be shown side by side.
Companion tools
hpotools— HPO in R: release-pinned loading, similarity, Phenomizer-style ranking, enrichment.awesome-human-phenotype-ontology— link-verified list of HPO tools.
Roadmap
--ic annotations · fuzzy label suggestions in lint · Phenopackets v2 export · --pairs file for patient × disease scoring · JOSS paper.
Cite
DOI (all versions): 10.5281/zenodo.22286170 · this version: 10.5281/zenodo.22286288
Soloshenko M. hpo-drift: quantifying the effect of HPO release changes on phenotype-similarity results. 2026, v0.1.0. MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hpo_drift-0.1.3.tar.gz.
File metadata
- Download URL: hpo_drift-0.1.3.tar.gz
- Upload date:
- Size: 14.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f239be496b15241c89a47bcd676d246b1881af9d4b6767371518163bb430b9d7
|
|
| MD5 |
6129fea01380d7d55f8e1875cc9a4171
|
|
| BLAKE2b-256 |
8a94b0738c8a79ad642b098f26a5f6a4570f15faa5a114460bb5ee6c04bb95c6
|
Provenance
The following attestation bundles were made for hpo_drift-0.1.3.tar.gz:
Publisher:
publish.yml on MargoSolo/hpo-drift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hpo_drift-0.1.3.tar.gz -
Subject digest:
f239be496b15241c89a47bcd676d246b1881af9d4b6767371518163bb430b9d7 - Sigstore transparency entry: 2702983079
- Sigstore integration time:
-
Permalink:
MargoSolo/hpo-drift@d093e4b6fccf67f7026d71320ebe44c6d8be3467 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/MargoSolo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d093e4b6fccf67f7026d71320ebe44c6d8be3467 -
Trigger Event:
release
-
Statement type:
File details
Details for the file hpo_drift-0.1.3-py3-none-any.whl.
File metadata
- Download URL: hpo_drift-0.1.3-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8edbedc3cb35a2b21c7f71be0854d4c195d11e7f4df0f46ca443696ee45defd
|
|
| MD5 |
e901c1bb4a974e7ac0ec04f56a170583
|
|
| BLAKE2b-256 |
575f4b2595f62f4850d19887a3b5cfdec32cc2b7400d67213edfbd518d786778
|
Provenance
The following attestation bundles were made for hpo_drift-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on MargoSolo/hpo-drift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hpo_drift-0.1.3-py3-none-any.whl -
Subject digest:
e8edbedc3cb35a2b21c7f71be0854d4c195d11e7f4df0f46ca443696ee45defd - Sigstore transparency entry: 2702983595
- Sigstore integration time:
-
Permalink:
MargoSolo/hpo-drift@d093e4b6fccf67f7026d71320ebe44c6d8be3467 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/MargoSolo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d093e4b6fccf67f7026d71320ebe44c6d8be3467 -
Trigger Event:
release
-
Statement type: