Pandas & NumPy Academy · Leçon

Structurer les étapes de transformation en fonctions

Découpez votre notebook en fonctions d’extraction, de transformation et de chargement, chacune acceptant et renvoyant un DataFrame pour faciliter les tests.

Leçon 1 sur 413 étapes

Structurer les étapes de transformation en fonctions est une leçon Pandas & NumPy Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Pandas & NumPy Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Pandas & NumPy Academy comprend 4 leçons au total.

Aller au-delà des notebooks

Les notebooks Jupyter sont parfaits pour l’exploration, mais peu adaptés aux chaînes de traitement de données destinées à la production. Du code réparti dans 50 cellules, avec un état global et aucun test, est fragile : la modification d’une cellule peut silencieusement en interrompre une autre. L’alternative professionnelle consiste à extraire chaque transformation dans une fonction nommée qui accepte un DataFrame et renvoie un DataFrame. Cette séparation des responsabilités constitue le fondement d’un code d’ingénierie des données facile à maintenir.

# Notebook-style (fragile)
df = pd.read_csv('orders.csv')
df = df.dropna(subset=['revenue'])
df = df[df['quantity'] > 0]
df['revenue_per_unit'] = df['revenue'] / df['quantity']

# Function-style (robust)
def extract(path):
    return pd.read_csv(path)

def transform(df):
    return (
        df.dropna(subset=['revenue'])
        .query('quantity > 0')
        .assign(revenue_per_unit=lambda d: d['revenue'] / d['quantity'])
    )

df = transform(extract('orders.csv'))

Le modèle ETL : extraire, transformer, charger

Le modèle ETL divise une chaîne de traitement des données en trois phases : Extract (lecture depuis la source), Transform (nettoyage et enrichissement) et Load (écriture vers la destination). Chaque phase est une fonction distincte. Cette séparation permet facilement de remplacer les sources de données (CSV ou base de données), de modifier la logique de nettoyage ou de changer le format de sortie sans toucher aux deux autres phases. Toute chaîne de traitement destinée à la production devrait suivre cette structure.

def extract(config):
    return pd.read_csv(config['input_path'], parse_dates=['order_date'])

def transform(df, config):
    return (
        df
        .dropna(subset=config['required_cols'])
        .query('quantity > 0')
        .assign(revenue=lambda d: d['quantity'] * d['unit_price'])
    )

def load(df, config):
    df.to_parquet(config['output_path'], index=False)
    print(f'Saved {len(df)} rows.')

Principe de responsabilité unique

Chaque fonction de transformation doit faire exactement une seule chose. Une fonction appelée clean_data() qui supprime les valeurs nulles, plafonne les valeurs aberrantes, analyse les dates et encode les catégories est difficile à tester et à déboguer. Écrivez plutôt drop_nulls(), cap_outliers(), parse_dates() et encode_categories() comme des fonctions distinctes. Cette granularité permet d’ignorer, de remplacer ou de réordonner facilement n’importe quelle étape.

def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

def remove_returns(df):
    return df[df['quantity'] > 0]

def compute_revenue(df):
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

def add_date_features(df):
    return df.assign(
        year=lambda d: d['order_date'].dt.year,
        month=lambda d: d['order_date'].dt.month
    )

Enchaîner les étapes avec pipe()

Reliez les fonctions à responsabilité unique à l’aide de pipe() afin de construire toute la phase de transformation sous forme d’une chaîne lisible. La chaîne se lit comme une recette : chaque ligne est une étape et les données s’écoulent de haut en bas. Vous pouvez commenter ou réordonner n’importe quelle étape sans renommer les variables. Le résultat final est le DataFrame propre et enrichi, prêt pour la phase de chargement.

import pandas as pd

REQUIRED = ['order_id', 'revenue', 'order_date']

