Skip to content

Corporate Ontology

A lightweight knowledge graph covering all Olink-measurable proteins and their relationships to diseases, drugs, pathways, and competitor platforms. Built entirely from local bioingest data as Parquet files -- no infrastructure required.

Schema

erDiagram
    Protein ||--o{ Platform : MEASURED_BY
    Protein ||--o{ Pathway : PARTICIPATES_IN
    Protein ||--o{ Drug : TARGETED_BY
    Protein ||--o{ Protein : INTERACTS_WITH
    Protein ||--o{ Platform : MEASURED_BY_COMPETITOR
    Protein ||--o{ External : SAME_AS
    Disease ||--o{ Disease : BROADER_THAN
    Disease ||--o{ External : SAME_AS
graph LR
    P[Protein<br/>26,498] -->|MEASURED_BY<br/>9,385| OL[Olink Panel]
    P -->|PARTICIPATES_IN<br/>53,996| PW[Pathway<br/>2,331]
    P -->|TARGETED_BY<br/>26,699| D[Drug<br/>65,169]
    P -->|INTERACTS_WITH<br/>196,305| P
    P -->|MEASURED_BY_COMPETITOR<br/>2,726| C[Competitor]
    DI[Disease<br/>31,884] -->|BROADER_THAN<br/>41,912| DI
    P -.->|SAME_AS<br/>60,880| X[External IDs]

Statistics

Category Count
Protein nodes 26,498
-- on Olink platforms 5,471
Disease nodes 31,884
Drug nodes 65,169
Pathway nodes (Reactome, human) 2,331
Platform nodes (Olink + competitors) 19
Total edges 391,903

Edge breakdown

Relationship Count Description
INTERACTS_WITH 196,305 Protein-protein interactions (score >= 0.4, at least one Olink protein)
SAME_AS 60,880 Cross-references (UniProt-ChEMBL, MONDO-DOID/MeSH/EFO)
PARTICIPATES_IN 53,996 Protein-to-Reactome pathway membership (human)
BROADER_THAN 41,912 Disease hierarchy (MONDO parent-child)
TARGETED_BY 26,699 Protein-to-drug links (TTD approved/clinical drugs)
MEASURED_BY 9,385 Protein-to-Olink panel membership
MEASURED_BY_COMPETITOR 2,726 Protein-to-competitor platform
Metric Coverage
Proteins in Reactome pathways 3,315 / 5,471 (61%)
Proteins that are drug targets 375 / 5,471 (7%)
Proteins with known interactions 4,916 / 5,471 (90%)

Data Sources

Source What it provides
bioingest/mapping/protein_map.parquet Protein nodes, ID cross-references
bioingest/mapping/disease_map.parquet Disease nodes (MONDO)
bioingest/mapping/mapping_graph.jsonl SAME_AS and BROADER_THAN edges
bioingest/bulk/olink_panels/ MEASURED_BY edges (Explore HT, Reveal, Explore 3072)
bioingest/bulk/reactome/ PARTICIPATES_IN edges
bioingest/bulk/ttd/ TARGETED_BY edges (drug-target activity)
bioingest/bulk/opentargets/interactions/ INTERACTS_WITH edges
bioingest/bulk/opentargets/drugs/ Drug node metadata
panel/store/competitor_coverage.parquet MEASURED_BY_COMPETITOR edges

Building the Ontology

# Build from scratch (requires bioingest data on disk)
panel ontology build

# Force rebuild
panel ontology build --force

# View stats
panel ontology stats

Output lands in panel/store/ontology/ as Parquet files:

panel/store/ontology/
  nodes_protein.parquet
  nodes_disease.parquet
  nodes_drug.parquet
  nodes_pathway.parquet
  nodes_platform.parquet
  edges.parquet

Programmatic Usage

Query a protein's full context

from panel.ontology import get_protein_context

ctx = get_protein_context("P01375")  # TNF
print(ctx["pathways"])      # Reactome pathway IDs
print(ctx["drugs"])         # Drug IDs targeting TNF
print(ctx["interactions"])  # Interacting protein UniProt IDs
print(ctx["olink_panels"]) # Which Olink panels measure it
print(ctx["competitors"])   # Which competitors measure it
from panel.ontology import get_olink_coverage_for_pathway

cov = get_olink_coverage_for_pathway("R-HSA-449147")  # Signaling by interleukins
# {'total': 112, 'on_olink': 68, 'coverage': 0.607}

Load raw edges for custom analysis

import pandas as pd
from panel.ontology import load_edges, load_protein_nodes

edges = load_edges()
proteins = load_protein_nodes()

# Find all drug targets that are NOT on Olink
drug_targets = set(edges[edges["rel_type"] == "TARGETED_BY"]["source_id"])
olink_proteins = set(proteins[proteins["on_olink"]]["uniprot_id"])
gaps = drug_targets - olink_proteins
print(f"{len(gaps)} drug targets not on any Olink panel")

# Find Olink proteins with the most pathway annotations
pathway_edges = edges[edges["rel_type"] == "PARTICIPATES_IN"]
pathway_counts = pathway_edges["source_id"].value_counts()
olink_pathway_counts = pathway_counts[pathway_counts.index.isin(olink_proteins)]
print(olink_pathway_counts.head(10))

Competitive landscape query

from panel.ontology import load_edges

edges = load_edges()

# Proteins measured by competitors but NOT by Olink
competitor_proteins = set(edges[edges["rel_type"] == "MEASURED_BY_COMPETITOR"]["source_id"])
olink_proteins = set(edges[edges["rel_type"] == "MEASURED_BY"]["source_id"])
competitor_only = competitor_proteins - olink_proteins
print(f"{len(competitor_only)} proteins only on competitor platforms")

Design Decisions

  • Parquet storage -- fast columnar reads, no database server needed, ships with the package
  • UniProt as primary key -- all proteins resolved to UniProt accession for consistent joins
  • Olink-centric filtering -- interactions filtered to include at least one Olink-measurable protein (reduces 14.6M to 196K edges)
  • Score threshold -- protein-protein interactions filtered to scoring >= 0.4 (medium-high confidence)
  • TTD for drug-target links -- provides experimentally validated drug-target relationships with activity data
  • MONDO for diseases -- comprehensive cross-references to ICD-10, MeSH, DOID, EFO