Database Snapshot

About this page

Key metrics and visualizations from the selected version of the AOP-Wiki database. Use the version selector above to compare different database snapshots over time.

Current Database State

Entity Counts

Total number of each entity type and unique authors in the latest version of the AOP-Wiki database.

Methodology

What this measures: Full inventory of the selected snapshot: AOPs, Key Events, Key Event Relationships, Stressors, and the count of distinct authors who have contributed content. Use this card for a complete picture of database scope; use the Core Entity Summary card next to it for a stripped-down AOP/KE/KER view.

Data source: SPARQL queries against the AOP-Wiki RDF endpoint count instances of each entity class (aopo:AdverseOutcomePathway, aopo:KeyEvent, aopo:KeyEventRelationship, nci:C54571 for Stressors) and distinct creator URIs within a single named graph version.

Known limitations: Data reflects the RDF transformation of AOP-Wiki, not the raw database. Author counts are based on unique dc:creator URIs and may not correspond exactly to AOP-Wiki user accounts. Entities without RDF type annotations would not be counted.

View SPARQL Query
SELECT ?graph (COUNT(?aop) AS ?count)
WHERE {
  GRAPH ?graph {
    ?aop a aopo:AdverseOutcomePathway .
  }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1

Network Analysis

AOP Connectivity

Analysis of AOP connectivity based on shared Key Events. Connected AOPs share at least one Key Event with other AOPs, while isolated AOPs have unique Key Events.

Methodology

What this measures: Analyzes the connectivity of AOPs based on shared Key Events. Connected AOPs share at least one Key Event with other AOPs, while isolated AOPs have entirely unique Key Events. This reveals the degree of overlap and interconnection in the AOP knowledge base.

Data source: Two SPARQL queries: one counts total AOPs, the other counts AOPs that share at least one Key Event with another AOP (via aopo:has_key_event). The difference gives the isolated AOP count.

Known limitations: Connectivity is determined solely by shared Key Events declared via aopo:has_key_event. Implicit relationships or shared stressors are not considered.

Total AOPs in the latest snapshot (denominator)
SELECT ?graph (COUNT(DISTINCT ?aop) AS ?total_aops)
WHERE {
  GRAPH ?graph {
    ?aop a aopo:AdverseOutcomePathway .
  }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1
AOPs sharing at least one Key Event with another AOP (connected); isolated = total - connected
SELECT ?graph (COUNT(DISTINCT ?aop1) AS ?connected_aops)
WHERE {
  GRAPH ?graph {
    ?aop1 aopo:has_key_event ?ke .
    ?aop2 aopo:has_key_event ?ke .
    FILTER(?aop1 != ?aop2)
  }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1

AOP-AOP Overlap Network

Force-directed network where nodes are AOPs and edges connect AOP pairs that reuse the same Key Event records. Similarity is the Jaccard index on each AOP's KE set, so overlap is judged relative to how large the two AOPs are rather than by raw shared-KE count. Use the slider above to adjust the threshold (range 0.30-0.60, default 0.34). Node colour = cluster, node size = #KEs in the AOP; magenta edges mark pairs whose KE sets are identical — the same pathway entered twice. Hover a node for its title and metadata.

Methodology

What this measures: Force-directed network of AOPs that reuse the same Key Event records. Nodes are AOPs (colour = cluster, size = #KEs); an edge joins a pair whose Jaccard similarity on their KE sets reaches the slider threshold (range 0.30-0.60, default 0.34). Jaccard is |A n B| / |A u B| over KE URIs, so overlap is judged relative to how large the two AOPs are — a change from the raw shared-KE count used before, which scored two 30-KE AOPs sharing 5 KEs the same as two 6-KE AOPs sharing 5. Clusters are the connected components of the thresholded pair graph. On the 2026-07-01 snapshot the default view gives 69 clusters over 458 pairs, the largest holding 24 AOPs; 7 pairs have identical KE sets and are drawn in magenta. Isolated AOPs (no partner meeting the threshold) are not drawn.

Data source: Three SPARQL queries against the target graph. First resolves the latest snapshot graph URI. Second pulls every (AOP, Key Event) membership in one flat result (~3.7k rows for 537 AOPs). Third fetches per-AOP title and OECD status for the nodes that appear in an edge, issued in chunks of 100 URIs because the shared SPARQL helper uses GET and a single VALUES block over every node overruns the URL length limit. Scoring is done in Python: an inverted Key Event → AOPs index yields only the ~11k pairs with a non-empty intersection rather than the ~144k full upper triangle, Jaccard is computed per pair, and clusters are the connected components (networkx). Spring layout per component (k = 2.6/√n, 120 iterations, seed=42), components tiled on a square grid.

Known limitations: Clustering is single-linkage, so it chains: a sequence of moderately-similar pairs merges into one component. That is why the slider floor is 0.30 — below it the graph collapses (at 0.25 the largest component holds 123 AOPs, at 0.20 it holds 227 of 537) and the picture stops being informative. Similarity is Key Event record identity, not biology: two AOPs describing the same events with separately-authored KE records score 0, so this finds redundant curation, not redundant science. The absolute floor of 2 shared KEs guards against a pair of tiny AOPs reaching a high Jaccard on a single shared KE; on current snapshots it is inert (no pair at the default threshold involves a 1-KE AOP). Edges are capped at 1000, strongest first; the cap does not bind at any threshold in the slider range today. AOPs with no Key Events are absent. The network is undirected — KE-level directionality is lost. The displayed queries reflect the default threshold; the live plot uses the slider value.

Resolve latest snapshot graph URI
SELECT ?graph WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
} GROUP BY ?graph ORDER BY DESC(?graph) LIMIT 1
Every (AOP, Key Event) membership — Jaccard is computed from these sets in Python
SELECT ?aop ?ke
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?ke .
  }
}
Per-AOP title and OECD status for nodes in an edge (issued in chunks of 100)
SELECT ?aop ?title ?status
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?aop { <https://identifiers.org/aop/324> <https://identifiers.org/aop/602> }
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL { ?aop <http://purl.org/dc/elements/1.1/title> ?title }
    OPTIONAL { ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?s . BIND(STR(?s) AS ?status) }
  }
}

Average Components per AOP

Average number of Key Events and Key Event Relationships per AOP in the current database.

Methodology

What this measures: Calculates the average number of Key Events and Key Event Relationships per AOP in the selected database version. This metric indicates the typical structural complexity of AOPs.

Data source: SPARQL queries count the total number of AOP-to-KE and AOP-to-KER relationships (via aopo:has_key_event and aopo:has_key_event_relationship), then divide by the total number of AOPs.

Known limitations: Averages can be skewed by a few very large or very small AOPs. The metric does not distinguish between well-developed and stub AOPs. Relationships must be explicitly declared in RDF.

Count AOPs in latest graph
SELECT ?graph (COUNT(?aop) AS ?count)
WHERE {
  GRAPH ?graph { ?aop a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1
Count AOP-Key Event memberships in latest graph (numerator; Key Events reused across AOPs are counted once per AOP)
SELECT ?graph (COUNT(?ke) AS ?count)
WHERE {
  GRAPH ?graph { ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?ke . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1
Count AOP-Key Event Relationship memberships in latest graph
SELECT ?graph (COUNT(?ker) AS ?count)
WHERE {
  GRAPH ?graph { ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event_relationship ?ker . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1

KER Directionality

Key Events classified by their position in the directed KER network — initiators, terminals, linear links, convergent/divergent branch points, two-way hubs, and KEs in no KER (isolated).

Methodology

What this measures: Classifies every Key Event by its position in the directed KER network (upstream KE → downstream KE): initiators (no upstream), terminals (no downstream), linear links, convergent/divergent branch points, two-way hubs, and KEs in no KER (isolated). The subtitle reports how many KEs and directed relationships form the network.

Data source: SPARQL retrieves distinct upstream/downstream KE pairs from KeyEventRelationships; Python counts each KE's distinct upstream (in) and downstream (out) neighbours and bins by (in, out) degree. A separate count of all KeyEvents yields the isolated (no-KER) bucket.

Known limitations: Degree uses distinct neighbours (parallel KERs between the same KE pair count once). Direction comes from aopo:has_upstream_key_event / has_downstream_key_event; KERs missing either endpoint are excluded. Roles are global across all AOPs, not per-AOP.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distinct directed KER edges (upstream KE -> downstream KE)
SELECT DISTINCT ?up ?down
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ker a aopo:KeyEventRelationship ;
         <http://aopkb.org/aop_ontology#has_upstream_key_event> ?up ;
         <http://aopkb.org/aop_ontology#has_downstream_key_event> ?down .
  }
}
Total Key Events (for the isolated bucket)
SELECT (COUNT(DISTINCT ?ke) AS ?ke_count)
WHERE { GRAPH <http://aopwiki.org/graph/2026-07-01> { ?ke a aopo:KeyEvent . } }

Key Event Analysis

KE Component Distribution

Distribution of Key Event component annotations (Process, Object, Action) in the current database.

Methodology

What this measures: Shows the distribution of Key Event Component annotations (Biological Process, Biological Object, and Action) across all Key Events. Each KE can have up to three component annotations describing the biological context.

Data source: A SPARQL query counts distinct biological process, object, and action terms attached to Key Events via the aopo:hasBiologicalEvent hop. Each KE links to a biological-event node, which carries optional aopo:hasProcess, aopo:hasObject, and aopo:hasAction annotations.

Known limitations: Only counts presence/absence of each component type, not the quality or specificity of annotations. KEs without any component annotations are not shown. Some KEs may have multiple annotations of the same type.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Count distinct process/object/action terms via biological events
SELECT
  (COUNT(DISTINCT ?process) AS ?processes)
  (COUNT(DISTINCT ?object) AS ?objects)
  (COUNT(DISTINCT ?action) AS ?actions)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?bioevent .
    OPTIONAL { ?bioevent aopo:hasProcess ?process . }
    OPTIONAL { ?bioevent aopo:hasObject ?object . }
    OPTIONAL { ?bioevent aopo:hasAction ?action . }
  }
}

KE Annotation Depth

Distribution of annotation depth for Key Events, showing how thoroughly they are documented.

Methodology

What this measures: Measures how thoroughly each Key Event is annotated by counting how many of the three possible KE Component types (Process, Object, Action) each KE has. Shows the distribution across 0, 1, 2, or 3 annotations per KE.

Data source: A SPARQL query counts the number of biological-event annotations on each Key Event (?ke aopo:hasBiologicalEvent ?bioevent). Each bioevent carries at most one of aopo:hasProcess, aopo:hasObject, or aopo:hasAction, so the bioevent count equals the KE's component-annotation depth. The plot bins KEs by this count (0, 1, 2, 3+ components).

Known limitations: Treats all component types equally regardless of scientific importance. A KE with 3 low-quality annotations ranks higher than one with 1 highly specific annotation. Only measures breadth, not depth of annotation.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Count biological-event annotations per Key Event
SELECT ?ke (COUNT(DISTINCT ?bioevent) AS ?annotation_depth)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent .
    OPTIONAL { ?ke aopo:hasBiologicalEvent ?bioevent . }
  }
}
GROUP BY ?ke
ORDER BY DESC(?annotation_depth)

MIE→AO Path Length

Shortest number of KER steps from Molecular Initiating Event to Adverse Outcome within each AOP. The highlighted "No path" bar flags AOPs whose MIE and AO aren't connected through their KERs.

Methodology

What this measures: For each AOP with both a Molecular Initiating Event and an Adverse Outcome, the shortest number of KER steps from any MIE to any AO through that AOP's own Key Event Relationships. AOPs whose MIE and AO are not connected via their KERs fall in 'No path' (highlighted) — a structural-completeness signal.

Data source: SPARQL retrieves each AOP's MIE(s) and AO(s), and separately its KER edges (upstream→downstream KE). Python builds each AOP's directed subgraph and BFS-computes the shortest MIE→AO distance, then bins (1..6, 7+, No path).

Known limitations: Uses only KERs declared on the AOP (aopo:has_key_event_relationship); a KER shared across AOPs contributes to each. AOPs missing a MIE or an AO are excluded. Path length counts KER hops, not weight of evidence.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
AOP endpoints (MIE and AO)
SELECT ?aop ?mie ?ao
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://aopkb.org/aop_ontology#has_molecular_initiating_event> ?mie ;
         <http://aopkb.org/aop_ontology#has_adverse_outcome> ?ao .
  }
}
Per-AOP KER edges (upstream KE -> downstream KE)
SELECT ?aop ?up ?down
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://aopkb.org/aop_ontology#has_key_event_relationship> ?ker .
    ?ker <http://aopkb.org/aop_ontology#has_upstream_key_event> ?up ;
         <http://aopkb.org/aop_ontology#has_downstream_key_event> ?down .
  }
}

