Principes d'un pipeline ETL

Première leçon du cours Pipelines ETL simples, en accès libre : environ 15 minutes, 3 exercices et 3 questions de quiz. C'est la leçon de base du catalogue ; dans l'espace étudiant, elle est réécrite pour ton profil.

Objectifs

  • Décrire les étapes extraire, transformer, charger et la différence entre ETL et ELT
  • Structurer un pipeline en fonctions testables
  • Expliquer et vérifier l'idempotence d'un traitement

Étapes ETLETL et ELTIdempotence

Chaque lundi, une analyste télécharge trois exports, les nettoie dans un tableur, les colle dans un fichier de suivi. Une erreur de copier-coller, et le comité de direction reçoit de faux chiffres. Un pipeline ETL automatise ce travail de façon fiable et reproductible.

Les trois étapes

  • Extract (extraire) : lire les données là où elles sont (fichiers CSV, API, base de production) sans les modifier.
  • Transform (transformer) : nettoyer, typer, normaliser, joindre, agréger, calculer des indicateurs.
  • Load (charger) : écrire le résultat dans la destination (entrepôt de données, base analytique, fichier).

ETL ou ELT ?

En ETL, on transforme avant de charger. En ELT, on charge d’abord les données brutes dans l’entrepôt, puis on transforme en SQL directement dans l’entrepôt. L’ELT s’est largement répandu avec les entrepôts cloud puissants et des outils comme dbt : on garde l’historique brut et on peut rejouer les transformations. Les principes de ce cours valent pour les deux : seules les étapes changent de place.

Découper en fonctions

Un pipeline lisible se compose de petites fonctions à responsabilité unique :

import io
import pandas as pd

def extraire(texte_csv):
    # lit la source sans rien modifier
    return pd.read_csv(io.StringIO(texte_csv))

def transformer(df):
    # fonction pure : ne modifie pas son entrée, renvoie un nouveau DataFrame
    sortie = df.copy()
    sortie["client"] = sortie["client"].str.strip().str.lower()
    sortie = sortie[sortie["montant"] > 0]
    return sortie.groupby("client", as_index=False)["montant"].sum()

def charger(df, destination):
    # remplace entièrement le contenu de la destination
    destination.clear()
    destination.extend(df.to_dict("records"))

source = "client,montant\n Alice ,10\nbob,5\nalice,7\nbob,-2\n"
entrepot = []                       # une liste joue ici le rôle de la destination
charger(transformer(extraire(source)), entrepot)
print(entrepot)   # [{'client': 'alice', 'montant': 17}, {'client': 'bob', 'montant': 5}]

Une fonction pure renvoie toujours le même résultat pour la même entrée et ne modifie rien autour d’elle. On peut la tester avec un petit DataFrame, sans base de données ni réseau.

L’idempotence

Un pipeline sera relancé : après une panne, une correction, un rattrapage. Il doit être idempotent : le relancer sur les mêmes données produit le même état final, sans doublons.

charger(transformer(extraire(source)), entrepot)   # deuxième exécution
print(len(entrepot))                               # toujours 2 lignes : idempotent

Si charger faisait un simple ajout (extend sans clear), la seconde exécution doublerait les données : c’est l’un des bugs les plus fréquents en production. Les stratégies idempotentes sont le remplacement (d’une table ou d’une partition, par exemple une journée) et l’upsert (insérer ou mettre à jour selon une clé), vus en leçon 5.

Paramétrer plutôt que coder en dur

La date traitée, le chemin source ou la connexion sont des paramètres : executer_pipeline(date="2026-11-09", source=..., destination=...). On peut alors rejouer n’importe quel jour et tester avec des données factices.

À retenir

  • Extraire sans modifier, transformer avec des fonctions pures, charger de façon idempotente.
  • ETL et ELT diffèrent par l’endroit où se fait la transformation.
  • Paramètres explicites ; tester qu’une double exécution ne change rien.

Exercices

Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.

  1. CodeDifficulté 2Python dans le navigateur2 indices

    Un mini pipeline idempotent

    Complète transformer(df) (sans modifier df : nettoie client avec strip + lower, garde les montants strictement positifs, puis somme par client avec as_index=False, trié par client) et charger(df, destination) (remplace le contenu de la liste destination par les enregistrements de df). Le code de test exécute le pipeline deux fois.

    import io
    import pandas as pd
    
    def extraire(texte_csv):
        return pd.read_csv(io.StringIO(texte_csv))
    
    def transformer(df):
        pass
    
    def charger(df, destination):
        pass
    
    source = 'client,montant\n Alice ,10\nbob,5\nalice,7\nbob,-2\n'
  2. QCMDifficulté 21 indice

    ETL ou ELT

    Une équipe charge chaque nuit les tables brutes de l’application dans son entrepôt cloud, puis construit les tables analytiques avec des requêtes SQL dans l’entrepôt. De quelle approche s’agit-il ?

    • ETL
    • ELT
    • Ni l'un ni l'autre
    • Un export manuel
  3. CodeDifficulté 2Python dans le navigateur1 indice

    Détecter un chargement non idempotent

    Écris est_idempotent(etape, entree) qui exécute etape(entree, destination) deux fois sur une même liste destination initialement vide et renvoie True si son contenu est identique après la première et la seconde exécution. Deux fonctions de chargement sont fournies pour tester.

    def charger_ajout(lignes, destination):
        destination.extend(lignes)
    
    def charger_remplacement(lignes, destination):
        destination[:] = list(lignes)
    
    def est_idempotent(etape, entree):
        pass

Quiz de fin de leçon

  1. Que signifie qu’un pipeline est idempotent ?

    • Il est rapide
    • Le relancer sur les mêmes données produit le même état final
    • Il ne lit qu'une source
  2. Qu’est-ce qu’une fonction pure ?

    • Une fonction sans paramètre
    • Une fonction qui renvoie toujours le même résultat pour la même entrée, sans effet de bord
    • Une fonction écrite en SQL
  3. Pourquoi passer la date traitée en paramètre ?

    • Pour pouvoir rejouer n'importe quel jour et tester
    • Pour accélérer le code
    • Parce que pandas l'exige

La suite du cours

  1. Extraction : CSV, JSON et bases SQL18 min
  2. Transformation : nettoyage, typage et jointures sûres20 min
  3. Contrôles de qualité et mise en quarantaine18 min
  4. Chargement : upsert, transactions et incrémental20 min
  5. Orchestration, journalisation et fiabilité18 min

Pipelines ETL simples · Avancé · 3 h 49