Skip to content

Note

To run this notebook in JupyterLab, load examples/ex7_0.ipynb

Measurement and inference

The following examples explore the use of inference as an automated way to generate new relation (i.e., expand the graph) based on the data in the KG plus the rules of its vocabularies. We'll also show how to use the kglab.Measure class to measure the size and composition of a KG.

Now let's load a KG from dat/gorm.ttl that describes a fictional small community of happy Vikings:

import kglab

namespaces = {
    "foaf": "http://xmlns.com/foaf/0.1/",
    "gorm": "http://example.org/sagas#",
    "rel":  "http://purl.org/vocab/relationship/",
    }

kg = kglab.KnowledgeGraph(
    name = "Happy Vikings KG example for SKOS/OWL inference",
    namespaces=namespaces,
    )

kg.load_rdf("../dat/gorm.ttl")
text = kg.save_rdf_text()
print(text)
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix gorm: <http://example.org/sagas#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .

gorm:Astrid a gorm:Viking ;
    gorm:childOf gorm:Bodil,
        gorm:Leif ;
    foaf:topic_interest gorm:Fighting .

gorm:childOf rdfs:domain gorm:Viking ;
    rdfs:range gorm:Viking ;
    owl:inverseOf gorm:ancestorOf .

gorm:spouseOf a owl:SymmetricProperty ;
    rdfs:domain gorm:Viking ;
    rdfs:range gorm:Viking .

gorm:Berserkr a foaf:Thing ;
    skos:broader gorm:Fighting .

gorm:Bjorn a gorm:Viking ;
    gorm:childOf gorm:Gorm ;
    foaf:topic_interest gorm:Pilaging .

gorm:Bodil a gorm:Viking ;
    gorm:spouseOf gorm:Leif .

gorm:Gorm a gorm:Viking ;
    foaf:topic_interest gorm:Berserkr .

gorm:Pilaging a foaf:Thing ;
    skos:broader gorm:Fighting .

gorm:Leif a gorm:Viking ;
    gorm:childOf gorm:Bjorn .

gorm:Fighting a foaf:Thing .

gorm:Viking a foaf:Person .

We can use the Measure class to count the number of nodes and edges in the graph at this point:

import pandas as pd
pd.set_option("max_rows", None)

measure = kglab.Measure()
measure.measure_graph(kg)

print("edges", measure.get_edge_count())
print("nodes", measure.get_node_count())
edges 25
nodes 15

Ancestors are important to Vikings, so let's see who's an anscestor of whom?

sparql = """
SELECT ?elder ?viking
  WHERE {
      ?elder gorm:ancestorOf ?viking
  }
  ORDER BY ASC(?viking)
  """

df = kg.query_as_df(sparql)
df

And who is a spouse of whom?

sparql = """
SELECT ?viking1 ?viking2
  WHERE {
      ?viking1 gorm:spouseOf ?viking2
  }
  """

df = kg.query_as_df(sparql)
df
viking1 viking2
0 gorm:Bodil gorm:Leif

Of course for Vikings one may not even need to ask, but who wants to fight?

sparql = """
SELECT ?viking ?hobby
  WHERE {
      ?viking foaf:topic_interest ?hobby .
      gorm:Fighting skos:narrower ?hobby
  }
  ORDER BY ASC(?viking)
  """

df = kg.query_as_df(sparql)
df

Huh. Nobody wants to fight?!? That doesn't seem especially Viking-like! Nor do these query results seem to fit an intuitive sense of our graph data. Let's use inference on the graph to help fix that.

Inference based on owlrl

Now we can call the infer_owlrl_closure() method to add RDF statements to the graph based on OWL inference:

kg.infer_owlrl_closure()
text = kg.save_rdf_text()
print(text)
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix gorm: <http://example.org/sagas#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

gorm:childOf rdfs:domain gorm:Viking ;
    rdfs:range gorm:Viking ;
    owl:inverseOf gorm:ancestorOf ;
    owl:sameAs gorm:childOf .