Breakdown Analysis

Key Events by Biological Level of Organization

Distribution of Key Events across biological levels of organization (molecular, cellular, tissue, organ, organism, population). Shows which biological scales are most studied.

Methodology

What this measures: Distribution of Key Events across biological levels of organization (molecular, cellular, tissue, organ, organism, population). Reveals which biological scales are most represented in the AOP-Wiki knowledge base.

Data source: SPARQL query counts distinct Key Events grouped by their Level of Biological Organisation annotation (NCI Thesaurus property C25664). KEs without this annotation appear as 'Not Annotated'.

Known limitations: Not all Key Events have biological level annotations. Unannotated KEs appear as 'Not Annotated' and may dominate the chart. The level vocabulary depends on how curators annotated each KE. Coverage varies by version as curation progresses.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Count KEs per biological level of organisation
SELECT ?level_label (COUNT(DISTINCT ?ke) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent .
    OPTIONAL {
      ?ke <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25664> ?level_obj .
    }
    BIND(IF(BOUND(?level_obj), STR(?level_obj), "Not Annotated") AS ?level_label)
  }
}
GROUP BY ?level_label
ORDER BY DESC(?count)

Taxonomic Group Distribution Across AOPs

Which taxonomic groups (species/organisms) are most represented across AOPs in the current database. Shows the top taxonomic applicability annotations.

Methodology

What this measures: Top 25 species annotated as taxonomically applicable to AOPs (via the `NCBITAXON/131567` predicate). Resolved from NCBI taxon IDs to scientific names using a curated label map (`static/data/ncbi_taxon_labels.json`, 133 species covering everything currently in AOP-Wiki). Both AOP-Wiki storage formats — modern `NCBITAXON/<id>` URIs and legacy `WCS_<id>` strings — are coalesced into a single bar per species. New species not yet in the label map render as `Taxon <id> (unresolved)` so they remain visible.

Data source: SPARQL pulls raw (`?aop`, `?taxon_obj`) pairs filtered to AOPs and the `131567` predicate, then Python aggregates per NCBI taxon ID (regex `(\d+)$` against the raw value handles both URI and `WCS_` formats). `WikiUser_<id>` strings — curator user IDs that historically leaked into the taxonomy field, not taxa — are filtered out; their count is logged for curation follow-up.

Known limitations: Only AOPs with an explicit `131567` annotation are shown. A single AOP may carry multiple taxa (it will then contribute to multiple bars). The label map is curated, not live: new species in future snapshots will display as `Taxon NNNN (unresolved)` until the map is regenerated. `WikiUser_NN` entries are filtered out, masking a known data-quality issue — see the dashboard logs for the count.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Pull raw (AOP, taxon) pairs for client-side aggregation
SELECT ?aop ?taxon_obj
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    ?aop <http://purl.bioontology.org/ontology/NCBITAXON/131567> ?taxon_obj .
  }
}

Entity Counts by OECD Status

Breakdown of AOPs, Key Events, and Key Event Relationships by their OECD approval status. Shows the curation maturity distribution across entity types.

Methodology

What this measures: Breaks down entity counts (AOPs, Key Events, Key Event Relationships) by OECD approval status category. Shows how curation maturity is distributed across entity types.

Data source: Three separate SPARQL queries count AOPs directly by their OECD Status annotation (NCI Thesaurus C25688), and KEs/KERs by the OECD status of their parent AOPs (via aopo:has_key_event and aopo:has_key_event_relationship relationships).

Known limitations: KEs and KERs inherit OECD status from their parent AOPs. A KE belonging to AOPs with different statuses appears in each status group. AOPs without an OECD status annotation appear as 'No Status'. Status categories reflect what is present in the data.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Count AOPs per OECD status
SELECT ?status_label (COUNT(DISTINCT ?aop) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
    }
    BIND(IF(BOUND(?status_obj), STR(?status_obj), "No Status") AS ?status_label)
  }
}
GROUP BY ?status_label
Count KEs per parent-AOP OECD status
SELECT ?status_label (COUNT(DISTINCT ?ke) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
    }
    BIND(IF(BOUND(?status_obj), STR(?status_obj), "No Status") AS ?status_label)
  }
}
GROUP BY ?status_label
Count KERs per parent-AOP OECD status
SELECT ?status_label (COUNT(DISTINCT ?ker) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event_relationship ?ker .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
    }
    BIND(IF(BOUND(?status_obj), STR(?status_obj), "No Status") AS ?status_label)
  }
}
GROUP BY ?status_label

Coverage Analysis

AOP Coverage of Organ Systems

AOP coverage of 13 high-level organ systems, classified per AOP from curated anatomy, biological-process, and title signals. Use the toggles below to change which Key Events count and to switch between absolute counts and share of AOPs. See Methodology for how each signal is derived and what the CSV contains.

Scope:
View:
Methodology

What this measures: Same per-AOP hybrid classification as before, but with two toggles. Scope picks which Key Events count toward Signals A, A' and B: all KEs (broadest), Apical KEs only (Tissue/Organ/Individual/Population), or just the Adverse Outcome (regulatory endpoint). Signal C (AOP title regex) is kept regardless of scope. View switches between absolute AOP counts and share of all AOPs in the snapshot. The four legacy panels collapse into this single view; the bar below mirrors the same scope so you read one coherent slice across the section.

Data source: One SPARQL query per snapshot pulls all AOPs, their member KEs, OrganContext / CellTypeContext / hasObject / hasProcess IRIs, and AOP titles. The UBERON / CL / GO term-to-bucket map is built offline by scripts/build_organ_system_cache.py — it queries Ubergraph (an integrated UBERON+CL+GO+RO ontology endpoint) and writes static/data/organ_system_cache.json. The dashboard loads that cache at startup. Click 'Browse full curated mapping (JSON)' to see the resolved term-to-bucket map, the bucket anchors, and the override layer.

