Why Ontologies

Why Ontologies
Why Ontologies? | Trace Origin Research
Trace Origin
Methodology
The question we get asked most

Why can't you just analyze the CSV?

You can. We did, at first. The charge co-occurrence analysis, Louvain community detection, and permutation testing all run on flat CSV data. The pipeline works without a single triple. So why build a 386,000-triple OWL ontology? Because the CSV answers "what patterns exist in the charges?" The ontology answers "what do those patterns mean under international law?" The first question is interesting. The second question opens cells.

A CSV knows what the state charged. It doesn't know what the state violated.

Here is a row from the OVD-Info dataset:

A single row in the CSV
NameArticleYearStatus
Alexey Gorinov207.32022imprisoned

This row tells you four things: who, what charge, when, and current status. Python can count how many people were charged under 207.3. It can compute co-occurrence with other articles. It can test whether the charging structure changed after 2022. All of that is the CSV analysis, and it works.

But the row does not tell you:

What the CSV cannot answer

Does Article 207.3 violate the ICCPR? The CSV contains a charge code. It does not contain a legal assessment of whether that charge code criminalizes conduct protected under international law.

Which ICCPR article does it violate? Article 19 (expression)? Article 14 (fair trial)? Both? The charge code alone doesn't say.

Is it facially incompatible or only as-applied? Some charges violate the ICCPR on their face (sedition laws). Others are legitimate statutes weaponized against dissidents (assault on a police officer). The distinction matters for legal submissions. The CSV doesn't encode it.

How many people are detained under statutes that violate Article 19? To answer this, you need to cross-reference every charge against a mapping of charges to ICCPR articles. That mapping doesn't exist in the CSV. It exists in the ontology.

Three layers. Each does something the others cannot.

The knowledge graph architecture
LAYER 1: SKOS VOCABULARY Maps each Criminal Code article to the ICCPR provisions it violates. Art. 207.3 → ICCPR Art. 19 (expression) | facially_incompatible LAYER 2: A-BOX (INSTANCES) Every person, every prosecution, every charge as an OWL individual. Gorinov → persecution_gorinov_2022 → hasCharge → art_207_3 217,814 triples (Russia) | 91,094 triples (Belarus) | 386,648 triples (Myanmar) LAYER 3: SPARQL QUERIES Cross-references charges with ICCPR violations in a single query. "How many people are detained under Art. 19-violating statutes?" → 8,628

The CSV is the raw material. The SKOS is the legal assessment. The A-Box is the bridge. SPARQL is the query language that crosses all three in one call. Remove any layer and the question "how many people are detained under statutes that violate Article 19?" becomes unanswerable without manual counting.

The state's language and our assessment, separated by construction.

When Russia charges someone under Article 207.3, the charge sheet says "dissemination of knowingly false information about the use of the Armed Forces." That is the state's characterization. Our assessment is that this provision criminalizes protected expression under ICCPR Article 19. These are two different claims, made by two different actors, and the ontology keeps them apart.

Without SKOS

The analyst writes in a report: "Article 207.3 violates freedom of expression." This is an assertion in prose. It cannot be queried, validated, or audited. Another analyst might disagree. There is no mechanism for resolving the disagreement because the assessment is embedded in narrative, not structure.

With SKOS

The SKOS concept for Article 207.3 has a skos:notation (the state's code), a skos:editorialNote (the analyst's reasoning), an ont:facialCharacter (facially_incompatible), and an ont:iccprViolation (iccpr:Art19). The assessment is structured, attributable, and queryable. A different analyst can inspect the reasoning and override the classification without changing the data.

The SKOS vocabulary is not a database. It is a controlled vocabulary with editorial provenance. Every annotation records who made the assessment, on what basis, and how confident they are. This is what makes the output citable in a legal proceeding: the tribunal can inspect the reasoning behind each ICCPR mapping, not just the conclusion.

What SKOS encodes that a CSV column cannot
PropertyExampleWhy it matters
skos:notation"207.3"The state's own charge code, unmodified
skos:prefLabel"Art. 207.3 of the Criminal Code"Human-readable label
skos:editorialNote"Criminalizes protected expression..."Analyst reasoning, attributable
ont:facialCharacter"facially_incompatible"Three-tier classification: facially incompatible, as-applied, legitimate-but-weaponized
ont:iccprViolationiccpr:Art19Machine-readable link to the specific ICCPR provision violated
ont:introducedDate"2022-03-04"When the statute entered the Criminal Code
prov:wasDerivedFromviasna:342Provenance: which source record this concept was derived from