gorm:spouseOf a owl:SymmetricProperty ;
    rdfs:domain gorm:Viking ;
    rdfs:range gorm:Viking ;
    owl:sameAs gorm:spouseOf .

rdf:HTML a rdfs:Datatype ;
    owl:sameAs rdf:HTML .

rdf:LangString a rdfs:Datatype ;
    owl:sameAs rdf:LangString .

rdf:PlainLiteral a rdfs:Datatype ;
    owl:sameAs rdf:PlainLiteral .

rdf:XMLLiteral a rdfs:Datatype ;
    owl:sameAs rdf:XMLLiteral .

rdf:type owl:sameAs rdf:type .

rdfs:Literal a rdfs:Datatype ;
    owl:sameAs rdfs:Literal .

rdfs:comment a owl:AnnotationProperty ;
    owl:sameAs rdfs:comment .

rdfs:domain owl:sameAs rdfs:domain .

rdfs:isDefinedBy a owl:AnnotationProperty ;
    owl:sameAs rdfs:isDefinedBy .

rdfs:label a owl:AnnotationProperty ;
    owl:sameAs rdfs:label .

rdfs:range owl:sameAs rdfs:range .

rdfs:seeAlso a owl:AnnotationProperty ;
    owl:sameAs rdfs:seeAlso .

rdfs:subClassOf owl:sameAs rdfs:subClassOf .

xsd:NCName a rdfs:Datatype ;
    owl:sameAs xsd:NCName .

xsd:NMTOKEN a rdfs:Datatype ;
    owl:sameAs xsd:NMTOKEN .

xsd:Name a rdfs:Datatype ;
    owl:sameAs xsd:Name .

xsd:anyURI a rdfs:Datatype ;
    owl:sameAs xsd:anyURI .

xsd:base64Binary a rdfs:Datatype ;
    owl:sameAs xsd:base64Binary .

xsd:boolean a rdfs:Datatype ;
    owl:sameAs xsd:boolean .

xsd:byte a rdfs:Datatype ;
    owl:sameAs xsd:byte .

xsd:date a rdfs:Datatype ;
    owl:sameAs xsd:date .

xsd:dateTime a rdfs:Datatype ;
    owl:sameAs xsd:dateTime .

xsd:dateTimeStamp a rdfs:Datatype ;
    owl:sameAs xsd:dateTimeStamp .

xsd:decimal a rdfs:Datatype ;
    owl:sameAs xsd:decimal .

xsd:double a rdfs:Datatype ;
    owl:sameAs xsd:double .

xsd:float a rdfs:Datatype ;
    owl:sameAs xsd:float .

xsd:hexBinary a rdfs:Datatype ;
    owl:sameAs xsd:hexBinary .

xsd:int a rdfs:Datatype ;
    owl:sameAs xsd:int .

xsd:integer a rdfs:Datatype ;
    owl:sameAs xsd:integer .

xsd:language a rdfs:Datatype ;
    owl:sameAs xsd:language .

xsd:long a rdfs:Datatype ;
    owl:sameAs xsd:long .

xsd:negativeInteger a rdfs:Datatype ;
    owl:sameAs xsd:negativeInteger .

xsd:nonNegativeInteger a rdfs:Datatype ;
    owl:sameAs xsd:nonNegativeInteger .

xsd:nonPositiveInteger a rdfs:Datatype ;
    owl:sameAs xsd:nonPositiveInteger .

xsd:normalizedString a rdfs:Datatype ;
    owl:sameAs xsd:normalizedString .

xsd:positiveInteger a rdfs:Datatype ;
    owl:sameAs xsd:positiveInteger .

xsd:short a rdfs:Datatype ;
    owl:sameAs xsd:short .

xsd:string a rdfs:Datatype ;
    owl:sameAs xsd:string .

xsd:time a rdfs:Datatype ;
    owl:sameAs xsd:time .

xsd:token a rdfs:Datatype ;
    owl:sameAs xsd:token .

xsd:unsignedByte a rdfs:Datatype ;
    owl:sameAs xsd:unsignedByte .