How the mapping works:

  1. Step 1 — SPARQL pull. One query per snapshot collects every AOP, its member Key Events, and any IRIs in the four annotation predicates: aopo:OrganContext, aopo:CellTypeContext, aopo:hasObject (inside aopo:hasBiologicalEvent), and aopo:hasProcess. AOP titles are pulled too — Signal C runs on them.
  2. Step 2 — UBERON / CL → bucket via Ubergraph (Signals A and A'). Anatomical IRIs are resolved by transitive (part_of | subClassOf)* closure to a small set of anchor IRIs per bucket — e.g. Nervous = UBERON_0001016 (nervous system); Hepatobiliary = liver + gallbladder + bile duct. Anchor-specificity pruning then drops reached anchors that are themselves ancestors of other reached anchors — so liver (UBERON:0002107) classifies only as Hepatobiliary even though UBERON places it under both digestive and endocrine systems. The lookup runs offline against https://ubergraph.apps.renci.org/sparql and is cached at static/data/organ_system_cache.json. Intentional dual-anchors are preserved: pancreas (UBERON_0001264) is its own anchor for both Digestive and Endocrine because it is both an exocrine digestive organ and an endocrine gland — so pancreas-derived terms classify to both.
  3. Step 3 — Process / Phenotype signal (Signal B). Three resolution paths into the same 13 buckets: (a) GO biological-process via RO:0002296 (results_in_development_of) → UBERON anchor (~3 terms); (b) HP and MP phenotypes via their rdfs:label matched against the Signal-C bucket regex — this replaces the previous UPHENO:0000001 approach, which Ubergraph materialises as an over-broad anatomy closure (hepatic-steatosis and microvesicular-hepatic-steatosis return the same 35 UBERON targets including endocrine and digestive system). Label-regex gives mono-organ classification when the phenotype name names an organ system. Phenotypes whose label contains no organ keyword (e.g. behavioural HP/MP terms) are NOT classified.
  4. Step 4 — Keyword regex on AOP titles (Signal C, exploratory). Each AOP title is scanned with a bucket-specific regex set, e.g. Nervous = \bneuro, \bsynap, \bmyelin, …; Hepatobiliary = \bhepat, \bliver\b, \bsteatosis\b, …; Cardiovascular = \bcardio, \bheart\b, …. Lowest-confidence signal, shown in grey throughout.
  5. Step 5 — Editorial override layer. A small project-owned override layer is applied after the ontology resolution to handle terms where the project's framing diverges from UBERON. Currently a single entry: blood (UBERON_0000178) is reported under both Cardiovascular and Immune/Haematopoietic — UBERON places it structurally under haematopoietic only, but toxicology practice frames blood-borne effects as cardiovascular too.
  6. Step 6 — Aggregate per AOP. An AOP is counted in a bucket if any of its member KEs (Signals A/A'/B) or its own title (Signal C) classifies into it. The bar segment colour shows the highest-confidence signal that classified the AOP–bucket pair (A > A' > B > C). AOPs can belong to multiple buckets — see the Multi-organ AOPs plot.
  7. Step 7 — Source code. Full classifier (~300 LOC, covering classify_anatomy, classify_process, classify_text, the SIGNAL_ORDER reducer, the apical-level filter, and AO-only filter): see plots/organ_systems.py on GitHub. The ontology cache builder is at scripts/build_organ_system_cache.py.

Known limitations: The bucket anchors are curated (13 buckets × ~1-4 anchors each). UBERON's own structural decisions drive the rest — e.g. blood is haematopoietic, not cardiovascular, in UBERON; we override that one case. Signal B only classifies 3 GO BPs in the current snapshot because the RO:0002296 axiom is rare in GO — the trade-off vs a hand-curated bridge is conscious. Signal C is regex on free-text titles, shown in grey; not for regulatory claims. The 13 buckets are coarse — sub-organ resolution is collapsed for the headline view but preserved in the CSV download.

Browse full curated mapping (JSON)

Consolidated query (all signals in one pull)
SELECT ?aop ?aop_title ?ao ?ke ?organ ?cell ?obj ?proc
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://purl.org/dc/elements/1.1/title> ?aop_title ;
         aopo:has_key_event ?ke .
    OPTIONAL { ?aop aopo:has_adverse_outcome ?ao . }
    OPTIONAL { ?ke aopo:OrganContext ?organ . }
    OPTIONAL { ?ke aopo:CellTypeContext ?cell . }
    OPTIONAL {
      ?ke aopo:hasBiologicalEvent ?be .
      OPTIONAL { ?be aopo:hasObject ?obj . }
      OPTIONAL { ?be aopo:hasProcess ?proc . }
    }
  }
}
Signal A — curated anatomy on KEs
# Signal A only — curated anatomy directly on Key Events
SELECT ?aop ?ke ?organ_or_cell
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    { ?ke aopo:OrganContext   ?organ_or_cell . }
    UNION
    { ?ke aopo:CellTypeContext ?organ_or_cell . }
  }
}
Signal A' — UBERON/CL via hasObject
# Signal A' only — UBERON / CL appearing as hasObject inside a biological event
SELECT ?aop ?ke ?obj
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    ?ke  aopo:hasBiologicalEvent ?be .
    ?be  aopo:hasObject ?obj .
    FILTER( STRSTARTS(STR(?obj), "http://purl.obolibrary.org/obo/UBERON_")
         || STRSTARTS(STR(?obj), "http://purl.obolibrary.org/obo/CL_") )
  }
}
Signal B — GO biological-process terms
# Signal B only — GO biological-process terms (bridged to buckets in Python)
SELECT ?aop ?ke ?proc
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    ?ke  aopo:hasBiologicalEvent ?be .
    ?be  aopo:hasProcess ?proc .
    FILTER( STRSTARTS(STR(?proc), "http://purl.obolibrary.org/obo/GO_") )
  }
}
Signal C — fetch titles for Python regex
# Signal C precursor — fetch AOP and AO titles (Signal C runs as regex in
# Python against these literals; the curated patterns are listed at
# /api/organ-system-buckets under "keyword_patterns").
SELECT ?aop ?aop_title ?ao ?ao_title
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://purl.org/dc/elements/1.1/title> ?aop_title .
    OPTIONAL {
      ?aop aopo:has_adverse_outcome ?ao .
      OPTIONAL { ?ao <http://purl.org/dc/elements/1.1/title> ?ao_title . }
    }
  }
}

Multi-organ AOPs

How many distinct organ systems each AOP classifies into across Signals A, A′, B and C. Single-organ AOPs sit in the “1” column; multi-organ pathways pile into 2, 3, …. The raw-data toggle lists every multi-organ AOP with its full bucket set; the CSV download gives the same per-AOP detail.

Methodology

What this measures: Distribution of AOPs by the number of distinct organ systems they classify into across Signals A, A', B, and C (excluding the No-annotation sentinel). Answers 'how many AOPs are single-organ vs multi-organ?'. The raw-data toggle lists every multi-organ AOP with its full bucket set, so you can inspect which pathways span e.g. Endocrine + Reproductive + Nervous.

Data source: Reuses the same SPARQL pull and ontology resolution as the AOP Coverage plot. For each AOP we count the number of distinct organ-system buckets it lands in via any of the four signals.

How the mapping works:

  1. Step 1 — SPARQL pull. One query per snapshot collects every AOP, its member Key Events, and any IRIs in the four annotation predicates: aopo:OrganContext, aopo:CellTypeContext, aopo:hasObject (inside aopo:hasBiologicalEvent), and aopo:hasProcess. AOP titles are pulled too — Signal C runs on them.
  2. Step 2 — UBERON / CL → bucket via Ubergraph (Signals A and A'). Anatomical IRIs are resolved by transitive (part_of | subClassOf)* closure to a small set of anchor IRIs per bucket — e.g. Nervous = UBERON_0001016 (nervous system); Hepatobiliary = liver + gallbladder + bile duct. Anchor-specificity pruning then drops reached anchors that are themselves ancestors of other reached anchors — so liver (UBERON:0002107) classifies only as Hepatobiliary even though UBERON places it under both digestive and endocrine systems. The lookup runs offline against https://ubergraph.apps.renci.org/sparql and is cached at static/data/organ_system_cache.json. Intentional dual-anchors are preserved: pancreas (UBERON_0001264) is its own anchor for both Digestive and Endocrine because it is both an exocrine digestive organ and an endocrine gland — so pancreas-derived terms classify to both.
  3. Step 3 — Process / Phenotype signal (Signal B). Three resolution paths into the same 13 buckets: (a) GO biological-process via RO:0002296 (results_in_development_of) → UBERON anchor (~3 terms); (b) HP and MP phenotypes via their rdfs:label matched against the Signal-C bucket regex — this replaces the previous UPHENO:0000001 approach, which Ubergraph materialises as an over-broad anatomy closure (hepatic-steatosis and microvesicular-hepatic-steatosis return the same 35 UBERON targets including endocrine and digestive system). Label-regex gives mono-organ classification when the phenotype name names an organ system. Phenotypes whose label contains no organ keyword (e.g. behavioural HP/MP terms) are NOT classified.
  4. Step 4 — Keyword regex on AOP titles (Signal C, exploratory). Each AOP title is scanned with a bucket-specific regex set, e.g. Nervous = \bneuro, \bsynap, \bmyelin, …; Hepatobiliary = \bhepat, \bliver\b, \bsteatosis\b, …; Cardiovascular = \bcardio, \bheart\b, …. Lowest-confidence signal, shown in grey throughout.
  5. Step 5 — Editorial override layer. A small project-owned override layer is applied after the ontology resolution to handle terms where the project's framing diverges from UBERON. Currently a single entry: blood (UBERON_0000178) is reported under both Cardiovascular and Immune/Haematopoietic — UBERON places it structurally under haematopoietic only, but toxicology practice frames blood-borne effects as cardiovascular too.
  6. Step 6 — Aggregate per AOP. An AOP is counted in a bucket if any of its member KEs (Signals A/A'/B) or its own title (Signal C) classifies into it. The bar segment colour shows the highest-confidence signal that classified the AOP–bucket pair (A > A' > B > C). AOPs can belong to multiple buckets — see the Multi-organ AOPs plot.
  7. Step 7 — Source code. Full classifier (~300 LOC, covering classify_anatomy, classify_process, classify_text, the SIGNAL_ORDER reducer, the apical-level filter, and AO-only filter): see plots/organ_systems.py on GitHub. The ontology cache builder is at scripts/build_organ_system_cache.py.

Known limitations: An AOP appearing in multiple buckets does not necessarily mean it is biologically multi-system — it can also mean Signal C (keywords) added an extra bucket on top of Signal A's curated anatomy. The CSV preserves which signal(s) contributed each bucket so this is auditable.

Browse full curated mapping (JSON)

Consolidated query (all signals in one pull)
SELECT ?aop ?aop_title ?ao ?ke ?organ ?cell ?obj ?proc
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://purl.org/dc/elements/1.1/title> ?aop_title ;
         aopo:has_key_event ?ke .
    OPTIONAL { ?aop aopo:has_adverse_outcome ?ao . }
    OPTIONAL { ?ke aopo:OrganContext ?organ . }
    OPTIONAL { ?ke aopo:CellTypeContext ?cell . }
    OPTIONAL {
      ?ke aopo:hasBiologicalEvent ?be .
      OPTIONAL { ?be aopo:hasObject ?obj . }
      OPTIONAL { ?be aopo:hasProcess ?proc . }
    }
  }
}
Signal A — curated anatomy on KEs
# Signal A only — curated anatomy directly on Key Events
SELECT ?aop ?ke ?organ_or_cell
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    { ?ke aopo:OrganContext   ?organ_or_cell . }
    UNION
    { ?ke aopo:CellTypeContext ?organ_or_cell . }
  }
}
Signal A' — UBERON/CL via hasObject
# Signal A' only — UBERON / CL appearing as hasObject inside a biological event
SELECT ?aop ?ke ?obj
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    ?ke  aopo:hasBiologicalEvent ?be .
    ?be  aopo:hasObject ?obj .
    FILTER( STRSTARTS(STR(?obj), "http://purl.obolibrary.org/obo/UBERON_")
         || STRSTARTS(STR(?obj), "http://purl.obolibrary.org/obo/CL_") )
  }
}
Signal B — GO biological-process terms
# Signal B only — GO biological-process terms (bridged to buckets in Python)
SELECT ?aop ?ke ?proc
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    ?ke  aopo:hasBiologicalEvent ?be .
    ?be  aopo:hasProcess ?proc .
    FILTER( STRSTARTS(STR(?proc), "http://purl.obolibrary.org/obo/GO_") )
  }
}
Signal C — fetch titles for Python regex
# Signal C precursor — fetch AOP and AO titles (Signal C runs as regex in
# Python against these literals; the curated patterns are listed at
# /api/organ-system-buckets under "keyword_patterns").
SELECT ?aop ?aop_title ?ao ?ao_title
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://purl.org/dc/elements/1.1/title> ?aop_title .
    OPTIONAL {
      ?aop aopo:has_adverse_outcome ?ao .
      OPTIONAL { ?ao <http://purl.org/dc/elements/1.1/title> ?ao_title . }
    }
  }
}