def transform(raw_df):
    return (
        raw_df
        .pipe(drop_null_rows, required_cols=REQUIRED)
        .pipe(remove_returns)
        .pipe(compute_revenue)
        .pipe(add_date_features)
    )

df_clean = transform(pd.read_csv('orders.csv', parse_dates=['order_date']))
print(df_clean.shape)

Renvoyer le nombre de lignes pour l’audit

Chaque fonction de transformation devrait pouvoir journaliser le nombre de lignes reçues et renvoyées. Entourer le corps de la fonction d’une journalisation du nombre de lignes avant et après constitue une piste d’audit légère, qui permet de voir rapidement où des lignes sont supprimées lors d’une exécution de la chaîne de traitement. Stockez ces nombres dans une liste et affichez-les sous forme de rapport à la fin de chaque exécution.

audit_log = []

def audited(func):
    def wrapper(df, *args, **kwargs):
        before = len(df)
        result = func(df, *args, **kwargs)
        after = len(result)
        audit_log.append({'step': func.__name__, 'in': before, 'out': after, 'dropped': before - after})
        return result
    return wrapper

@audited
def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

Des fonctions testables avec de petits DataFrames

Le principal avantage des fonctions nommées est leur capacité à être testées. Créez un petit DataFrame de test représentant un cas limite réaliste et vérifiez le résultat de la fonction. Testez la fonction drop_null_rows avec une ligne contenant une valeur nulle et vérifiez qu’elle est supprimée, puis avec une autre ligne sans valeur nulle et vérifiez qu’elle est conservée. Les tests unitaires des fonctions de transformation détectent les régressions lorsque le code de la chaîne de traitement évolue.

import pandas as pd

def test_drop_null_rows():
    test_df = pd.DataFrame({
        'order_id': [1, 2, 3],
        'revenue': [100.0, None, 200.0]
    })
    result = drop_null_rows(test_df, required_cols=['revenue'])
    assert len(result) == 2, 'Should have 2 non-null rows'
    assert result['revenue'].isna().sum() == 0, 'No nulls in revenue'
    print('test_drop_null_rows PASSED')

test_drop_null_rows()

Organiser les fonctions en modules

À mesure que la chaîne de traitement s’agrandit, répartissez les fonctions dans des fichiers de modules Python : extract.py, transform.py, load.py et validate.py. Le script principal pipeline.py les importe et les orchestre. Cette organisation des fichiers est lisible, testable avec pytest et déployable sous forme de paquet Python. Elle correspond à la structure standard utilisée par les équipes d’ingénierie des données qui emploient des outils comme dbt ou Airflow.

# pipeline.py
# from extract import extract_orders
# from transform import transform
# from load import load_to_parquet
# from validate import validate_sales_df

# def run_pipeline(config):
#     raw = extract_orders(config)
#     clean = transform(raw, config)
#     validate_sales_df(clean)
#     load_to_parquet(clean, config)

print('Module-based pipeline structure shown above (imports commented for demo)')

Idempotence : exécuter plusieurs fois sans risque

Une fonction de chaîne de traitement bien conçue est idempotente : l’exécuter deux fois sur la même entrée produit le même résultat et ne provoque aucun effet secondaire. Évitez les modifications sur place (df.drop(..., inplace=True)) et utilisez toujours df.copy() au début des fonctions qui modifient les colonnes. Les fonctions idempotentes peuvent être réexécutées après une défaillance sans altérer les données de sortie.

def add_revenue_flag(df, threshold=500):
    # Use copy to avoid mutating the input
    df = df.copy()
    df['is_large_order'] = df['revenue'] >= threshold
    return df

# Running twice gives the same result
df1 = add_revenue_flag(df_clean)
df2 = add_revenue_flag(df_clean)
assert df1.equals(df2), 'Function is not idempotent!'
print('Idempotency check passed.')

Les annotations de type pour s’auto-documenter