xsd:unsignedInt a rdfs:Datatype ;
    owl:sameAs xsd:unsignedInt .

xsd:unsignedLong a rdfs:Datatype ;
    owl:sameAs xsd:unsignedLong .

xsd:unsignedShort a rdfs:Datatype ;
    owl:sameAs xsd:unsignedShort .

owl:backwardCompatibleWith a owl:AnnotationProperty ;
    owl:sameAs owl:backwardCompatibleWith .

owl:deprecated a owl:AnnotationProperty ;
    owl:sameAs owl:deprecated .

owl:equivalentClass owl:sameAs owl:equivalentClass .

owl:incompatibleWith a owl:AnnotationProperty ;
    owl:sameAs owl:incompatibleWith .

owl:inverseOf owl:sameAs owl:inverseOf .

owl:priorVersion a owl:AnnotationProperty ;
    owl:sameAs owl:priorVersion .

owl:sameAs owl:sameAs owl:sameAs .

owl:versionInfo a owl:AnnotationProperty ;
    owl:sameAs owl:versionInfo .

skos:broader owl:sameAs skos:broader .

foaf:topic_interest owl:sameAs foaf:topic_interest .

gorm:Berserkr a foaf:Thing ;
    owl:sameAs gorm:Berserkr ;
    skos:broader gorm:Fighting .

gorm:Gorm a gorm:Viking ;
    gorm:ancestorOf gorm:Bjorn ;
    owl:sameAs gorm:Gorm ;
    foaf:topic_interest gorm:Berserkr .

gorm:Pilaging a foaf:Thing ;
    owl:sameAs gorm:Pilaging ;
    skos:broader gorm:Fighting .

gorm:ancestorOf owl:sameAs gorm:ancestorOf .

owl:SymmetricProperty owl:sameAs owl:SymmetricProperty .

foaf:Person owl:sameAs foaf:Person .

gorm:Astrid a gorm:Viking ;
    gorm:childOf gorm:Bodil,
        gorm:Leif ;
    owl:sameAs gorm:Astrid ;
    foaf:topic_interest gorm:Fighting .

gorm:Bjorn a gorm:Viking ;
    gorm:ancestorOf gorm:Leif ;
    gorm:childOf gorm:Gorm ;
    owl:sameAs gorm:Bjorn ;
    foaf:topic_interest gorm:Pilaging .

gorm:Bodil a gorm:Viking ;
    gorm:ancestorOf gorm:Astrid ;
    gorm:spouseOf gorm:Leif ;
    owl:sameAs gorm:Bodil .

owl:Class owl:sameAs owl:Class .

owl:Nothing a owl:Class ;
    rdfs:subClassOf owl:Nothing,
        owl:Thing ;
    owl:equivalentClass owl:Nothing ;
    owl:sameAs owl:Nothing .

gorm:Fighting a foaf:Thing ;
    owl:sameAs gorm:Fighting .

gorm:Leif a gorm:Viking ;
    gorm:ancestorOf gorm:Astrid ;
    gorm:childOf gorm:Bjorn ;
    gorm:spouseOf gorm:Bodil ;
    owl:sameAs gorm:Leif .

owl:Thing a owl:Class ;
    rdfs:subClassOf owl:Thing ;
    owl:equivalentClass owl:Thing ;
    owl:sameAs owl:Thing .

foaf:Thing owl:sameAs foaf:Thing .

gorm:Viking a foaf:Person ;
    owl:sameAs gorm:Viking .

owl:AnnotationProperty owl:sameAs owl:AnnotationProperty .

rdfs:Datatype owl:sameAs rdfs:Datatype .

How much has the size of our KG increased?

measure = kglab.Measure()
measure.measure_graph(kg)

print("edges", measure.get_edge_count())
print("nodes", measure.get_node_count())
edges 156
nodes 74

In other word, the graph increased by 59 nodes and 131 edges. How about the query results respectively for begetting and spousing?