AOP Life-Stage Applicability

Distribution of AOPs across declared life-stage applicability labels (e.g. Adult, Juvenile, Embryonic). Surfaces gaps in coverage of vulnerable populations. Sex applicability is not exposed as a structured RDF predicate; see methodology note.

Methodology

What this measures: Counts how many AOPs declare each life-stage applicability label (Adult, Juvenile, Embryo, All life stages, etc.) via aopo:LifeStageContext. An AOP with no LifeStageContext literal at all is reported as Not specified. AOPs with multiple life-stage labels contribute once per label.

Data source: Single SPARQL query selecting every aopo:AdverseOutcomePathway and OPTIONAL aopo:LifeStageContext literal in the selected snapshot. Aggregation happens in Python after deduplicating per AOP.

Known limitations: aopo:LifeStageContext values are free-text literals chosen by curators — there is no fixed controlled vocabulary, so spelling variants (e.g. "Adult" vs "Adults") appear as distinct bars. Sex applicability is NOT exposed as a structured RDF predicate; it is only described inside free-text aopo:AopContext literals and cannot be machine-aggregated reliably. The dashboard surfaces this gap rather than guessing.

View SPARQL Query
SELECT ?aop ?life_stage
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL { ?aop aopo:LifeStageContext ?life_stage . }
  }
}

MIE Stressor Coverage

Molecular Initiating Events binned by how many distinct stressors are annotated on the AOPs that contain them. The highlighted "0 (no stressor)" bar is the curation gap. Stressors are AOP-level annotations attributed to the MIE via shared AOPs.

Methodology

What this measures: Bins Molecular Initiating Events by how many distinct stressors are annotated on the AOPs that contain them. The '0 (no stressor)' bar (highlighted) is the curation gap — MIEs with no known chemical/biological trigger. The subtitle reports the overall share of MIEs with at least one associated stressor.

Data source: SPARQL query, per MIE, counts distinct stressors (nci:C54571) on the AOPs referencing that MIE via aopo:has_molecular_initiating_event. MIEs are then binned in Python (0, 1, 2, 3-5, 6-10, 11+).

Known limitations: Stressors are annotated at the AOP level, not directly on the MIE, so coverage is inferred through shared AOPs — an AOP's stressors are attributed to its MIE. An MIE shared by several AOPs aggregates all their stressors. Reflects annotation presence, not stressor-MIE causal specificity.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distinct stressors per MIE (via shared AOPs)
SELECT ?mie (COUNT(DISTINCT ?stressor) AS ?stressor_count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         <http://aopkb.org/aop_ontology#has_molecular_initiating_event> ?mie .
    OPTIONAL { ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C54571> ?stressor . }
  }
}
GROUP BY ?mie
ORDER BY DESC(?stressor_count)

Domain Lens

How strong is AOP-Wiki in one biological field? A domain here is a handful of ontology root terms — anatomy, physiological process, development, abnormal phenotype — expanded to their descendants and intersected with the terms Key Events actually carry. No keyword list and no per-AOP curation: changing the roots changes the domain. All three charts below follow the selector.

AOP Coverage by Biological Domain

AOPs with at least one Key Event annotated with a term from each domain’s ontology closure, so a field can be read against its neighbours rather than against an absolute number nobody can calibrate. Domains overlap — an AOP annotated in two of them counts in both. An AOP that describes a domain only in free text is not counted, which is the same blind spot the coverage-holes chart reports from the other side.

Methodology

What this measures: AOPs with at least one Key Event annotated with a term from each domain's ontology closure. A domain is defined by five root terms — anatomy, physiological process, development, mouse phenotype and human phenotype — expanded to their descendants; the cardiovascular preset is UBERON:0001009, GO:0003013, GO:0072359, MP:0005385 and HP:0001626. No keyword list and no per-AOP curation is involved. On the 2026-07-01 snapshot: Nervous 122 AOPs (20.7% of 588), Hepatobiliary 88, Reproductive 76, Cardiovascular 41 (7.0%), Respiratory 26, Renal/Urinary 22. The bar for the selected domain is highlighted; the others stay visible so a field can be read against its neighbours.

Data source: Two things are combined. The domain vocabulary is a committed offline artefact, static/data/domain_lens_cache.json, built by scripts/build_domain_lens_cache.py: it asks Ubergraph for the subClassOf / part_of descendants of each domain root (one hop against the pre-materialised <http://reasoner.renci.org/redundant> closure graph), restricts each root's closure to its own namespace family (UBERON roots admit UBERON and CL; GO, MP and HP roots admit only themselves), drops the Drosophila-derived UBERON_6* range, and intersects the result with every OBO term annotated on a Key Event in ANY snapshot. Ubergraph is never contacted at request time. The AOP-level query then runs against the selected snapshot only, so the version selector works. Term usage is read from aopo:OrganContext, aopo:CellTypeContext, obo:PATO_0001241 (biological object) and obo:GO_0008150 (biological process) — deliberately not the predicate set the ontology-usage plots query, which goes through aopo:hasBiologicalEvent only and never sees OrganContext or CellTypeContext.

Known limitations: A domain count is a count of machine-readable annotation, not of biology. An AOP that describes cardiac injury only in free text is not in the cohort, so these numbers are a floor. Validated against a JRC expert review of 80 candidate cardiovascular AOPs (56 rated core) on the 2026-07-01 snapshot: the lens returns 41 AOPs, of which 28 were rated core — precision 28/41, recall 28/56. The recall gap is mostly unannotated AOPs; the precision gap is AOPs that annotate a downstream cardiac Key Event while being about something else (three renal-failure AOPs) — a domain hit anywhere in the pathway counts, and role is not used to filter because role belongs to the (AOP, Key Event) pair rather than to the Key Event. Domains overlap and an AOP can be counted in several. This is NOT the organ-system coverage classifier used higher up the page: that one is deliberately more generous (editorial overrides, UPHENO phenotype closure) and returns 63 cardiovascular AOPs at precision 29/63 — better for a coverage bar, worse for a domain scope. MeSH is not covered: it is absent from Ubergraph, and of the 70 MeSH descriptors AOP-Wiki uses only 3 sit under tree number C14, every one of them on an AOP the OBO closure already reaches. The six presets are the domains with a clean root set in all four ontologies; adding one means adding roots to the builder, not curating AOPs.

Browse full curated mapping (JSON)

Resolve latest snapshot graph URI
SELECT ?graph WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
} GROUP BY ?graph ORDER BY DESC(?graph) LIMIT 1
Total AOPs in the snapshot (denominator for the share)
SELECT (COUNT(DISTINCT ?aop) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> { ?aop a aopo:AdverseOutcomePathway . }
}
AOPs annotated with a Cardiovascular term (one VALUES block per domain)
SELECT DISTINCT ?aop ?term
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?term {
      <http://purl.obolibrary.org/obo/CL_0000071> <http://purl.obolibrary.org/obo/CL_0000746> <http://purl.obolibrary.org/obo/GO_0001525>
      <http://purl.obolibrary.org/obo/GO_0008015> <http://purl.obolibrary.org/obo/GO_0042311> <http://purl.obolibrary.org/obo/GO_0043114>
      <http://purl.obolibrary.org/obo/HP_0001695> <http://purl.obolibrary.org/obo/HP_0002621> <http://purl.obolibrary.org/obo/MP_0000231>
      <http://purl.obolibrary.org/obo/MP_0001544> <http://purl.obolibrary.org/obo/MP_0001863> <http://purl.obolibrary.org/obo/MP_0001914>
      <http://purl.obolibrary.org/obo/MP_0003070> <http://purl.obolibrary.org/obo/MP_0004878> <http://purl.obolibrary.org/obo/MP_0010019>
      <http://purl.obolibrary.org/obo/MP_0010128> <http://purl.obolibrary.org/obo/UBERON_0000948> <http://purl.obolibrary.org/obo/UBERON_0001009>
      <http://purl.obolibrary.org/obo/UBERON_0001981> <http://purl.obolibrary.org/obo/UBERON_0002049> <http://purl.obolibrary.org/obo/UBERON_0004535>
      <http://purl.obolibrary.org/obo/UBERON_0004638> <http://purl.obolibrary.org/obo/UBERON_0007798> <http://purl.obolibrary.org/obo/UBERON_0035753>
    }
    ?ke a aopo:KeyEvent ; ?p ?term .
    FILTER(?p IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
    ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?ke .
  }
}

