Skip to content


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

Load data via Morph-KGC

morph-kgc is an engine that constructs RDF knowledge graphs from heterogeneous data sources with R2RML and RML mapping languages. Morph-KGC is built on top of pandas and it leverages mapping partitions to significantly reduce execution times and memory consumption for large data sources.

For documentation see

This example uses a simple SQLite database as input, transforming it into an RDF knowledge graph based on an R2RML mapping for relations between "students" and "sports".

First, let's visualize the sample database: CREATE TABLE "Student" ( "ID" integer PRIMARY KEY, "FirstName" varchar(50), "LastName" varchar(50) );

CREATE TABLE "Sport" ( "ID" integer PRIMARY KEY, "Description" varchar(50) );

CREATE TABLE "Student_Sport" ( "ID_Student" integer, "ID_Sport" integer, PRIMARY KEY ("ID_Student","ID_Sport"), FOREIGN KEY ("ID_Student") REFERENCES "Student"("ID"), FOREIGN KEY ("ID_Sport") REFERENCES "Sport"("ID") );

INSERT INTO "Student" ("ID","FirstName","LastName") VALUES (10,'Venus', 'Williams'); INSERT INTO "Student" ("ID","FirstName","LastName") VALUES (11,'Fernando', 'Alonso'); INSERT INTO "Student" ("ID","FirstName","LastName") VALUES (12,'David', 'Villa');

INSERT INTO "Sport" ("ID", "Description") VALUES (110,'Tennis'); INSERT INTO "Sport" ("ID", "Description") VALUES (111,'Football'); INSERT INTO "Sport" ("ID", "Description") VALUES (112,'Formula1');

INSERT INTO "Student_Sport" ("ID_Student", "ID_Sport") VALUES (10,110); INSERT INTO "Student_Sport" ("ID_Student", "ID_Sport") VALUES (11,111); INSERT INTO "Student_Sport" ("ID_Student", "ID_Sport") VALUES (11,112); INSERT INTO "Student_Sport" ("ID_Student", "ID_Sport") VALUES (12,111); This has three tables plus the data to populate them.

Morph-KGC needs a configuration to describe the mapping, so let's create a basic one for our example:

import os

cwd = os.path.dirname(os.getcwd())

config = f"""


You can see how to create this config file in the docs.

Alternatively, you provide a path to a config file, for example:

config = "path/to/config.ini"

This config references a R2RML mapping (in student_sport.r2rml.ttl) which gets applied to the input data. One way to create such a mapping is YARRRML, "a human-friendly text-based representation of RML rules". You write rules in YAML then transform these with a yarrrml-parser into either a RML or R2RML file. See the browser-based version called Matey to get an idea of how this works.

The YARRRML file needed for this example can be found in dat/ which can be used with the sample database:

  ns1: ""

    queryFormulation: mysql
    query: select * from Student left join Student_Sport on Student.ID = Student_Sport.ID_Student left join Sport on Sport.ID = Student_Sport.ID_Sport;

    sources: students
      - [a, ns1:Student]
      - [ns1:firstName, $(FirstName)]
      - [ns1:lastName, $(LastName)]
      - p: ns1:plays
        - mapping: sport
            function: equal
              - [str1, $(ID_Sport)]
              - [str2, $(ID_Sport)]
    sources: students
      - [a, ns1:Sport]
      - [ns1:description, $(Description)]
      - [ns1:id, $(ID_Sport)]

Using the yarrrml-parser:

yarrrml-parser -i student_sport.yml -o student_sport.r2rml.ttl -f R2RML

produces the students_sport.r2rml.ttl file. This is the file referenced in the configuration for Morph-KGC.

Next we'll use morph-kgc to load the RDF data from the SQLite based on an R2RML mapping:

from icecream import ic
import kglab

namespaces = {
    "ex":  "",

kg = kglab.KnowledgeGraph(
    name = "A KG example with students and sports",
    namespaces = namespaces,

INFO | 2022-03-23 11:32:45,453 | 7 mapping rules retrieved.
INFO | 2022-03-23 11:32:45,466 | Mapping partition with 1 groups generated.
INFO | 2022-03-23 11:32:45,467 | Maximum number of rules within mapping group: 7.
INFO | 2022-03-23 11:32:45,469 | Mappings processed in 0.281 seconds.
INFO | 2022-03-23 11:32:45,694 | Number of triples generated in total: 22.

Data can be loaded from multiple text formats, e.g. CSV, JSON, XML, Parquet, and also through different relational DBMS such as PostgresSQL, MySQL, Oracle, Microsoft SQL Server, MariaDB, and so on.

Now let's try to query!

sparql = """
PREFIX ex: <>

SELECT ?student_name ?sport_desc
  ?student rdf:type ex:Student .
  ?student ex:firstName ?student_name .
  ?student ex:plays ?sport .
  ?sport ex:description ?sport_desc

for row in kg._g.query(sparql):
    student_name = kg.n3fy(row.student_name)
    sport_desc = kg.n3fy(row.sport_desc)
    ic(student_name, sport_desc)
ic| student_name: 'Fernando', sport_desc: 'Formula1'
ic| student_name: 'Fernando', sport_desc: 'Football'
ic| student_name: 'Venus', sport_desc: 'Tennis'
ic| student_name: 'David', sport_desc: 'Football'

Last update: 2022-03-23