sparql = """
SELECT ?elder_viking ?viking
  WHERE {
      ?elder_viking gorm:ancestorOf ?viking
  }
  ORDER BY ASC(?viking)
  """

df = kg.query_as_df(sparql)
df
elder_viking viking
0 gorm:Leif gorm:Astrid
1 gorm:Bodil gorm:Astrid
2 gorm:Gorm gorm:Bjorn
3 gorm:Bjorn gorm:Leif
sparql = """
SELECT ?viking1 ?viking2
  WHERE {
      ?viking1 gorm:spouseOf ?viking2
  }
  """

df = kg.query_as_df(sparql)
df
viking1 viking2
0 gorm:Leif gorm:Bodil
1 gorm:Bodil gorm:Leif

Both the transitive gorm:ancestorOf relations and the symmetric gorm:spouseOf relations have been inferred to add RDF statements through the OWL-RL closure.

The Measure class also tallies the counts for subjects, predicates, and objects:

measure.s_gen.get_tally()
count
http://example.org/sagas#Astrid 5
http://example.org/sagas#Bjorn 5
http://example.org/sagas#Leif 5
http://www.w3.org/2002/07/owl#Nothing 5
http://example.org/sagas#spouseOf 4
http://example.org/sagas#Bodil 4
http://example.org/sagas#childOf 4
http://www.w3.org/2002/07/owl#Thing 4
http://example.org/sagas#Gorm 4
http://example.org/sagas#Pilaging 3
http://example.org/sagas#Berserkr 3
http://www.w3.org/2001/XMLSchema#int 2
http://www.w3.org/2001/XMLSchema#NMTOKEN 2
http://www.w3.org/2001/XMLSchema#NCName 2
http://www.w3.org/2001/XMLSchema#boolean 2
http://www.w3.org/2001/XMLSchema#dateTime 2
http://www.w3.org/2001/XMLSchema#decimal 2
http://www.w3.org/2002/07/owl#priorVersion 2
http://www.w3.org/2001/XMLSchema#nonNegativeInteger 2
http://www.w3.org/2001/XMLSchema#anyURI 2
http://www.w3.org/2000/01/rdf-schema#Literal 2
http://www.w3.org/2001/XMLSchema#hexBinary 2
http://www.w3.org/2001/XMLSchema#base64Binary 2
http://www.w3.org/2001/XMLSchema#integer 2
http://www.w3.org/2000/01/rdf-schema#seeAlso 2
http://www.w3.org/2001/XMLSchema#negativeInteger 2
http://www.w3.org/2002/07/owl#versionInfo 2
http://www.w3.org/2001/XMLSchema#long 2
http://www.w3.org/2002/07/owl#backwardCompatibleWith 2
http://www.w3.org/2001/XMLSchema#byte 2
http://www.w3.org/2001/XMLSchema#time 2
http://www.w3.org/2001/XMLSchema#Name 2
http://www.w3.org/2002/07/owl#deprecated 2
http://www.w3.org/2001/XMLSchema#unsignedLong 2
http://www.w3.org/2002/07/owl#incompatibleWith 2
http://www.w3.org/2000/01/rdf-schema#label 2
http://www.w3.org/2001/XMLSchema#unsignedByte 2
http://www.w3.org/2001/XMLSchema#float 2
http://www.w3.org/2001/XMLSchema#positiveInteger 2
http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral 2
http://www.w3.org/2001/XMLSchema#short 2
http://example.org/sagas#Fighting 2
http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral 2
http://www.w3.org/2001/XMLSchema#unsignedInt 2
http://www.w3.org/2000/01/rdf-schema#comment 2
http://www.w3.org/2001/XMLSchema#language 2
http://www.w3.org/2001/XMLSchema#dateTimeStamp 2
http://www.w3.org/2001/XMLSchema#nonPositiveInteger 2
http://example.org/sagas#Viking 2
http://www.w3.org/2001/XMLSchema#unsignedShort 2
http://www.w3.org/2001/XMLSchema#string 2
http://www.w3.org/1999/02/22-rdf-syntax-ns#LangString 2
http://www.w3.org/2001/XMLSchema#double 2
http://www.w3.org/2001/XMLSchema#token 2
http://www.w3.org/2001/XMLSchema#date 2
http://www.w3.org/2001/XMLSchema#normalizedString 2
http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML 2
http://www.w3.org/2000/01/rdf-schema#isDefinedBy 2
http://xmlns.com/foaf/0.1/topic_interest 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#type 1
http://xmlns.com/foaf/0.1/Person 1
http://www.w3.org/2002/07/owl#SymmetricProperty 1
http://example.org/sagas#anscestorOf 1
http://www.w3.org/2002/07/owl#equivalentClass 1
http://www.w3.org/2004/02/skos/core#broader 1
http://www.w3.org/2002/07/owl#Class 1
http://www.w3.org/2002/07/owl#AnnotationProperty 1
http://www.w3.org/2002/07/owl#sameAs 1
http://www.w3.org/2002/07/owl#inverseOf 1
http://www.w3.org/2000/01/rdf-schema#subClassOf 1
http://www.w3.org/2000/01/rdf-schema#range 1
http://www.w3.org/2000/01/rdf-schema#Datatype 1
http://www.w3.org/2000/01/rdf-schema#domain 1
http://xmlns.com/foaf/0.1/Thing 1
measure.p_gen.get_tally()
count
http://www.w3.org/2002/07/owl#sameAs 74
http://www.w3.org/1999/02/22-rdf-syntax-ns#type 57
http://example.org/sagas#ancestorOf 4
http://example.org/sagas#childOf 4
http://www.w3.org/2000/01/rdf-schema#subClassOf 3
http://xmlns.com/foaf/0.1/topic_interest 3
http://www.w3.org/2000/01/rdf-schema#domain 2
http://www.w3.org/2000/01/rdf-schema#range 2
http://www.w3.org/2004/02/skos/core#broader 2
http://www.w3.org/2002/07/owl#equivalentClass 2
http://example.org/sagas#spouseOf 2
http://www.w3.org/2002/07/owl#inverseOf 1
measure.o_gen.get_tally()
count
http://www.w3.org/2000/01/rdf-schema#Datatype 37
http://example.org/sagas#Viking 10
http://www.w3.org/2002/07/owl#AnnotationProperty 10
http://www.w3.org/2002/07/owl#Thing 4
http://xmlns.com/foaf/0.1/Thing 4
http://example.org/sagas#Fighting 4
http://example.org/sagas#Leif 4
http://example.org/sagas#Bodil 3
http://www.w3.org/2002/07/owl#Nothing 3
http://example.org/sagas#Astrid 3
http://www.w3.org/2002/07/owl#Class 3
http://example.org/sagas#Bjorn 3
http://example.org/sagas#Pilaging 2
http://www.w3.org/2002/07/owl#SymmetricProperty 2
http://example.org/sagas#Berserkr 2
http://example.org/sagas#Gorm 2
http://example.org/sagas#anscestorOf 2
http://xmlns.com/foaf/0.1/Person 2
http://www.w3.org/2002/07/owl#equivalentClass 1
http://www.w3.org/2000/01/rdf-schema#Literal 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#type 1
http://www.w3.org/2004/02/skos/core#broader 1
http://www.w3.org/2002/07/owl#backwardCompatibleWith 1
http://www.w3.org/2002/07/owl#sameAs 1
http://www.w3.org/2001/XMLSchema#byte 1
http://www.w3.org/2001/XMLSchema#Name 1
http://www.w3.org/2001/XMLSchema#time 1
http://example.org/sagas#spouseOf 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral 1
http://example.org/sagas#childOf 1
http://www.w3.org/2001/XMLSchema#dateTime 1
http://www.w3.org/2000/01/rdf-schema#seeAlso 1
http://www.w3.org/2002/07/owl#deprecated 1
http://www.w3.org/2000/01/rdf-schema#isDefinedBy 1
http://www.w3.org/2001/XMLSchema#long 1
http://www.w3.org/2001/XMLSchema#positiveInteger 1
http://www.w3.org/2001/XMLSchema#anyURI 1
http://www.w3.org/2001/XMLSchema#integer 1
http://www.w3.org/2001/XMLSchema#double 1
http://www.w3.org/2001/XMLSchema#unsignedInt 1
http://www.w3.org/2001/XMLSchema#NMTOKEN 1
http://www.w3.org/2000/01/rdf-schema#label 1
http://www.w3.org/2000/01/rdf-schema#comment 1
http://www.w3.org/2001/XMLSchema#string 1
http://www.w3.org/2000/01/rdf-schema#range 1
http://www.w3.org/2002/07/owl#versionInfo 1
http://www.w3.org/2002/07/owl#incompatibleWith 1
http://www.w3.org/2001/XMLSchema#nonNegativeInteger 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral 1
http://www.w3.org/2001/XMLSchema#language 1
http://www.w3.org/2001/XMLSchema#unsignedByte 1
http://www.w3.org/2002/07/owl#inverseOf 1
http://www.w3.org/2001/XMLSchema#nonPositiveInteger 1
http://www.w3.org/2001/XMLSchema#date 1
http://www.w3.org/2001/XMLSchema#normalizedString 1
http://www.w3.org/2001/XMLSchema#float 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML 1
http://www.w3.org/2001/XMLSchema#unsignedLong 1
http://www.w3.org/2001/XMLSchema#short 1
http://www.w3.org/2002/07/owl#priorVersion 1
http://www.w3.org/2001/XMLSchema#negativeInteger 1
http://www.w3.org/2001/XMLSchema#boolean 1
http://www.w3.org/2000/01/rdf-schema#subClassOf 1
http://www.w3.org/2001/XMLSchema#NCName 1
http://www.w3.org/2001/XMLSchema#int 1
http://www.w3.org/2000/01/rdf-schema#domain 1
http://xmlns.com/foaf/0.1/topic_interest 1
http://www.w3.org/2001/XMLSchema#token 1
http://www.w3.org/2001/XMLSchema#decimal 1
http://www.w3.org/2001/XMLSchema#hexBinary 1
http://www.w3.org/2001/XMLSchema#dateTimeStamp 1
http://www.w3.org/2001/XMLSchema#unsignedShort 1
http://www.w3.org/2001/XMLSchema#base64Binary 1
http://www.w3.org/1999/02/22-rdf-syntax-ns#LangString 1