Curation Depth by Property Tier

The selected domain against all AOPs, scored with the same property tiers as the property-presence charts (Essential, Content, Context, Assessment, Metadata; each scoped to the entity types it can appear on). Placeholder values such as &nbsp;, TBD and N/A are stripped before counting. Presence is not quality: a filled field can still be wrong or out of date.

Methodology

What this measures: Property presence for the selected domain against all AOPs, averaged over the properties in each tier of property_labels.csv (Essential, Content, Context, Assessment, Metadata) and scoped by that file's applies_to column, so every property is only scored on entity types it can legitimately appear on. Reusing the dashboard's own tiers rather than a bespoke basket of counts changed the cardiovascular conclusion: on 2026-07-01 the 41-AOP subset sits above the wiki average on every AOP-level tier — Assessment 52.2% against 31.3%, Context 57.1% against 39.5% — while its Key Events lead by only 4 points on Essential. The evidence rationale is written; the structured fields lag. An ad-hoc measure (mean KE, mean KER, description coverage) reported the same subset as below average across the board.

Data source: Two things are combined. The domain vocabulary is a committed offline artefact, static/data/domain_lens_cache.json, built by scripts/build_domain_lens_cache.py: it asks Ubergraph for the subClassOf / part_of descendants of each domain root (one hop against the pre-materialised <http://reasoner.renci.org/redundant> closure graph), restricts each root's closure to its own namespace family (UBERON roots admit UBERON and CL; GO, MP and HP roots admit only themselves), drops the Drosophila-derived UBERON_6* range, and intersects the result with every OBO term annotated on a Key Event in ANY snapshot. Ubergraph is never contacted at request time. The AOP-level query then runs against the selected snapshot only, so the version selector works. Term usage is read from aopo:OrganContext, aopo:CellTypeContext, obo:PATO_0001241 (biological object) and obo:GO_0008150 (biological process) — deliberately not the predicate set the ontology-usage plots query, which goes through aopo:hasBiologicalEvent only and never sees OrganContext or CellTypeContext. Presence is measured per entity and then averaged across the properties in a tier, the same shape the property-presence charts use, restricted to a cohort. Placeholder values are stripped before counting: HTML tags and the entities the rich-text editor emits (&nbsp; and friends) are removed, and a residue equal to one of -, ., ?, N/A, TBD, TBA, x or 'to be determined' counts as absent. Only literals shorter than 64 characters are returned to be tested; anything longer is content by construction. Curator answers such as 'None identified' or 'No data.' are NOT stripped — they are statements about the science, not formatting artefacts.

Known limitations: A domain count is a count of machine-readable annotation, not of biology. An AOP that describes cardiac injury only in free text is not in the cohort, so these numbers are a floor. Validated against a JRC expert review of 80 candidate cardiovascular AOPs (56 rated core) on the 2026-07-01 snapshot: the lens returns 41 AOPs, of which 28 were rated core — precision 28/41, recall 28/56. The recall gap is mostly unannotated AOPs; the precision gap is AOPs that annotate a downstream cardiac Key Event while being about something else (three renal-failure AOPs) — a domain hit anywhere in the pathway counts, and role is not used to filter because role belongs to the (AOP, Key Event) pair rather than to the Key Event. Domains overlap and an AOP can be counted in several. This is NOT the organ-system coverage classifier used higher up the page: that one is deliberately more generous (editorial overrides, UPHENO phenotype closure) and returns 63 cardiovascular AOPs at precision 29/63 — better for a coverage bar, worse for a domain scope. MeSH is not covered: it is absent from Ubergraph, and of the 70 MeSH descriptors AOP-Wiki uses only 3 sit under tree number C14, every one of them on an AOP the OBO closure already reaches. The six presets are the domains with a clean root set in all four ontologies; adding one means adding roots to the builder, not curating AOPs. Presence is not quality: a field can be filled and still be wrong, out of date or a single word. The placeholder strip is deliberately conservative and typographic, so some semantically-empty text still counts as present. KE Assessment reads 0% for every cohort because aopo:has_evidence is empty on Key Events throughout the RDF, and KER Content reads 100% because a Key Event Relationship cannot exist without its upstream and downstream Key Events — both are properties of the export, not of the domain.

Browse full curated mapping (JSON)

Resolve latest snapshot graph URI
SELECT ?graph WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
} GROUP BY ?graph ORDER BY DESC(?graph) LIMIT 1
AOPs annotated with a Cardiovascular term (one VALUES block per domain)
SELECT DISTINCT ?aop ?term
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?term {
      <http://purl.obolibrary.org/obo/CL_0000071> <http://purl.obolibrary.org/obo/CL_0000746> <http://purl.obolibrary.org/obo/GO_0001525>
      <http://purl.obolibrary.org/obo/GO_0008015> <http://purl.obolibrary.org/obo/GO_0042311> <http://purl.obolibrary.org/obo/GO_0043114>
      <http://purl.obolibrary.org/obo/HP_0001695> <http://purl.obolibrary.org/obo/HP_0002621> <http://purl.obolibrary.org/obo/MP_0000231>
      <http://purl.obolibrary.org/obo/MP_0001544> <http://purl.obolibrary.org/obo/MP_0001863> <http://purl.obolibrary.org/obo/MP_0001914>
      <http://purl.obolibrary.org/obo/MP_0003070> <http://purl.obolibrary.org/obo/MP_0004878> <http://purl.obolibrary.org/obo/MP_0010019>
      <http://purl.obolibrary.org/obo/MP_0010128> <http://purl.obolibrary.org/obo/UBERON_0000948> <http://purl.obolibrary.org/obo/UBERON_0001009>
      <http://purl.obolibrary.org/obo/UBERON_0001981> <http://purl.obolibrary.org/obo/UBERON_0002049> <http://purl.obolibrary.org/obo/UBERON_0004535>
      <http://purl.obolibrary.org/obo/UBERON_0004638> <http://purl.obolibrary.org/obo/UBERON_0007798> <http://purl.obolibrary.org/obo/UBERON_0035753>
    }
    ?ke a aopo:KeyEvent ; ?p ?term .
    FILTER(?p IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
    ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?ke .
  }
}
Key Events and Key Event Relationships of the domain cohort
SELECT DISTINCT ?ke ?ker
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?term {
      <http://purl.obolibrary.org/obo/CL_0000071> <http://purl.obolibrary.org/obo/CL_0000746> <http://purl.obolibrary.org/obo/GO_0001525>
      <http://purl.obolibrary.org/obo/GO_0008015> <http://purl.obolibrary.org/obo/GO_0042311> <http://purl.obolibrary.org/obo/GO_0043114>
      <http://purl.obolibrary.org/obo/HP_0001695> <http://purl.obolibrary.org/obo/HP_0002621> <http://purl.obolibrary.org/obo/MP_0000231>
      <http://purl.obolibrary.org/obo/MP_0001544> <http://purl.obolibrary.org/obo/MP_0001863> <http://purl.obolibrary.org/obo/MP_0001914>
      <http://purl.obolibrary.org/obo/MP_0003070> <http://purl.obolibrary.org/obo/MP_0004878> <http://purl.obolibrary.org/obo/MP_0010019>
      <http://purl.obolibrary.org/obo/MP_0010128> <http://purl.obolibrary.org/obo/UBERON_0000948> <http://purl.obolibrary.org/obo/UBERON_0001009>
      <http://purl.obolibrary.org/obo/UBERON_0001981> <http://purl.obolibrary.org/obo/UBERON_0002049> <http://purl.obolibrary.org/obo/UBERON_0004535>
      <http://purl.obolibrary.org/obo/UBERON_0004638> <http://purl.obolibrary.org/obo/UBERON_0007798> <http://purl.obolibrary.org/obo/UBERON_0035753>
    }
    ?domain_ke a aopo:KeyEvent ; ?p ?term .
    FILTER(?p IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
    ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?domain_ke .
    OPTIONAL { ?aop aopo:has_key_event ?ke . }
    OPTIONAL { ?aop aopo:has_key_event_relationship ?ker . }
  }
}
Property presence on the cohort's AOPs (repeated per entity type and property tier; short literals are returned so placeholders can be stripped)
SELECT DISTINCT ?s ?p ?short
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?term {
      <http://purl.obolibrary.org/obo/CL_0000071> <http://purl.obolibrary.org/obo/CL_0000746> <http://purl.obolibrary.org/obo/GO_0001525>
      <http://purl.obolibrary.org/obo/GO_0008015> <http://purl.obolibrary.org/obo/GO_0042311> <http://purl.obolibrary.org/obo/GO_0043114>
      <http://purl.obolibrary.org/obo/HP_0001695> <http://purl.obolibrary.org/obo/HP_0002621> <http://purl.obolibrary.org/obo/MP_0000231>
      <http://purl.obolibrary.org/obo/MP_0001544> <http://purl.obolibrary.org/obo/MP_0001863> <http://purl.obolibrary.org/obo/MP_0001914>
      <http://purl.obolibrary.org/obo/MP_0003070> <http://purl.obolibrary.org/obo/MP_0004878> <http://purl.obolibrary.org/obo/MP_0010019>
      <http://purl.obolibrary.org/obo/MP_0010128> <http://purl.obolibrary.org/obo/UBERON_0000948> <http://purl.obolibrary.org/obo/UBERON_0001009>
      <http://purl.obolibrary.org/obo/UBERON_0001981> <http://purl.obolibrary.org/obo/UBERON_0002049> <http://purl.obolibrary.org/obo/UBERON_0004535>
      <http://purl.obolibrary.org/obo/UBERON_0004638> <http://purl.obolibrary.org/obo/UBERON_0007798> <http://purl.obolibrary.org/obo/UBERON_0035753>
    }
    ?domain_ke a aopo:KeyEvent ; ?annotates ?term .
    FILTER(?annotates IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
    ?s a aopo:AdverseOutcomePathway ; aopo:has_key_event ?domain_ke .
    VALUES ?p {
      <http://purl.org/dc/elements/1.1/title>
      <http://purl.org/dc/terms/abstract>
      <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25217>
      <http://aopkb.org/aop_ontology#AopContext>
    }
    ?s ?p ?o .
  }
  BIND(IF(isLiteral(?o) && STRLEN(STR(?o)) < 64, STR(?o), "") AS ?short)
}
The same property presence over every AOP in the snapshot (the baseline series)
SELECT DISTINCT ?s ?p ?short
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?s a aopo:AdverseOutcomePathway .
    VALUES ?p {
      <http://purl.org/dc/elements/1.1/title>
      <http://purl.org/dc/terms/abstract>
      <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25217>
      <http://aopkb.org/aop_ontology#AopContext>
    }
    ?s ?p ?o .
  }
  BIND(IF(isLiteral(?o) && STRLEN(STR(?o)) < 64, STR(?o), "") AS ?short)
}

