
Après avoir exploré les concepts théoriques de Delta Lake et Apache Iceberg, Marc vous confie une nouvelle mission : mettre les mains dans le code et réaliser vos premières manipulations concrètes avec Delta Lake. Vous avez compris l'architecture, le journal des transactions, les avantages de ce format ouvert. Maintenant, il est temps de créer vos premières tables Delta, d'y insérer des données agricoles de GreenFarm, et de maîtriser les opérations qui vous permettront de gérer efficacement vos données de capteurs IoT et de traçabilité. C'est parti !
Commençons par les bases : comment créer une table Delta et lire vos données. Ces deux opérations sont la fondation de toute utilisation de Delta Lake.
Delta Lake n’est pas qu’un format de fichier. C’est un système transactionnel complet au-dessus du stockage, qui combine la simplicité du Data Lake avec la fiabilité d’une base de données. Chaque table Delta est composée de :
fichiers Parquet (pour les données) ;
un dossier _delta_log (pour l’historique et les métadonnées).
Ce journal garantit que vos lectures seront toujours cohérentes, même si d’autres processus écrivent en parallèle.
Simulons un jeu de données de capteurs d’humidité et stockons-le au format Delta.
pip install pandas pyarrow deltalake
from deltalake import write_deltalake, DeltaTable
import pandas as pd
from pathlib import Path
from datetime import datetime, timezone
# Dossier de stockage local
path = Path("data/sensors_delta")
path.mkdir(parents=True, exist_ok=True)
# Données simulées
df = pd.DataFrame({
"sensor_id": [101, 102, 103],
"humidity": [42.1, 48.3, 44.9],
"parcel": ["North-1", "East-2", "South-3"]
})
write_deltalake(str(path), df)
print("Table Delta créée ! ✅")Lire une table Delta, c’est un peu comme “reconstituer son état le plus récent”. Delta parcourt son journal des transactions pour assembler une vue cohérente de la table à la version la plus récente.
table = DeltaTable(str(path))
print(table.to_pandas())Même si plusieurs écritures ont eu lieu en parallèle, Delta garantit une lecture intégralement cohérente : c’est l’un de ses grands atouts par rapport à un simple dossier de fichiers Parquet.
Dans un système Delta, l’écriture est aussi fiable que la lecture. Que vous vouliez remplacer l’intégralité d’une table, ou simplement y ajouter des lignes, toutes les opérations sont atomiques et historisées.
Le mode overwrite permet de remplacer complètement le contenu de la table, tout en créant une nouvelle version du journal.
overwrite_df = pd.DataFrame({
"sensor_id": [101, 102, 103],
"humidity": [41.5, 47.8, 43.9],
"parcel": ["North-1", "East-2", "South-3"]
})
write_deltalake(str(path), overwrite_df, mode="overwrite")
print("Table Delta remplacée par une nouvelle version.")
Cette opération est atomique : soit tout est remplacé, soit rien ne change.
Lorsque de nouveaux capteurs ou fichiers arrivent, le mode append permet d’enrichir la table sans supprimer les données existantes.
new_data = pd.DataFrame({
"sensor_id": [104, 105],
"humidity": [49.2, 43.0],
"parcel": ["West-1", "North-2"]
})
write_deltalake(str(path), new_data, mode="append")
print("Nouvelles mesures ajoutées ✅")Cette opération ajoute simplement de nouveaux fichiers Parquet, tout en consignant leur arrivée dans le journal _delta_log.
Le partitionnement consiste à organiser vos fichiers selon une clé logique, par exemple la date de mesure. Cela accélère considérablement les lectures ciblées.
df_part = pd.DataFrame({
"date": ["2025-11-08", "2025-11-09", "2025-11-10"],
"parcel": ["North-1", "East-2", "South-3"],
"humidity": [40.3, 46.1, 43.7]
})
write_deltalake("data/sensors_partitioned", df_part, partition_by=["date"])
print("📁 Table Delta partitionnée créée.")Delta Lake vous permet de manipuler vos données comme dans une base relationnelle classique :
vous pouvez supprimer, corriger, ou fusionner des enregistrements — toujours de manière transactionnelle.
table = DeltaTable(str(path))
table.delete("parcel = 'East-2'")
print("Données supprimées pour East-2.") Les fichiers contenant ces lignes ne sont pas immédiatement effacés : ils sont marqués comme obsolètes dans le journal, ce qui préserve l’historique complet.
table.update(
predicate="parcel = 'North-1'",
updates={"humidity": 45.2}
)
print("Donnée corrigée pour North-1.")Chaque nuit, GreenFarm reçoit des fichiers de mise à jour : certaines mesures changent, d’autres sont nouvelles. L’opération merge combine tout cela en une seule étape.
updates = pd.DataFrame({
"sensor_id": [102, 106],
"humidity": [47.0, 41.9],
"parcel": ["East-2", "West-3"]
})
(
table.merge(
source=updates,
predicate="source.sensor_id = target.sensor_id",
source_alias="source",
target_alias="target",
)
.when_matched_update(
updates={"humidity": "source.humidity", "parcel": "source.parcel"}
)
.when_not_matched_insert_all()
.execute()
)
print("Fusion terminée !")Une seule commande pour ajouter les nouveaux capteurs et mettre à jour les existants, sans doublon ni incohérence.
Vous avez maintenant écrit, corrigé, supprimé et fusionné des données. Mais comment retracer l’ensemble de ces opérations ? Delta Lake conserve un historique complet de tout ce qui s’est passé, version après version.
Le dossier _delta_log contient les fichiers JSON listant toutes les modifications apportées.
Chaque opération (WRITE, DELETE, MERGE, UPDATE) crée une nouvelle version.
history = table.history()
rows = []
for h in history:
rows.append({
"version": h.get("version"),
"operation": h.get("operation"),
"timestamp_utc": datetime.fromtimestamp(
h.get("timestamp", 0) / 1000, tz=timezone.utc
).isoformat(),
"readVersion": h.get("readVersion"),
"mode": (h.get("operationParameters") or {}).get("mode"),
"predicate": (h.get("operationParameters") or {}).get("predicate"),
"mergePredicate": (h.get("operationParameters") or {}).get("mergePredicate"),
"num_added_rows": (h.get("operationMetrics") or {}).get("num_added_rows"),
"num_output_rows": (h.get("operationMetrics") or {}).get("num_output_rows"),
"num_updated_rows": (h.get("operationMetrics") or {}).get("num_updated_rows"),
"num_deleted_rows": (h.get("operationMetrics") or {}).get("num_deleted_rows"),
"execution_time_ms": (h.get("operationMetrics") or {}).get("execution_time_ms"),
"engineInfo": h.get("engineInfo"),
})
df_hist = pd.DataFrame(rows).sort_values("version")
print(df_hist.to_string(index=False))Cet historique est la clé de la traçabilité et de la conformité. GreenFarm peut savoir exactement quand une donnée a été modifiée, et par quel processus.
Delta Lake permet de relire n’importe quelle version précédente de votre table :
old = DeltaTable(str(path), version=0)
print(old.to_pandas())C’est un véritable “machine à remonter le temps” des données : utile pour auditer, comparer, ou même restaurer un état antérieur.
Ce journal d’historique fait de Delta Lake un outil de gouvernance à part entière : il garantit la transparence, la traçabilité et la reproductibilité de chaque transformation de données.
Chez GreenFarm, cette fonctionnalité est cruciale : l’entreprise doit démontrer que les données issues de ses fermes et capteurs n’ont jamais été altérées, notamment pour ses certifications environnementales.
Vous venez de découvrir comment manipuler Delta Lake en Python natif, dans un environnement local simple. Mais Delta Lake n’est pas limité à Python : il fonctionne aussi avec Spark, Rust, Java, Scala, Go, et bien d’autres langages.