Inference based on skosify

Next, let's run SKOS inference to expand the skos:broader and skos:narrower relations about Vikings' hobby interests:

kg.infer_skos_related()
kg.infer_skos_hierarchical(narrower=True)
kg.infer_skos_transitive(narrower=True)

measure = kglab.Measure()
measure.measure_graph(kg)

print("edges", measure.get_edge_count())
print("nodes", measure.get_node_count())
edges 158
nodes 74

This added 2 edges to the graph. Let's query to see who wants to fight now?

sparql = """
SELECT ?viking ?hobby
  WHERE {
      ?viking foaf:topic_interest ?hobby .
      gorm:Fighting skos:narrower ?hobby
  }
  ORDER BY ASC(?viking)
  """

df = kg.query_as_df(sparql)
df
viking hobby
0 gorm:Bjorn gorm:Pilaging
1 gorm:Gorm gorm:Berserkr

These are the two relations added. More fighters – now that seems much more Viking-like!


Exercises

Exercise 1:

Starting from an initial load kg.load_rdf("dat/gorm.ttl") of this example Viking graph, show how to combine use of OWL and SKOS inference plus SPARQL queries to enumerate the RDF nodes in the KG which represent children of Vikings who enjoy some form of fighting.

Exercise 2:

Use the Measure class to

  1. Tally occurrences for each RDF node in the KG that's used as a subject or object
  2. Calculate a probability distribution for nodes based on this occurrence data
  3. Render this distribution as a histogram

Last update: 2021-04-10