OECD Status of Domain AOPs

How far the domain’s AOPs have travelled through the OECD workflow, as a share of each cohort so the two sizes stay comparable. No status means the AOP never entered the OECD process — it is displayed, never scored.

Methodology

What this measures: The OECD workflow status of the selected domain's AOPs against the whole wiki, as a share of each cohort because the two differ by an order of magnitude in size. On 2026-07-01 the 41 cardiovascular AOPs are 73.2% no status, 14.6% Under Development and 12.2% WPHA/WNT Endorsed, against 75.2% / 14.5% / 7.1% for all 588 — the domain is endorsed at nearly twice the background rate, off a base of five AOPs.

Data source: Two things are combined. The domain vocabulary is a committed offline artefact, static/data/domain_lens_cache.json, built by scripts/build_domain_lens_cache.py: it asks Ubergraph for the subClassOf / part_of descendants of each domain root (one hop against the pre-materialised <http://reasoner.renci.org/redundant> closure graph), restricts each root's closure to its own namespace family (UBERON roots admit UBERON and CL; GO, MP and HP roots admit only themselves), drops the Drosophila-derived UBERON_6* range, and intersects the result with every OBO term annotated on a Key Event in ANY snapshot. Ubergraph is never contacted at request time. The AOP-level query then runs against the selected snapshot only, so the version selector works. Term usage is read from aopo:OrganContext, aopo:CellTypeContext, obo:PATO_0001241 (biological object) and obo:GO_0008150 (biological process) — deliberately not the predicate set the ontology-usage plots query, which goes through aopo:hasBiologicalEvent only and never sees OrganContext or CellTypeContext. Status comes from ncit:C25688 on the AOP; an AOP with no such triple is reported as 'No Status' rather than dropped.

Known limitations: A domain count is a count of machine-readable annotation, not of biology. An AOP that describes cardiac injury only in free text is not in the cohort, so these numbers are a floor. Validated against a JRC expert review of 80 candidate cardiovascular AOPs (56 rated core) on the 2026-07-01 snapshot: the lens returns 41 AOPs, of which 28 were rated core — precision 28/41, recall 28/56. The recall gap is mostly unannotated AOPs; the precision gap is AOPs that annotate a downstream cardiac Key Event while being about something else (three renal-failure AOPs) — a domain hit anywhere in the pathway counts, and role is not used to filter because role belongs to the (AOP, Key Event) pair rather than to the Key Event. Domains overlap and an AOP can be counted in several. This is NOT the organ-system coverage classifier used higher up the page: that one is deliberately more generous (editorial overrides, UPHENO phenotype closure) and returns 63 cardiovascular AOPs at precision 29/63 — better for a coverage bar, worse for a domain scope. MeSH is not covered: it is absent from Ubergraph, and of the 70 MeSH descriptors AOP-Wiki uses only 3 sit under tree number C14, every one of them on an AOP the OBO closure already reaches. The six presets are the domains with a clean root set in all four ontologies; adding one means adding roots to the builder, not curating AOPs. Absence of a status overwhelmingly means the AOP never entered the OECD process, not that it failed one — 442 of 588 have none, so the field is displayed and never scored. Cohorts this small move several percentage points per AOP: one endorsement is 2.4% of the cardiovascular cohort.

Browse full curated mapping (JSON)

Resolve latest snapshot graph URI
SELECT ?graph WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
} GROUP BY ?graph ORDER BY DESC(?graph) LIMIT 1
AOPs annotated with a Cardiovascular term (one VALUES block per domain)
SELECT DISTINCT ?aop ?term
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    VALUES ?term {
      <http://purl.obolibrary.org/obo/CL_0000071> <http://purl.obolibrary.org/obo/CL_0000746> <http://purl.obolibrary.org/obo/GO_0001525>
      <http://purl.obolibrary.org/obo/GO_0008015> <http://purl.obolibrary.org/obo/GO_0042311> <http://purl.obolibrary.org/obo/GO_0043114>
      <http://purl.obolibrary.org/obo/HP_0001695> <http://purl.obolibrary.org/obo/HP_0002621> <http://purl.obolibrary.org/obo/MP_0000231>
      <http://purl.obolibrary.org/obo/MP_0001544> <http://purl.obolibrary.org/obo/MP_0001863> <http://purl.obolibrary.org/obo/MP_0001914>
      <http://purl.obolibrary.org/obo/MP_0003070> <http://purl.obolibrary.org/obo/MP_0004878> <http://purl.obolibrary.org/obo/MP_0010019>
      <http://purl.obolibrary.org/obo/MP_0010128> <http://purl.obolibrary.org/obo/UBERON_0000948> <http://purl.obolibrary.org/obo/UBERON_0001009>
      <http://purl.obolibrary.org/obo/UBERON_0001981> <http://purl.obolibrary.org/obo/UBERON_0002049> <http://purl.obolibrary.org/obo/UBERON_0004535>
      <http://purl.obolibrary.org/obo/UBERON_0004638> <http://purl.obolibrary.org/obo/UBERON_0007798> <http://purl.obolibrary.org/obo/UBERON_0035753>
    }
    ?ke a aopo:KeyEvent ; ?p ?term .
    FILTER(?p IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
    ?aop a aopo:AdverseOutcomePathway ; aopo:has_key_event ?ke .
  }
}
OECD status of every AOP in the snapshot
SELECT ?aop ?status
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL { ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status . }
  }
}

Key Event Reuse Analysis

Key Event Reuse Distribution

Histogram showing how many AOPs each Key Event belongs to. Most KEs appear in only 1 AOP, but some are shared across many pathways.

Methodology

What this measures: Histogram showing the distribution of how many AOPs each Key Event belongs to. Most KEs appear in only 1 AOP, but some are shared across many pathways, indicating shared biological mechanisms.

Data source: SPARQL query counts distinct AOPs per Key Event, then bins the results into discrete categories: 1, 2, 3, 4, 5, 6-10, and 11+ AOPs per KE.

Known limitations: Bins KEs into discrete groups for readability. The '6-10' and '11+' bins aggregate multiple values. Only KEs that are linked to at least one AOP via aopo:has_key_event are included. Orphan KEs (not linked to any AOP) are excluded.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
AOP count per Key Event (full distribution)
SELECT ?ke (COUNT(DISTINCT ?aop) AS ?aop_count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
  }
}
GROUP BY ?ke

Most Reused Key Events Across AOPs

Top 30 Key Events that appear in the most AOPs, revealing shared biology across pathways. Click on a bar to open the Key Event in AOP-Wiki.

Methodology

What this measures: Shows the top 30 Key Events that appear in the most AOPs, revealing shared biology across adverse outcome pathways. Clicking a bar opens the corresponding KE page on AOP-Wiki.

Data source: SPARQL query counts distinct AOPs per Key Event via aopo:has_key_event relationships. Only KEs appearing in more than one AOP are shown. KE titles are retrieved via dc:title.

Known limitations: Limited to the top 30 most reused KEs. KE titles may be truncated for readability. The count reflects explicit aopo:has_key_event declarations in the RDF data. Entity links open https://aopwiki.org/events/{id} in a new tab.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Top 30 KEs by AOP membership count (HAVING > 1)
SELECT ?ke (SAMPLE(?title_val) AS ?title) (COUNT(DISTINCT ?aop) AS ?aop_count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    OPTIONAL { ?ke dc:title ?title_val . }
  }
}
GROUP BY ?ke
HAVING (COUNT(DISTINCT ?aop) > 1)
ORDER BY DESC(?aop_count)
LIMIT 30

Data Quality & Completeness

KE Measurement-Method Coverage

Share of Key Events that carry an obo:MMO_0000000 (measurement method) description — the single biggest completeness signal for NAM-ready AOPs. Currently only ~37% of KEs have one.

Methodology

What this measures: Donut showing what fraction of KEs in the latest snapshot carry an `obo:MMO_0000000` measurement-method description. ~37% covered today; the uncovered ~63% is the single biggest NAM-readiness gap visible in AOP-Wiki RDF. Companion to the historical trend on the Trends page.

Data source: Single SPARQL aggregation over the latest named graph: `SUM(IF(BOUND(?mmo), 1, 0))` divided by `COUNT(?ke)`.

Known limitations: Counts presence of any MMO triple — does not assess length or quality of the assay text. KEs reused across AOPs are counted once.