Vous travaillez sur des données issues d’un réseau de capteurs environnementaux.
Votre objectif est de manipuler une table Delta Lake locale, d’appliquer différentes opérations (ajout, correction, suppression, fusion) et d’explorer son historique.
Créer la table Delta
Importez le fichiersensor.csvdans un DataFrame Pandas.
Créez une table Delta locale à l’aide dewrite_deltalake().
Ajouter de nouvelles mesures
Simulez l’arrivée de nouveaux capteurs (sensor_id104 et 105).
Utilisez le modeappendpour enrichir la table.
Corriger une erreur de mesure
Supposons qu’un capteur ait envoyé une mauvaise valeur.
Corrigez la mesure du sensor_id = 101 à l’aide de update().
Supprimer une ligne obsolète
Supprimez la parcelle obsolète présente dans le fichier d’origine.
Fusionner de nouvelles données
Créez un DataFrameupdatescontenant :
une mesure corrigée (sensor_id = 102),
un nouveau capteur (sensor_id = 106).
Fusionnez ces données avec la table principale à l’aide demerge().
Explorer l’historique et le time travel
Utiliseztable.history()pour observer les versions créées après chaque opération d’écriture.
Relisez ensuite la version 0 de la table afin de la comparer avec la version finale.
Résultat attendu : À la fin, votre table doit contenir :
sensor_id | humidity | parcel |
101 | 145.2 | North-1 |
102 | 47.0 | East-2 |
103 | 44.5 | South-3 |
104 | 49.2 | West-1 |
105 | 43.0 | North-2 |
106 | 41.9 | West-3 |
L’historique doit montrer 5 versions d’écriture (de la version 0 à la version 4), correspondant aux différentes opérations effectuées sur la table.
Delta Lake offre plusieurs méthodes pour créer des tables (SQL DDL, API DeltaTableBuilder, DataFrameWriter), chacune adaptée à des besoins différents de contrôle et de simplicité.
Le partitionnement divise automatiquement les données en unités organisées par valeurs de colonnes, permettant de requêter sélectivement sans scanner l'ensemble des fichiers, avec un gain de performance majeur sur les tables de plus de 1 To.
Les opérations DML (DELETE, UPDATE, MERGE) créent de nouveaux fichiers et marquent les anciens comme obsolètes dans le journal des transactions, préservant la capacité de Time Travel sans suppression physique immédiate.
La commande MERGE combine UPDATE, DELETE et INSERT en une seule opération atomique, idéale pour gérer les données changeantes en maintenant l'intégrité de la plateforme.
Le journal des transactions enregistre chronologiquement chaque modification avec DESCRIBE HISTORY, garantissant une traçabilité complète et la possibilité de revenir à n'importe quelle version antérieure des données.
Bravo et continuez sur cette lancée !