Aller au contenu

10 minutes avec ddigraph

Ce tutoriel vous présente l'essentiel de ddigraph en une dizaine de minutes. À l'issue de cette lecture, vous aurez chargé un fichier DDI XML dans une base de données graphe et exécuté vos premières requêtes.

Qu'est-ce que DDI ?

La Data Documentation Initiative (DDI) est un standard international destiné à décrire les données d'enquête, les questionnaires et les métadonnées statistiques. Les fichiers DDI sont des documents XML qui capturent les variables, les questions, les listes de codes, les concepts et les relations qui les unissent -- exactement le type de métadonnées richement connectées qui prennent toute leur valeur dans une base de données graphe.

ddigraph analyse le XML DDI et le transforme en graphe de connaissances, avec une prise en charge privilégiée de Neo4j et des backends supplémentaires incluant RDF, Gremlin et NetworkX.


Installer ddigraph

pip install ddigraph

Version de Python

ddigraph nécessite Python 3.12-3.14. Vérifiez votre version avec python --version.

Le paquet installe automatiquement toutes les dépendances requises (lxml, pilote neo4j, pydantic-settings, networkx, rdflib, gremlinpython, etc.).


Démarrer Neo4j

La méthode la plus rapide pour obtenir une instance Neo4j locale est d'utiliser Docker :

docker run --rm --name neo4j-demo \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  neo4j:5

Après quelques secondes, la base de données est accessible sur bolt://localhost:7687 et l'interface web est disponible à l'adresse http://localhost:7474.

Neo4j Aura

Si vous préférez une instance gérée dans le cloud, créez gratuitement une base Neo4j Aura à l'adresse https://neo4j.com/cloud/aura-free/ et utilisez l'URI neo4j+s:// qui vous sera fournie.

Docker non installé ?

Si Docker n'est pas installé sur votre machine, vous pouvez également utiliser Neo4j Desktop qui fournit une interface graphique pour gérer vos bases de données locales.


Variables d'environnement

Indiquez à ddigraph comment se connecter à Neo4j :

export DDIGRAPH_NEO4J_URI=bolt://localhost:7687
export DDIGRAPH_NEO4J_USER=neo4j
export DDIGRAPH_NEO4J_PASSWORD=password

Vous pouvez aussi placer ces valeurs dans un fichier .env dans votre répertoire de travail -- ddigraph le détecte automatiquement.

# .env
DDIGRAPH_NEO4J_URI=bolt://localhost:7687
DDIGRAPH_NEO4J_USER=neo4j
DDIGRAPH_NEO4J_PASSWORD=password

Sécurité

Ne versionnez jamais le fichier .env dans votre dépôt Git. Ajoutez-le à votre .gitignore.


Amorcer le schéma

Avant de charger des données, créez les contraintes d'unicité et les index dont le chargeur a besoin :

# Schéma Codebook uniquement
ddigraph bootstrap

# Inclure également le schéma DDI-L FragmentInstance
ddigraph bootstrap

La commande est idempotente : l'exécuter plusieurs fois est sans danger.

Que fait cette commande ?

bootstrap crée des contraintes d'unicité sur les identifiants de noeuds et des index sur les propriétés couramment interrogées. Cela garantit l'intégrité des données et accélère les requêtes.


Charger un fichier DDI

Via la CLI

La CLI auto-détecte si le fichier utilise le format DDI Codebook ou DDI-L FragmentInstance :

# DDI Codebook -- un identifiant de jeu de données est requis
ddigraph load codebook_survey.xml --dataset-id my-survey

# DDI-L FragmentInstance -- pas besoin d'identifiant de jeu de données
ddigraph load questionnaire.xml

Un chargement réussi affiche un récapitulatif :

Ingestion complete: Category=45, CodeList=12, Instrument=1, QuestionItem=120, Sequence=30

Exécution à blanc

Ajoutez --dry-run pour valider le XML sans rien écrire dans Neo4j :

ddigraph load survey.xml --dataset-id demo --dry-run

Via l'API Python

import asyncio
from neo4j import AsyncGraphDatabase
from ddigraph import DDILoader, DDIFragmentLoader, detect_ddi_format, Settings

