1. Rdflib
From ZERO to HERO in an hour
Daniel Fernández Álvarez
fernandezalvdaniel@uniovi.es
I got it… I think
2. Rdflib es…
• Librería para manejo de RDF:
• Parseo y serialización
• Múltiples formatos: N-triples, turtle, rdf/XML, JSON-LD (con plug-in)…
• Modelo de grafo en memoria:
• Creación
• Modificación
• Consulta (interfaz SPARQL)
• Soporte a quads (named graphs).
• Lenguajes de programación:
• Python
• Posible utilizarlo en R
3. Rdflib no es …
• …un triplestore*:
• Verdaderos triplestores: Virtuoso, BlazeGraph, Neptune…
• … apta para BigData*:
• Contenido del grafo cargado en memoria.
• … una librería para lanzar consultas contra endpoints:
• sparqlwrapper, de los los mismos desarrolladores, sí sirve para ello.
* Puede ser utilizada junto con algunas bases de datos (ej: Berkeley DB)
para hacer su contenido persistente…
… pero si necesitas eso, usa un triplestore ;)
4. Parsear un grafo
Local, remoto, string
from rdflib import Graph
# Local graph
g = Graph()
g.parse("example.ttl", # param 'source'
format="turtle")
# Remote graph
g = Graph()
g.parse("https://raw.githubusercontent.com/DaniFdezAlvarez/"
"shexerp3/develop/test/t_files/t_graph_1.ttl",
format="turtle")
# Graph in a 'str' object
dumb_g_str = "<http://ex.org/Asturias> <http://ex.org/capital>
<http://ex.org/Oviedo> ."
g = Graph()
g.parse(data=dumb_g_str,
format="nt")
• Parámetro source:
• Fichero local o
remoto
• Parámetro data:
• str
• Parámetro format:
• Fundamental
(rdflib no adivina
sintaxis)
• Rdf/xml por
defecto.
5. Parsear un grafo
Local, string
• Parámetro
destination:
• Fichero local
• Por defecto, None
• Si destination es
None, retorna objeto
‘bytes’
• Convertir ‘bytes’ a
‘str’ con decode()
• Parámetro format:
• Rdf/xml por defecto.
• No adivina formato
por la extensión
especificada.
g = Graph()
g.parse("example.ttl",
format="turtle")
g.serialize(destination="example_out.nt", # To local file
format="nt")
str_g = g.serialize(format="nt") # If 'destination' is None
# (deftault value), the content
# is returned as a bytes object
print(str_g)
print(type(str_g)) # type(str_g) --> 'bytes'
str_g = str_g.decode("utf-8")
print(str_g) # type(str_g) --> 'str'
print(type(str_g))
6. Modificar/crear grafo
• Cada tripleta es una 3-tupla de Python.
• Tuplas: conjuntos ordenados e inmutables.
• Las tripletas se componen de objetos de modelo de rdflib:
• URI rdflib.URIRef
• Literal rdflib.Literal
• Nodo anónimo rdflib.BNode
• Añadir una tripleta a un grafo g:
• g.add( a_triple ) # type(a_triple) == ‘tuple’
7. Modelo Rdflib: URIs
• URIRef
• Representa cualquier entidad con una URI (clases, instancias, propiedades).
• Instancias de URIRef usando constructor:
• Instancias de URIRef usando rdflib.Namespace:
• Rdflib incluye algunos namespaces preconstruidos: RDF, RDFS, OWL, XSD.
• Aconsejable: hacer binding del namespace.
• Asocia un prefijo reconocible a cierto namespace al serializar.
URIRef("http://example.org/Labra")
example = Namespace("http://example.org/")
labra = example.Labra
example = Namespace("http://example.org/")
g.bind("ex", example)
8. Modelo Rdflib: literales
• Literal:
• Representa cualquier tipo de literal.
• Instancias con constructor.
• Parámetros:
• value: valor del literal.
• datatype (opcional): tipo de dato, especificado en forma de URI (URIRef).
• lang(opcional): añade una etiqueta de lenguaje al literal (en, es…).
• Especificación de datatype:
• Si no se rellena datatype, rdflib es capaz de mapear el type() de value con los
elementos más comunes de XSD.
• CONSEJO: su mapeo puede no ser perfecto. Si se puede conocer el tipo a priori, usar un
datatype explícito.
9. Modelo Rdflib: nodos anónimos
• BNode:
• Representa nodos anónimos (Blank Nodes).
• Instancias con constructor:
• En general, sin parámetros. Puedes especificar un id para tu BNode con el parámetro
value si el contexto lo requiere.
• Cuando operes con un nodo anónimo, recuerda guardar un puntero al objeto
BNode instanciado para poder referenciarlo desde diferentes tripletas.
apple_anonymous = BNode()
g.add((example.Labra, example.holds, apple_anonymous))
g.add((apple_anonymous, RDF.type, example.Apple))
g.add((apple_anonymous, example.color, example.Green))
12. Recorrer grafo
• Todas las tripletas de un grafo:
• Tripletas de un grafo con algún elemento conocido: g.triples()
• triples() devuelve un generador, no una lista.
• Muy útil para evitar consultas SPARQL sencillas.
• El parámetro de triples() es una 3-tupla: sujeto, predicado, objeto.
• En la tupla se le da valor a las posiciones conocidas con objetos de modelo.
• Las posiciones desconocidas se dejan a None.
for a_triple in g:
pass # Do stuff with your triples
for a_triple in g.triples( (None, RDF.type, None) ): # Just triples with rdf:type as predicate
pass # Do stuff with your triples
13. Recorrer grafo
• Slicing de Python: sintaxis alternativa a g.triples ( (s,p,o) ):
• g[example.Bob] ↔ g.triples( (example.Bob, None, None) )
• En general, g[x] ↔ tripletas en que x es sujeto
• g[example.Bob : foaf.age] ↔ g.triples((example.Bob, foaf.age , None))
• En general, g[x:y] ↔ tripletas en que x es sujeto e y predicado.
• g[: foaf.age: Literal(31)] ↔ g.triples((None, foaf.age, Literal(31)) )
• En general, g[x:y:z] ↔ tripletas en que x es sujeto, y predicado y z objeto.
• x, y y z pueden ser vacíos. Equivaldría a usar None en la posición correspondiente de
g.triples( (s,p,o) )
14. Consultar grafo
• Rdflib es compatible con SPARQL.
• Cada ResultRow contiene una tupla ordenada de resultados. El
orden lo marca la consulta SELECT.
• Los resultados también se pueden recorrer usando nombres de variables
query_result = g.query(
""" SELECT ?s ?p WHERE {
?s a ?p
} """)
for a_row in query_result:
print(a_row, type(a_row)) # object ResultRow, it contains sorted model objects
for a_row in query_result:
# Equivalent ways to access the results using variable names
print(a_row.s)
print(a_row["s"])
15. Moralejas
En general rdflib es una librería válida. Casi todas las
alternativas en Python para tratamiento de RDF se
construyen sobre rdflib. Pero tras usarla durante 7 años…
He visto cosas soldado…
¡Y qué cosas!
¡No las soportarías!
16. Moralejas: ¿<> en URIs?
• Al mezclar rdflib con otras módulos para análisis de RDF pueden
aparecer contradicciones debido a la presencia o ausencia de
caracteres “<“ y “>”.
• En las serializaciones de RDF, las URIs se rodean de <>.
• En cambio, en rdflib, al instanciar una URIRef no debe haber <>.
• De la misma forma, no esperes caracteres <> al obtener el valor de una URI
labra = URIRef("http://example.org/Labra") # That's OK
print(labra) # http://example.org/Labra
labra = URIRef("<http://example.org/Labra>") # It will crash here
17. Moralejas: soporte a contenido dudoso
• Ejemplo: enconding de caracteres no ASCII:
• En teoría, una URI tiene un conjunto de caracteres válidos limitados.
• Caracteres especiales como ‘ñ’ deberían ser utilizados usando algún
codificación.
g = Graph()
g.add( (URIRef("http://example.org/España"), RDF.type, example.Country) )
print(g.serialize(format="turtle").decode("utf-8"))
g = Graph()
g.add( (URIRef("http://example.org/España"), RDF.type, example.Country) )
print(g.serialize(format="nt").decode("utf-8"))
18. Moralejas: encoding
• Error frecuente: al parsear/serializar, rdflib rompe con errores
confusos que hablan de códecs.
• Habitual cuando tratas con grafos que contienen labels en idiomas con
caracteres no ASCII.
• A veces ocurre que parsea datos, pero no los serializa después.
• Ante esto, parsear y serializar especificando encoding (habitualmente utf-8)
g.serialize(destination="example_out.nt",
format="nt",
encoding="utf-8")
g.parse("example.ttl",
format="turtle",
encoding="utf-8")
19. Moralejas: grandes cargas de datos
• Contras: gran consumo de RAM, parseo muy lento.
• Ejemplos de consumo (un hilo, Intel core i7):
• 0,5 M tripletas 32 segundos, 0,62 GB
• 1 M tripletas 1 minuto 11 segundos, 1,26 GB
• 7,7 M tripletas 8 minutos 26 segundos, 6.1 GB
• Ejemplos de grandes datasets*:
• Wikidata: 1.200 M statements (más tripletas debido a qualifiers).
• Capítulo inglés de DBpedia: 580 M.
• DBpedia: 3.000 M.
• … Rdflib no alcanza para hacer procesamiento en fuentes de esta
dimensión.
*Números aproximados a fecha de 03/03/2021
20. Rdflib
From ZERO to HERO in an hour
Daniel Fernández Álvarez
fernandezalvdaniel@uniovi.es
I got it… I think