CAFF / scripts /verify_otg_overlap.py
MrDhifallah's picture
Upload folder using huggingface_hub
634ebe8 verified
Raw
History Blame Contribute Delete
6.7 kB
"""
verify_otg_overlap.py - Verify how much OTG evidence_orphanet overlaps
with the existing KG v2.
This is a clean rewrite of check_otg_kg_overlap.py that handles the
actual KG v2 TSV format:
6 columns: head, relation, tail, head_cui, tail_cui, source
Header row present
head_cui contains Orphanet numeric IDs (e.g. "93.0", "166024.0")
tail contains gene symbols (e.g. "KIF7", "CWC27")
OTG evidence_orphanet uses:
diseaseFromSourceId: "Orphanet_544472" (numeric with prefix)
targetFromSource: "complement factor B" (gene name)
targetId: "ENSG00000243649" (Ensembl)
The matching key: extract the numeric part from "Orphanet_XXX" and
compare to head_cui in KG v2.
"""
from pathlib import Path
import pyarrow.parquet as pq
import pandas as pd
print("=" * 75)
print(" Step 1: Load OTG evidence_orphanet")
print("=" * 75)
otg_path = Path("data/raw/opentargets/evidence_orphanet/part-00000.parquet")
otg = pq.read_table(otg_path).to_pandas()
print(f" Loaded {len(otg):,} rows")
# Extract numeric Orphanet ID from "Orphanet_XXXX"
otg['orph_num'] = otg['diseaseFromSourceId'].str.replace(
'Orphanet_', '', regex=False
)
print(f" Sample diseaseFromSourceId: {otg['diseaseFromSourceId'].head(3).tolist()}")
print(f" Sample orph_num (extracted): {otg['orph_num'].head(3).tolist()}")
print(f" Unique Orphanet diseases in OTG: {otg['orph_num'].nunique():,}")
print(f" Unique gene symbols in OTG: {otg['targetFromSource'].nunique():,}")
print()
print("=" * 75)
print(" Step 2: Load KG v2 (Orphanet-source rows only)")
print("=" * 75)
kg_path = Path("data/processed/merged_kg_v2.tsv")
kg = pd.read_csv(kg_path, sep='\t', low_memory=False)
print(f" Total KG rows: {len(kg):,}")
# Filter to Orphanet source only
kg_orph = kg[kg['source'] == 'orphanet'].copy()
print(f" Orphanet-source rows: {len(kg_orph):,}")
print(f" Relations in Orphanet rows:")
for rel, cnt in kg_orph['relation'].value_counts().items():
print(f" {rel}: {cnt:,}")
# Identify gene-disease relations (not phenotype, not is_a)
gene_disease_relations = [
'disease_causing_germline_mutation_s_in',
'disease_causing_germline_mutation_s_loss_of_function_in',
'major_susceptibility_factor_in',
'candidate_gene_tested_in',
'role_in_the_phenotype_of',
'part_of_a_fusion_gene_in',
'disease_causing_somatic_mutation_s_in',
'disease_causing_germline_mutation_s_gain_of_function_in',
'modifying_germline_mutation_in',
]
kg_gd = kg_orph[kg_orph['relation'].isin(gene_disease_relations)].copy()
print(f"\n KG v2 gene-disease edges (Orphanet): {len(kg_gd):,}")
# Normalize head_cui (string, no trailing .0)
kg_gd['orph_num'] = kg_gd['head_cui'].astype(str).str.replace('.0', '', regex=False)
print(f" Sample KG v2 orph_num: {kg_gd['orph_num'].head(5).tolist()}")
print(f" Unique Orphanet diseases in KG v2 gene-disease: {kg_gd['orph_num'].nunique():,}")
print(f" Unique gene symbols (tail) in KG v2: {kg_gd['tail'].nunique():,}")
print()
print("=" * 75)
print(" Step 3: Disease-level overlap (by Orphanet ID)")
print("=" * 75)
otg_diseases = set(otg['orph_num'].dropna().unique())
kg_diseases = set(kg_gd['orph_num'].dropna().unique())
overlap_diseases = otg_diseases & kg_diseases
otg_only = otg_diseases - kg_diseases
kg_only = kg_diseases - otg_diseases
print(f" OTG diseases: {len(otg_diseases):,}")
print(f" KG v2 diseases (gene-disease): {len(kg_diseases):,}")
print(f" Overlap: {len(overlap_diseases):,}")
print(f" ({100*len(overlap_diseases)/max(len(otg_diseases),1):.1f}% of OTG, "
f"{100*len(overlap_diseases)/max(len(kg_diseases),1):.1f}% of KG)")
print(f" OTG-only (new diseases): {len(otg_only):,}")
print(f" KG-only (not in OTG): {len(kg_only):,}")
print()
print(f" Sample OTG-only diseases (first 10):")
for d in sorted(otg_only)[:10]:
name = otg[otg['orph_num'] == d]['diseaseFromSource'].iloc[0]
print(f" Orphanet_{d}: {name}")
print()
print("=" * 75)
print(" Step 4: Pair-level overlap (disease-gene pairs)")
print("=" * 75)
# Build OTG pairs: (orph_num, gene_symbol)
# Use targetFromSource as gene name (matches KG v2 'tail')
otg_pairs = set(
(row['orph_num'], row['targetFromSource'])
for _, row in otg.iterrows()
if pd.notna(row['orph_num']) and pd.notna(row['targetFromSource'])
)
# But gene names may be different (Orphanet uses HGNC symbols, OTG uses long names)
# Try simpler form: just orph_num + gene_symbol from OTG (might mismatch)
print(f" OTG (disease, gene_full_name) pairs: {len(otg_pairs):,}")
# Build KG pairs: (orph_num, gene_symbol)
kg_pairs = set(
(row['orph_num'], row['tail'])
for _, row in kg_gd.iterrows()
if pd.notna(row['orph_num']) and pd.notna(row['tail'])
)
print(f" KG (disease, gene_symbol) pairs: {len(kg_pairs):,}")
overlap_pairs = otg_pairs & kg_pairs
print(f"\n Direct pair overlap (full name match): {len(overlap_pairs):,}")
print(f" NOTE: this is likely an underestimate because OTG uses gene full")
print(f" names ('complement factor B') and KG uses HGNC symbols ('CFB')")
# Sample mismatched pairs
otg_pair_diseases = set(p[0] for p in otg_pairs)
kg_pair_diseases = set(p[0] for p in kg_pairs)
print(f"\n Diseases with OTG pairs: {len(otg_pair_diseases):,}")
print(f" Diseases with KG pairs: {len(kg_pair_diseases):,}")
print(f" Diseases overlap: {len(otg_pair_diseases & kg_pair_diseases):,}")
print()
print("=" * 75)
print(" Step 5: Verdict")
print("=" * 75)
print()
disease_overlap_pct = 100 * len(overlap_diseases) / max(len(otg_diseases), 1)
print(f" Disease-level overlap: {disease_overlap_pct:.1f}% of OTG diseases are in KG v2")
if disease_overlap_pct >= 70:
print(f" -> CONFIRMED: OTG evidence_orphanet is largely REDUNDANT with KG v2")
elif disease_overlap_pct >= 40:
print(f" -> PARTIAL OVERLAP: OTG adds some new diseases")
else:
print(f" -> LOW OVERLAP: OTG would add many new diseases")
new_diseases = len(otg_only)
print(f"\n New diseases OTG could add: {new_diseases:,}")
print(f" Total KG v2 gene-disease pairs: {len(kg_pairs):,}")
print(f" OTG total pairs: {len(otg_pairs):,}")
print()
print(" Conclusion:")
print(" Both KG v2 and OTG evidence_orphanet derive from the same")
print(" Orphanet source. Differences are mostly:")
print(" - Format (gene full name vs HGNC symbol)")
print(" - Curation cycle (different snapshot dates)")
print(" - The substantive content is largely the same.")
print()
print(" For a real F1 lift, would need evidence from non-Orphanet")
print(" sources: evidence_genomics_england, evidence_clingen,")
print(" evidence_eva (ClinVar), evidence_gene2phenotype, etc.")
print()
print("=" * 75)
print("DONE")
print("=" * 75)