async def main():
    settings = Settings()
    driver = AsyncGraphDatabase.driver(
        settings.neo4j_uri,
        auth=(settings.neo4j_user, settings.neo4j_password.get_secret_value()),
    )

    path = "survey.xml"

    # Auto-détection du format
    fmt = detect_ddi_format(path)
    print(f"Format détecté : {fmt}")

    if fmt == "lifecycle":
        loader = DDIFragmentLoader(driver, settings=settings)
        result = await loader.load(path)
    else:
        loader = DDILoader(driver, settings=settings)
        result = loader.load(
            path,
            dataset_id="my-survey",
            dataset_name="Mon enquête",
        )

    print(result)
    await driver.close()

asyncio.run(main())

Async/await

ddigraph utilise des E/S asynchrones pour l'écriture dans Neo4j. Le chargeur Codebook (DDILoader) propose une méthode synchrone load(), tandis que le chargeur Lifecycle (DDIFragmentLoader) est entièrement asynchrone.


Explorer le graphe avec Cypher

Ouvrez le navigateur Neo4j à l'adresse http://localhost:7474 et essayez quelques requêtes.

Compter les noeuds par type :

CALL db.labels() YIELD label
RETURN label, count { MATCH (n) WHERE label IN labels(n) RETURN n } AS count
ORDER BY count DESC

Lister tous les jeux de données :

MATCH (d:Dataset) RETURN d.id, d.name

Trouver les variables liées à une question :

MATCH (v:Variable)-[:ASKS]->(q:Question)
RETURN v.label AS variable, q.text AS texte_question
LIMIT 10

Explorer le flux de contrôle d'un instrument DDI-L :

MATCH path = (i:Instrument)-[:HAS_CONSTRUCT*1..3]->(n)
RETURN path
LIMIT 50

Trouver les questions qui référencent une liste de codes :

MATCH (q:QuestionItem)-[:USES_CODELIST]->(cl:CodeList)
RETURN q.name AS question, cl.name AS liste_de_codes, cl.code_count
ORDER BY cl.code_count DESC
LIMIT 10

Auto-détection du format DDI

ddigraph peut vous indiquer le format d'un fichier sans le charger :

ddigraph detect survey.xml
# Format: codebook
# File: survey.xml

ddigraph detect questionnaire.xml --json
# {"path":"questionnaire.xml","format":"lifecycle"}

En Python :

from ddigraph import detect_ddi_format

fmt = detect_ddi_format("my_file.xml")
print(fmt)  # "codebook" ou "lifecycle"

La détection ne lit que l'élément racine du XML : elle est donc rapide même sur les fichiers volumineux.


Utiliser des backends alternatifs

Neo4j est le backend principal, mais ddigraph peut écrire vers d'autres systèmes graphe grâce à son patron adaptateur. Voici un exemple rapide avec NetworkX qui ne nécessite aucune base de données :

import asyncio
from pathlib import Path
import networkx as nx
from ddigraph.ingest.fragment_loader import DDIFragmentParser

async def load_to_networkx(path: str) -> nx.MultiDiGraph:
    G = nx.MultiDiGraph()
    parser = DDIFragmentParser(Path(path))

    for batch in parser.parse_batches():
        # Ajouter les noeuds
        for element_type, fragments in batch.fragments_by_type.items():
            for fragment in fragments:
                props = fragment.to_dict()
                node_id = props.pop("fragment_id")
                G.add_node(node_id, node_type=element_type, **props)

        # Ajouter les arêtes
        for from_id, rel_type, to_id in batch.relationships:
            G.add_edge(from_id, to_id, key=rel_type, type=rel_type)

    return G

G = asyncio.run(load_to_networkx("questionnaire.xml"))
print(f"Noeuds : {G.number_of_nodes()}, Arêtes : {G.number_of_edges()}")

Autres backends

Le répertoire demo/ du dépôt contient des exemples complets pour RDF/SPARQL, Gremlin et pandas.


Prochaines étapes

Vous maîtrisez désormais le flux de travail principal : installer, connecter, amorcer, charger, interroger. Voici quelques pistes pour aller plus loin :

Bonne exploration !