You could add an "iccpr_violation" column to a CSV. But a CSV column is a flat string. It cannot encode the reasoning, the three-tier classification, the provenance, or the relationship between the analyst's assessment and the state's terminology. It cannot be validated by an OWL reasoner. It cannot be merged with another dataset's vocabulary. The SKOS can do all of these.

NetworkX finds the structure. The ontology explains what it means.

The charge co-occurrence graph is a weighted undirected network where nodes are Criminal Code articles and edge weights are the number of people charged under both. NetworkX builds this graph from the CSV. Then Louvain community detection partitions the graph into clusters without being told what to look for. The clusters are not hypothesized. They are discovered.

This is the analytical engine of the pipeline. It produces the findings that make the reports worth reading: three regimes in Russia, four communities collapsing in Belarus, a civilian-military bifurcation in Myanmar. None of these were assumed. All were detected by Louvain and validated by permutation testing.

What NetworkX computes
AnalysisMethodWhat it reveals
Community detectionLouvain (Blondel et al. 2008)Which charges cluster together (charging regimes)
ModularityQ = community_louvain.modularity()How distinct the regimes are (higher Q = more separation)
Stability testing100 random seeds, NMI comparisonWhether the communities are an artifact of initialization or a real structure
Structural breaksPermutation null model (5,000 iterations)When the charging architecture actually changed (vs. when events happened)
Betweenness centralitynx.betweenness_centrality(G)Which articles bridge different regimes (the connectors)
Degree distributionnx.degree(G, weight="weight")Which articles co-occur with the most other articles

NetworkX runs on the CSV. It does not need the ontology. But it produces findings that the ontology makes citable. "Louvain found 7 communities with Q=0.47" is a statistical result. "The 7 communities correspond to three regimes, each violating a different ICCPR article" is a legal finding. The first requires NetworkX. The second requires SKOS. You need both.

NetworkX alone

"Community C0 contains Articles 207.3, 280.3, 205.2, and 354.1. Community C2 contains Articles 282.2, 205.5, and 282.3." These are charge codes. A treaty body does not read charge codes. They read: "Regime 1 violates Article 19 (expression). Regime 2 violates Article 22 (association)."

NetworkX + SKOS

"Louvain independently recovers three regimes. The SKOS vocabulary maps each regime to the ICCPR provisions it violates. Regime 1 (speech) maps to Article 19. Regime 2 (organizational) maps to Articles 18, 22, and 26. Regime 3 (street protest) maps to Article 21." This is a submission.

The query that justifies the entire architecture.

Here is the SPARQL query that returns the number of prosecutions by ICCPR article violated, run against the Myanmar merged graph (386,648 triples):

PREFIX ont: <http://repressions.myanmar/ontology/>
PREFIX iccpr: <http://www.ohchr.org/EN/ProfessionalInterest/Pages/CCPR#>

SELECT ?iccpr_article (COUNT(DISTINCT ?persecution) AS ?n)
WHERE {
    ?persecution a ont:Persecution ;
                 ont:hasCharge ?charge .
    ?charge ont:iccprViolation ?iccpr_article .
}
GROUP BY ?iccpr_article
ORDER BY DESC(?n)

This query does something no Python script running on a CSV can do: it crosses the A-Box (which knows that persecution X has charge Y) with the SKOS (which knows that charge Y violates ICCPR Article Z) and returns the count. The result:

Myanmar: prosecutions by ICCPR article violated
ICCPR articleRightProsecutions
Article 14Fair trial11,373
Article 9Liberty and security10,375
Article 19Expression8,628
Article 22Association4,142
Article 21Assembly384
Article 6Right to life164

These numbers are exact counts derived from 31,824 detention records. They are reproducible by running the query against the published ontology. A treaty body can verify the count by executing the same SPARQL.

To produce this table from a CSV, you would need to:

The CSV approach

1. Manually create a lookup table mapping each of 738 charge sections to ICCPR articles.

2. Write a Python script that joins the CSV against the lookup table.

3. Hope that the lookup table is complete, correct, and consistent.

4. When challenged, show the lookup table as a spreadsheet and the Python script as source code.

5. When another researcher wants to extend the analysis to Turkey, build an entirely new lookup table from scratch.

The ontology approach

1. The SKOS vocabulary encodes each mapping with editorial provenance, reasoning, and a three-tier classification.

2. The A-Box connects persons to prosecutions to charges using typed OWL relations.

3. SPARQL crosses the two layers in a single query. The query is the analysis.

4. When challenged, the tribunal can inspect the SKOS concept, read the editorial note, and evaluate the reasoning.

5. When another researcher extends to Turkey, they build a new SKOS vocabulary for the Turkish Penal Code and merge it with the same A-Box schema. The SPARQL queries work unchanged.

The CSV answers "what patterns exist in the charges?" The ontology answers "what do those patterns mean under international law?" The first question is interesting. The second question opens cells.