View SPARQL Query
SELECT (SUM(IF(BOUND(?mmo), 1, 0)) AS ?with_mmo)
       (COUNT(?ke) AS ?total)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent .
    OPTIONAL { ?ke <http://purl.obolibrary.org/obo/MMO_0000000> ?mmo }
  }
}

Ontology Term Diversity

Number of unique ontology terms used per source ontology (GO, CHEBI, UBERON, etc.) in Key Event annotations. Shows usage diversity, not coverage against reference ontologies.

Methodology

What this measures: Counts the number of unique ontology terms used per source ontology (GO, CHEBI, UBERON, NCBITAXON, etc.) in KE annotations. Shows the breadth of ontology vocabulary in use.

Data source: SPARQL query retrieves all distinct ontology-linked IRIs from biological process and biological object annotations. Python post-processing classifies each IRI by ontology namespace prefix and counts unique terms per source.

Known limitations: Shows aggregate counts of unique terms per ontology, not coverage vs reference ontologies (Phase 5 scope). Classification is based on URI prefix patterns. Terms from unrecognized ontologies appear as 'Other'.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distinct ontology terms attached to KEs via biological events
SELECT DISTINCT ?term
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?be .
    { ?be aopo:hasProcess ?term . }
    UNION
    { ?be aopo:hasObject ?term . }
  }
  FILTER(isIRI(?term))
}

Ontology Coverage Holes

The inverse of the chart above: parts of an anatomy branch that AOP-Wiki never annotates against. Each bar is a sub-branch in which not one term carries an annotation, sized by how many ontology terms sit beneath it. Cardiac valve is one such hole, which is how “no coherent valvulopathy AOP exists” falls out of a query rather than an expert reading 80 pathways. Read a bar as AOP-Wiki carries no machine-readable statement about this part of the ontology — not as absent biology, since a pathway can describe an organ in free text and annotate nothing.

Methodology

What this measures: The inverse of the ontology-usage views: parts of an anatomy branch that AOP-Wiki never annotates against. For the selected organ system, each bar is a sub-branch in which not one term carries an annotation, sized by how many ontology terms sit beneath it. A sub-branch is reported only when it is the topmost unannotated node on its path, so the chart names the largest actionable branch rather than every leaf below it. On the 2026-07-01 snapshot the cardiovascular branch has 80 such holes, led by arterial system (564 terms), venous system (459) and vasculature of organ (250); only 10 of its 1,781 terms are used (0.6%). Cardiac valve (UBERON:0000946, 34 terms) is among them — which is how "no coherent valvulopathy AOP exists" falls out of a query rather than an expert panel reading 80 pathways.

Data source: Two SPARQL queries against the AOP-Wiki endpoint: one resolves the latest snapshot graph, one lists the ontology terms annotated on Key Events in the target graph. Term usage is read from aopo:OrganContext, aopo:CellTypeContext, obo:PATO_0001241 (biological object) and obo:GO_0008150 (biological process). Note this deliberately differs from the other ontology views, which query aopo:hasBiologicalEvent only and therefore never see OrganContext or CellTypeContext — 4 of the 10 in-use circulatory-system terms are reachable only via those two predicates. The ontology structure itself comes from a committed offline artefact, static/data/ontology_branch_cache.json, built by scripts/build_ontology_branch_cache.py: it queries Ubergraph for the descendants of each curated organ-system anchor (one hop against the pre-materialised <http://reasoner.renci.org/redundant> closure graph), the asserted parent-child edges from <...nonredundant> for depth and subtree size, rdfs:label for every candidate, and RO:0002161 (never_in_taxon) for the non-vertebrate filter. Ubergraph is never contacted at request time, so the page does not depend on that service being up. The used-versus-unused comparison is computed per snapshot in Python, so the version selector works.

Known limitations: Absence of a term is not absence of the biology. A pathway can describe valve damage in free text and annotate nothing, so the honest reading is "AOP-Wiki carries no machine-readable statement about this part of the ontology", never "this biology is missing". Branches cover UBERON and CL only. Ubergraph's closure mixes species-specific anatomy ontologies into UBERON's branches and they dominate — only 12% of the 34,808 terms under nervous system are UBERON or CL, the rest being FBbt (Drosophila), PCL and the mouse brain atlas — and AOP-Wiki annotates with none of them, so including them would report fly neuroanatomy as an AOP-Wiki gap. Holes are reported only down to depth 3 from the branch root; deeper gaps are leaf-level and not actionable. The used fraction in the subtitle is computed over the whole branch, not the capped set. Non-vertebrate anatomy is filtered out — terms declared never_in_taxon a vertebrate-containing clade, plus the Drosophila-derived UBERON_6* range — but the filter is partial: invertebrate terms carrying neither marker survive it, e.g. UBERON:0015231 circulatory system dorsal vessel. Annotations made with a non-OBO vocabulary are invisible here, so a structure annotated only that way can appear as a hole: AOP-Wiki uses 21 FMA terms and no FMA-to-UBERON cross-reference resolves in Ubergraph (0 of 21). Measured exposure is small — of six FMA terms checked, five have their OBO equivalent also in use; FMA:58241 (lens) is the one real false hole. Branch membership follows subClassOf and part_of, so a term reachable only by another relation is not counted. The ontology is a directed acyclic graph, not a tree — 26 of the 143 cardiovascular candidates have more than one parent — so subtree sizes count distinct reachable terms, and the same sub-branch may be reachable by several paths.

Browse full curated mapping (JSON)

Resolve latest snapshot graph URI
SELECT ?graph WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
} GROUP BY ?graph ORDER BY DESC(?graph) LIMIT 1
Ontology terms annotated on Key Events in this snapshot
SELECT DISTINCT ?term
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ; ?p ?term .
    FILTER(?p IN (
      <http://aopkb.org/aop_ontology#OrganContext>,
      <http://aopkb.org/aop_ontology#CellTypeContext>,
      <http://purl.obolibrary.org/obo/PATO_0001241>,
      <http://purl.obolibrary.org/obo/GO_0008150>
    ))
  }
  FILTER(isIRI(?term))
}

Most-Reused Ontology Terms

Top 30 individual ontology terms referenced by the most Key Events, across all biological-event slots (process, object, action), coloured by ontology source (GO, CHEBI, PATO, MeSH, ...).

Methodology

What this measures: Top 30 individual ontology terms referenced by the most Key Events, across all biological-event slots (process, object, action). Bars are labelled by the term's dc:title and coloured by ontology source (GO, CHEBI, PATO, MeSH, ...), revealing which specific biological concepts recur across the AOP-Wiki.

Data source: SPARQL query counts distinct Key Events per ontology term reached via aopo:hasBiologicalEvent and any of aopo:hasProcess / aopo:hasObject / aopo:hasAction. Human-readable labels come from dc:title and the ontology source from dc:source on each term.

Known limitations: Limited to the top 30 terms. A term is counted once per Key Event regardless of slot. Terms lacking a dc:title are shown as a CURIE (e.g. GO:0023052); sources without dc:source are derived from the IRI prefix or labelled 'Other'. Counts reflect explicit biological-event annotations in the RDF data.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Top 30 ontology terms by distinct-KE count (all biological-event slots)
SELECT ?term (SAMPLE(?title_val) AS ?label) (SAMPLE(?source_val) AS ?source) (COUNT(DISTINCT ?ke) AS ?ke_count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?be .
    ?be aopo:hasProcess|aopo:hasObject|aopo:hasAction ?term .
    OPTIONAL { ?term dc:title ?title_val . }
    OPTIONAL { ?term dc:source ?source_val . }
  }
  FILTER(isIRI(?term))
}
GROUP BY ?term
ORDER BY DESC(?ke_count)
LIMIT 30

Ontology Usage

Overview of ontology term usage across Key Event annotations in the AOP-Wiki database.

Methodology

What this measures: Shows the distribution of ontology terms used to annotate Key Event biological components (processes, objects, actions). Groups terms by their source ontology (GO, MP, NBO, MI, VT, MESH, HP, OTHER) to reveal which ontologies are most commonly used in the AOP-Wiki knowledge base.

Data source: SPARQL query retrieves all biological event terms linked to Key Events via aopo:hasProcess, aopo:hasObject, and aopo:hasAction predicates, then classifies each term by its URI prefix to determine the source ontology.

Known limitations: Ontology classification is based on URI prefix matching (e.g., GO_, MP_, HP_). Terms with unrecognized prefixes are grouped as OTHER. Only counts distinct terms, not total usage frequency. Limited to Key Event annotations in the selected version.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distribution of process/object/action terms by ontology source
SELECT ?ontology (COUNT(DISTINCT ?term) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?bioevent .
    {
      ?bioevent aopo:hasProcess ?term .
    } UNION {
      ?bioevent aopo:hasObject ?term .
    } UNION {
      ?bioevent aopo:hasAction ?term .
    }
    BIND(
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/GO_"), "GO",
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/MP_"), "MP",
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/NBO_"), "NBO",
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/MI_"), "MI",
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/VT_"), "VT",
      IF(STRSTARTS(STR(?term), "http://purl.org/commons/record/mesh/"), "MESH",
      IF(STRSTARTS(STR(?term), "http://purl.obolibrary.org/obo/HP_"), "HP", "OTHER"))))))) AS ?ontology)
  }
}
GROUP BY ?ontology

AOP Completeness

Analysis of AOP data completeness showing the distribution of essential vs optional fields.

Methodology

What this measures: Analyzes how completely AOPs are documented by measuring the presence of key metadata properties. Shows the distribution of completeness scores across all AOPs, where completeness is the fraction of non-ubiquitous properties that are filled in.

Data source: Properties are loaded from property_labels.csv which defines expected properties per entity type. For each AOP, SPARQL queries check presence of each property. Properties that are 100% present (always filled) are excluded to focus on informative variation.

Known limitations: Completeness is based on property presence, not quality or accuracy of the data. Properties always present (100%) are excluded from the calculation. The property list is defined externally and may not capture all relevant metadata.

