RDF / SPARQL¶
ddigraph peut exporter les métadonnées DDI sous forme de triplets RDF pour une utilisation avec des points d'accès SPARQL et des outils du web sémantique.
Vue d'ensemble¶
Le backend RDF convertit les entités DDI en triplets RDF en utilisant des vocabulaires standards :
- DDI-RDF : Vocabulaire RDF de l'Alliance DDI (lorsque disponible)
- Dublin Core : Termes de métadonnées standards
- SKOS : Pour les listes de codes et les catégories
- Espace de noms personnalisé : Pour les propriétés spécifiques à DDI
Dépendances¶
RDFLib est inclus avec ddigraph :
pip install ddigraph # inclut rdflib
Pour les points d'accès SPARQL distants, vous pourriez avoir besoin de pilotes supplémentaires :
pip install sparqlwrapper # pour les requêtes SPARQL distantes
Utilisation de base¶
Convertir DDI en RDF¶
from rdflib import Graph, Namespace, Literal, URIRef
from rdflib.namespace import RDF, RDFS, SKOS, DCTERMS
from ddigraph import DDIFragmentParser
# Définir les espaces de noms
DDI = Namespace("http://ddi.example.org/")
DATA = Namespace("http://data.example.org/")
g = Graph()
g.bind("ddi", DDI)
g.bind("data", DATA)
g.bind("skos", SKOS)
g.bind("dcterms", DCTERMS)
# Analyser DDI et créer les triplets
parser = DDIFragmentParser()
for fragment in parser.parse("survey.xml"):
subj = DATA[fragment.fragment_id]
# Triplet de type
g.add((subj, RDF.type, DDI[fragment.element_type]))
# Libellé
if fragment.label:
g.add((subj, RDFS.label, Literal(fragment.label)))
# URN comme identifiant
if fragment.urn:
g.add((subj, DCTERMS.identifier, Literal(fragment.urn)))
# Relations
for rel_type, ref in fragment.references:
obj = DATA[ref.id]
g.add((subj, DDI[rel_type], obj))
# Sérialiser
print(g.serialize(format="turtle"))
Formats d'export¶
# Turtle (lisible par l'humain)
g.serialize("output.ttl", format="turtle")
# N-Triples (traitement en flux)
g.serialize("output.nt", format="nt")
# RDF/XML
g.serialize("output.rdf", format="xml")
# JSON-LD
g.serialize("output.jsonld", format="json-ld")
Exemple complet¶
Consultez demo/load_rdf.py pour un exemple complet :
"""Load DDI into RDF graph and perform SPARQL queries."""
from pathlib import Path
from rdflib import Graph, Namespace, Literal, URIRef
from rdflib.namespace import RDF, RDFS, SKOS, XSD
from ddigraph.ingest.fragment_loader import DDIFragmentParser
DDI = Namespace("http://ddialliance.org/Specification/DDI-Lifecycle/3.3/")
DATA = Namespace("http://example.org/data/")
def load_ddi_to_rdf(ddi_path: str) -> Graph:
"""Parse DDI-L file and create RDF graph."""
g = Graph()
g.bind("ddi", DDI)
g.bind("data", DATA)
g.bind("skos", SKOS)
parser = DDIFragmentParser()
for fragment in parser.parse(ddi_path):
subj = DATA[fragment.fragment_id]
# Node type
g.add((subj, RDF.type, DDI[fragment.element_type]))
# Properties
if fragment.label:
g.add((subj, RDFS.label, Literal(fragment.label)))
if fragment.urn:
g.add((subj, DDI.urn, Literal(fragment.urn)))
# Special handling for QuestionItems
if fragment.element_type == "QuestionItem":
props = fragment.to_dict()
if props.get("question_text"):
g.add((subj, DDI.questionText, Literal(props["question_text"])))
# Categories as SKOS concepts
if fragment.element_type == "Category":
g.add((subj, RDF.type, SKOS.Concept))
props = fragment.to_dict()
if props.get("category_label"):
g.add((subj, SKOS.prefLabel, Literal(props["category_label"])))
# Relationships
for rel_type, ref in fragment.references:
obj = DATA[ref.id]
g.add((subj, DDI[rel_type], obj))
return g
def main():
ddi_file = "data/Ireland_LabourSurvey.xml"
g = load_ddi_to_rdf(ddi_file)
print(f"Created {len(g)} triples")
# Export
g.serialize("output.ttl", format="turtle")
# Query
query = """
PREFIX ddi: <http://ddialliance.org/Specification/DDI-Lifecycle/3.3/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?item ?label ?text
WHERE {
?item a ddi:QuestionItem .
OPTIONAL { ?item rdfs:label ?label }
OPTIONAL { ?item ddi:questionText ?text }
}
LIMIT 10
"""
for row in g.query(query):
print(f"{row.label}: {row.text}")
if __name__ == "__main__":
main()
Requêtes SPARQL¶
Requêtes locales¶
# Comptage par type
query = """
SELECT ?type (COUNT(?s) AS ?count)
WHERE { ?s a ?type }
GROUP BY ?type
ORDER BY DESC(?count)
"""
for row in g.query(query):
print(f"{row.type}: {row.count}")
Trouver les questions avec des listes de codes¶
PREFIX ddi: <http://ddialliance.org/Specification/DDI-Lifecycle/3.3/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?question ?questionText ?codeList
WHERE {
?question a ddi:QuestionItem ;
ddi:questionText ?questionText ;
ddi:USES_CODELIST ?codeList .
}
Traverser le flux de contrôle¶
PREFIX ddi: <http://ddialliance.org/Specification/DDI-Lifecycle/3.3/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?instrument ?construct ?constructType
WHERE {
?instrument a ddi:Instrument ;
ddi:HAS_CONSTRUCT+ ?construct .
?construct a ?constructType .
}
Points d'accès SPARQL distants¶
Chargement dans un triplestore¶
from SPARQLWrapper import SPARQLWrapper
# Exemple avec Virtuoso
sparql = SPARQLWrapper("http://localhost:8890/sparql")
sparql.setMethod("POST")
# Insérer les données
insert_query = """
INSERT DATA {
GRAPH <http://example.org/ddi/> {
%s
}
}
""" % g.serialize(format="nt")
sparql.setQuery(insert_query)
sparql.query()
Interroger des points d'accès distants¶
from SPARQLWrapper import SPARQLWrapper, JSON
sparql = SPARQLWrapper("http://localhost:8890/sparql")
sparql.setReturnFormat(JSON)
sparql.setQuery("""
SELECT ?s ?p ?o
FROM <http://example.org/ddi/>
WHERE { ?s ?p ?o }
LIMIT 100
""")
results = sparql.query().convert()
for result in results["results"]["bindings"]:
print(result)
Vocabulaire DDI-RDF¶
ddigraph utilise des types de relations sémantiques qui correspondent aux concepts DDI :
| Relation ddigraph | Propriété RDF | Description |
|---|---|---|
HAS_CONSTRUCT |
ddi:hasConstruct |
La séquence contient un construct |
USES_CODELIST |
ddi:usesCodeList |
La question utilise une liste de codes |
HAS_CATEGORY |
ddi:hasCategory |
La CodeList contient une catégorie |
ASKS_QUESTION |
ddi:asksQuestion |
Le construct référence une question |
USES_CONCEPT |
ddi:usesConcept |
L'entité référence un concept |
Intégration avec le Linked Data¶
Liaison avec des vocabulaires externes¶
from rdflib import OWL
# Lier les catégories à des vocabulaires externes
g.add((DATA["category-1"], OWL.sameAs, URIRef("http://eurovoc.europa.eu/123")))
# Utiliser des ontologies standards
g.add((DATA["variable-1"], DCTERMS.subject, URIRef("http://dbpedia.org/resource/Employment")))
Publication en tant que Linked Data¶
# Ajouter une description de jeu de données VoID
VOID = Namespace("http://rdfs.org/ns/void#")
dataset = DATA["dataset"]
g.add((dataset, RDF.type, VOID.Dataset))
g.add((dataset, DCTERMS.title, Literal("DDI Survey Metadata")))
g.add((dataset, VOID.sparqlEndpoint, URIRef("http://example.org/sparql")))
Voir aussi¶
- Architecture des adaptateurs - Construction d'adaptateurs personnalisés
- Modèle de relations - Types de relations DDI
- Vocabulaire DDI-RDF - Spécification officielle DDI-RDF