One schema. Four countries. The same SPARQL.

The T-Box (class definitions and property axioms) is the same across Cuba, Russia, Belarus, and Myanmar. A Persecution has a Person and one or more ChargeArticle instances, connected by hasPerson and hasCharge relations. Each ChargeArticle links to ICCPR provisions via iccprViolation.

Because the schema is shared, the same SPARQL query runs against any country's graph. "How many people are detained under facially incompatible statutes?" returns 9,029 for Myanmar, 6,298 for Belarus, and a corresponding count for Russia and Cuba. The query does not change. The vocabulary does. That is the point of a formal ontology: the analytical structure is reusable even when the legal substance is different.

Same query, four countries, four answers
SELECT ?iccpr (COUNT(...)) Cuba 1,172 prisoners SKOS: Cuban Penal Russia 7,111 prosecutions SKOS: Russian CC Belarus 8,689 prisoners SKOS: Belarus CC Myanmar 31,824 detentions SKOS: Penal + CTL

A Python script that analyzes the Russian CSV does not generalize to Myanmar without rewriting the column names, the charge parsing logic, the lookup tables, and the output format. An ontology that analyzes the Russian knowledge graph generalizes to Myanmar by swapping the SKOS vocabulary. Everything else (the T-Box, the SPARQL queries, the report generator) works unchanged.

An OWL reasoner catches errors a Python script cannot.

When you build a knowledge graph in OWL, you can run a reasoner (Pellet, HermiT, ELK) that checks logical consistency. If you accidentally classify the same charge as both "facially_incompatible" and "legitimate," the reasoner flags a contradiction. If you assert that a person was prosecuted under Article 207.3 but Article 207.3 doesn't exist in the SKOS vocabulary, the reasoner detects an unresolved reference.

A Python script on a CSV has no equivalent. If your lookup table has a typo ("207.3" vs "207-3"), the join silently fails and the count is wrong. You don't know until someone manually checks. An OWL ontology with SHACL validation catches these errors before the analysis runs.

CSV: silent failure

lookup["207.3"] = "Art. 19"
lookup["207-3"] = "Art. 19"  # typo: dash vs dot
# One of these never matches.
# The count is wrong.
# Nobody knows.

OWL: explicit failure

# SHACL validation:
# "Persecution X has charge
#  'art_207_3' which does not
#  exist in the SKOS scheme."
# Build fails. You fix it.

A treaty body will not cite a Python script.

When the UN Working Group on Arbitrary Detention reviews a case, it evaluates evidence. When the Human Rights Committee conducts a periodic review, it considers submissions. When the Special Rapporteur on Myanmar prepares a report, she cites sources.

A Python script that processes a CSV and prints "8,628 prosecutions violate Article 19" is an assertion by an analyst. A SPARQL query that returns 8,628 by crossing a published ontology against a published vocabulary is a reproducible finding. The treaty body can download the ontology, run the query, and verify the count. The reasoning behind each ICCPR mapping is in the SKOS editorial notes. The data provenance is in the A-Box. The schema is in the T-Box. Every component is inspectable, auditable, and citable.

That is the difference between evidence and analysis. Analysis is what you do with a CSV. Evidence is what you produce with an ontology. International legal proceedings require evidence.

The ontology does not open the cell. It writes down what the state has done, in a form the state cannot revise, in a language other institutions can read. That is not a remedy. It is a precondition for one.

CSV vs ontology: what each can and cannot do.

CapabilityCSV + PythonCSV + NetworkXFull stack (NetworkX + OWL + SKOS + SPARQL)
Count charges per articleYesYesYes
Co-occurrence analysisManualYesYes
Louvain community detectionNoYesYes
Structural break testingNoYesYes
Map charges to ICCPR violationsManualNoSKOS vocabulary
Query "how many Art. 19 violations?"Custom scriptNoSame SPARQL, any country
Separate state terminology from assessmentNoNoBy construction
Validate charge code consistencySilent failuresSilent failuresOWL reasoner + SHACL
Audit the reasoning behind each mappingProseProseskos:editorialNote per concept
Extend to a new countryRewritePartial rewriteNew SKOS, same T-Box
Cite in a legal proceedingNoStatistical onlyReproducible, auditable finding

The CSV gets you 30% of the way. NetworkX gets you to 70%. The ontology gets you the last 30%. That last 30% is the part that matters in a courtroom.

Trace Origin LLC builds knowledge graphs from political prisoner registries. The outputs are designed for UN treaty bodies, not academic journals. Four countries analyzed: Cuba, Russia, Belarus, Myanmar. traceoriginresearch.com

© 2026 Trace Origin LLC