Total AOPs in latest graph
SELECT ?graph (COUNT(DISTINCT ?aop) AS ?total_aops)
WHERE {
  GRAPH ?graph { ?aop a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph) LIMIT 1
Count AOPs per property in latest graph
SELECT DISTINCT ?p (COUNT(DISTINCT ?aop) AS ?n)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         ?p ?o .
  }
}
GROUP BY ?p

AOP Completeness by OECD Status

Comparison of AOP completeness scores across OECD status categories (Endorsed, Under Review, Under Development) grouped by property type: Essential (Title, Abstract, Description), Content (Key Events, Relationships, Stressors), Context (Applicability domains), Assessment (Evidence, Plausibility), and Metadata (Dates, Identifiers).

Methodology

What this measures: Compares AOP completeness scores across OECD approval status categories (e.g., Endorsed, Under Review, Under Development). Reveals whether more mature AOPs tend to be better documented. Properties are grouped into categories: Essential, Content, Context, Assessment, and Metadata.

Data source: Combines AOP completeness data with OECD status information. SPARQL queries retrieve both property presence counts and the OECD status for each AOP. Status is determined via the NCI Thesaurus OECD status URI (http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688).

Known limitations: OECD status data is version-specific and may change between releases. Some AOPs may lack a status annotation. Properties always present (100%) are excluded from completeness calculation. Status categories reflect the OECD approval pipeline stages present in the data.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
AOPs with each property, grouped by OECD status
SELECT ?status ?p (COUNT(DISTINCT ?aop) AS ?count_with_property)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
    ?aop ?p ?o .
  }
}
GROUP BY ?status ?p
Total AOPs per OECD status (denominator)
SELECT ?status (COUNT(DISTINCT ?aop) AS ?total_aops)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
  }
}
GROUP BY ?status
Show property details
Loading properties...

KE Completeness by AOP OECD Status

Completeness of Key Events based on the OECD status of AOPs they belong to. KEs belonging to multiple AOPs with different statuses are counted for each status. Property types: Essential (Title, Description), Content (Biological components), Context (Applicability), Assessment (Evidence, Measurement), and Metadata (Dates, Links).

Methodology

What this measures: Measures completeness of Key Events based on the OECD status of the AOPs they belong to. KEs linked to multiple AOPs with different statuses are counted for each status. Property categories include Essential (Title, Description), Content (Biological components), Context (Applicability), Assessment (Evidence), and Metadata.

Data source: SPARQL queries join KE property presence data with AOP status information via the aopo:has_key_event relationship. Each KE inherits the status of its parent AOP(s).

Known limitations: A KE belonging to multiple AOPs with different OECD statuses will appear in each status group, potentially inflating counts. KEs not linked to any AOP are excluded. Properties always present are excluded from completeness scores.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
KEs with each property, grouped by parent AOP OECD status
SELECT ?status ?p (COUNT(DISTINCT ?ke) AS ?count_with_property)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
    ?ke a aopo:KeyEvent ;
        ?p ?o .
  }
}
GROUP BY ?status ?p
Total KEs per OECD status (denominator)
SELECT ?status (COUNT(DISTINCT ?ke) AS ?total_kes)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event ?ke .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
  }
}
GROUP BY ?status
Show property details
Loading properties...

KER Completeness by AOP OECD Status

Completeness of Key Event Relationships based on the OECD status of AOPs they belong to. KERs belonging to multiple AOPs with different statuses are counted for each status. Property types: Essential (Title, Description), Content (Upstream/Downstream KEs), Context (Applicability, Evidence type), and Metadata (Dates, Links).

Methodology

What this measures: Measures completeness of Key Event Relationships based on the OECD status of the AOPs they belong to. KERs linked to multiple AOPs with different statuses are counted for each status. Property categories include Essential, Content, Context, and Metadata.

Data source: SPARQL queries join KER property presence data with AOP status information via the aopo:has_key_event_relationship predicate. Each KER inherits the status of its parent AOP(s).

Known limitations: A KER belonging to multiple AOPs with different OECD statuses will appear in each status group. KERs not linked to any AOP are excluded. Properties always present are excluded from completeness scores.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
KERs with each property, grouped by parent AOP OECD status
SELECT ?status ?p (COUNT(DISTINCT ?ker) AS ?count_with_property)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event_relationship ?ker .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
    ?ker a aopo:KeyEventRelationship ;
         ?p ?o .
  }
}
GROUP BY ?status ?p
Total KERs per OECD status (denominator)
SELECT ?status (COUNT(DISTINCT ?ker) AS ?total_kers)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway ;
         aopo:has_key_event_relationship ?ker .
    OPTIONAL {
      ?aop <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C25688> ?status_obj .
      BIND(STR(?status_obj) AS ?status)
    }
  }
}
GROUP BY ?status
Show property details
Loading properties...

Biological Process Usage

Distribution of biological process ontology terms used in Key Event annotations.

Methodology

What this measures: Shows the distribution of biological process ontology terms used in Key Event annotations. Identifies which ontologies (e.g., GO, MESH, NCIT) are most commonly referenced for describing biological processes in KE component annotations.

Data source: A SPARQL query retrieves biological process URIs reached from Key Events via the aopo:hasBiologicalEvent → aopo:hasProcess path, then groups them by their ontology source namespace.

Known limitations: Only covers processes explicitly annotated in the RDF data. Ontology classification is based on URI prefix patterns. Some processes may be classified differently across ontologies.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distribution of biological-process terms by ontology source
SELECT ?ontology (COUNT(DISTINCT ?process) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?bioevent .
    ?bioevent aopo:hasProcess ?process .
    BIND(
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/GO_"), "GO",
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/MP_"), "MP",
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/NBO_"), "NBO",
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/MI_"), "MI",
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/VT_"), "VT",
      IF(STRSTARTS(STR(?process), "http://purl.org/commons/record/mesh/"), "MESH",
      IF(STRSTARTS(STR(?process), "http://purl.obolibrary.org/obo/HP_"), "HP", "OTHER"))))))) AS ?ontology)
  }
}
GROUP BY ?ontology

Biological Object Usage

Distribution of biological object ontology terms used in Key Event annotations.

Methodology

What this measures: Shows the distribution of biological object ontology terms used in Key Event annotations. Identifies which ontologies are most commonly referenced for describing biological objects (molecules, genes, proteins, etc.) in KE component annotations.

Data source: A SPARQL query retrieves biological object URIs reached from Key Events via the aopo:hasBiologicalEvent → aopo:hasObject path, then groups them by their ontology source namespace.

Known limitations: Only covers objects explicitly annotated in the RDF data. Ontology classification is based on URI prefix patterns. Multiple ontology terms may refer to the same biological entity.

Identify latest graph (KE anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:KeyEvent . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Distribution of biological-object terms by ontology source
SELECT ?ontology (COUNT(DISTINCT ?object) AS ?count)
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?ke a aopo:KeyEvent ;
        aopo:hasBiologicalEvent ?bioevent .
    ?bioevent aopo:hasObject ?object .
    BIND(
      IF(STRSTARTS(STR(?object), "http://purl.obolibrary.org/obo/CL_"), "CL",
      IF(STRSTARTS(STR(?object), "http://purl.obolibrary.org/obo/UBERON_"), "UBERON",
      IF(STRSTARTS(STR(?object), "http://purl.obolibrary.org/obo/CHEBI_"), "ChEBI",
      IF(STRSTARTS(STR(?object), "http://purl.obolibrary.org/obo/GO_"), "GO",
      IF(STRSTARTS(STR(?object), "http://purl.obolibrary.org/obo/PR_"), "PR", "OTHER"))))) AS ?ontology)
  }
}
GROUP BY ?ontology

Cross-Property Completeness Correlation

Pairwise φ correlation between the presence of nine variable AOP properties. High positive = AOPs that fill in one property tend to fill in the other; near zero = independently curated.

Methodology

What this measures: Pairwise phi correlation between the presence of nine variable AOP properties across all AOPs. A high positive value means AOPs that fill in one property tend to also fill in the other; near zero means the two are curated independently. The diagonal is self-correlation (1).

Data source: SPARQL records, per AOP, which of nine curated properties (Key Events, KERs, MIE, Adverse Outcome, Creator, Description, Life Stage, Domain of Applicability, Stressor) are present. Python builds the 0/1 presence matrix over all AOPs and computes the Pearson (= phi, for binary) correlation matrix.

Known limitations: Limited to nine properties chosen for partial presence (title/page are ~100% present and carry no signal, so excluded). Correlation reflects co-occurrence of property *presence*, not content quality. A property fully present or fully absent in a given snapshot yields undefined (blank) correlations.

Identify latest graph (AOP anchor)
SELECT ?graph
WHERE {
  GRAPH ?graph { ?s a aopo:AdverseOutcomePathway . }
  FILTER(STRSTARTS(STR(?graph), "http://aopwiki.org/graph/"))
}
GROUP BY ?graph
ORDER BY DESC(?graph)
LIMIT 1
Per-AOP presence of the nine curated properties
SELECT DISTINCT ?aop ?p
WHERE {
  GRAPH <http://aopwiki.org/graph/2026-07-01> {
    ?aop a aopo:AdverseOutcomePathway .
    OPTIONAL { ?aop ?p ?o . FILTER(?p IN (
      <http://aopkb.org/aop_ontology#has_key_event>,
      <http://aopkb.org/aop_ontology#has_key_event_relationship>,
      <http://aopkb.org/aop_ontology#has_molecular_initiating_event>,
      <http://aopkb.org/aop_ontology#has_adverse_outcome>,
      <http://purl.org/dc/elements/1.1/creator>,
      <http://purl.org/dc/elements/1.1/description>,
      <http://aopkb.org/aop_ontology#LifeStageContext>,
      <http://aopkb.org/aop_ontology#AopContext>,
      <http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#C54571>)) }
  }
}