Ajoutez des annotations de type Python aux signatures des fonctions de transformation afin de rendre le contrat explicite : def transform(df: pd.DataFrame) -> pd.DataFrame. Les annotations de type s’auto-documentent : tout développeur qui lit la fonction sait exactement ce qu’elle attend et ce qu’elle renvoie sans avoir à lire son corps. Elles permettent également aux outils d’analyse statique comme mypy et à la complétion automatique des environnements de développement de détecter les erreurs de type avant l’exécution.

from typing import List

def drop_null_rows(df: pd.DataFrame, required_cols: List[str]) -> pd.DataFrame:
    return df.dropna(subset=required_cols)

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

print('Type-annotated functions ready for production use.')

Documenter chaque étape avec des docstrings

Chaque fonction de transformation devrait comporter une docstring d’une ligne indiquant ce qu’elle fait, les colonnes dont elle a besoin et celles qu’elle ajoute ou supprime. De bonnes docstrings rendent la fonction accessible avec help() et dans les infobulles de l’environnement de développement. Elles servent également de documents de spécification : une assertion en échec qui contredit la docstring est un bogue ; une docstring qui ne correspond pas au code est une erreur de documentation.

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    """Add 'revenue' column as quantity * unit_price.

    Requires: 'quantity' (numeric), 'unit_price' (numeric) columns.
    Returns: df with new 'revenue' float column appended.
    """
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

help(compute_revenue)

Exécuter la chaîne de traitement complète

Orchestrez l’ETL complet en appelant les trois phases dans l’ordre : extract, transform et load. Ajoutez une mesure du temps autour de chaque phase afin de déterminer où le temps est consacré. Interceptez séparément les exceptions de chaque phase afin que les messages d’erreur indiquent celle qui a échoué. Journalisez l’heure de début et de fin de l’exécution complète, ainsi que sa réussite ou son échec, à des fins de supervision.

import time

CONFIG = {
    'input_path': 'orders.csv',
    'output_path': 'orders_clean.parquet',
    'required_cols': ['order_id', 'revenue']
}

t0 = time.time()
raw = extract(CONFIG)
clean = transform(raw, CONFIG)
load(clean, CONFIG)

print(f'Pipeline completed in {time.time()-t0:.1f}s')
print(f'Audit log: {audit_log}')

Vérification rapide

Vérifiez votre compréhension des concepts d’analyse des données abordés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris à : structurer les chaînes de traitement sous forme de fonctions ETL à responsabilité unique, rendre les fonctions testables, idempotentes et auto-documentées grâce aux annotations de type et aux docstrings, et orchestrer la chaîne de traitement complète avec une mesure du temps et une journalisation d’audit. Nous allons maintenant étudier le paramétrage des chaînes de traitement à l’aide de dictionnaires de configuration pour les réutiliser avec différents jeux de données.

Gratuit pour commencer

Apprends Python avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
30
Leçons
120

Questions Fréquemment Posées

La leçon « Structurer les étapes de transformation en fonctions » est-elle gratuite ?

Oui — le texte complet de « Structurer les étapes de transformation en fonctions » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Pandas & NumPy Academy, passe à CoddyKit PRO. Le cours Pandas & NumPy Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Structurer les étapes de transformation en fonctions » ?

Découpez votre notebook en fonctions d’extraction, de transformation et de chargement, chacune acceptant et renvoyant un DataFrame pour faciliter les tests. Tu pratiques Pandas & NumPy Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Pandas & NumPy Academy ?

Aucune expérience préalable n'est requise. Pandas & NumPy Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Structurer les étapes de transformation en fonctions » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Pandas & NumPy Academy ?

Oui. Chaque leçon Pandas & NumPy Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Structurer les étapes de transformation en fonctions
  2. Paramétrer les pipelines avec des dictionnaires de configuration
  3. Tester les étapes du pipeline avec des assertions
  4. Planifier et journaliser les exécutions du pipeline
← Retour à Pandas & NumPy Academy