DuckDB : La petite base qui monte, qui monte... 🦆
DataGyver #5 : L’alternative SQL moderne pour vos gros fichiers
Bienvenue à la 5ème édition de cette newsletter !
Nous allons bientôt passer le cap des 700 abonné.e.s à cette Newsletter. Merci pour votre confiance, vos partages, vos retours 🙏.
Dans ce numéro, j’espère vous faire gagner du temps dans vos analyses ;)
Il y a quelques années, Pandas était mon meilleur ami. Enfin, c'était surtout mon seul ami capable de manipuler de gros fichiers CSV par exemple. Mais plus mes projets devenaient ambitieux, plus Pandas montrait ses limites : trop lent, trop gourmand en mémoire, trop fragile.
C'est à ce moment-là, autour de 2019, que DuckDB a commencé à faire parler de lui. Développé par une équipe de chercheurs de la Vrije Universiteit Amsterdam, DuckDB est une base de données OLAP intégrée directement dans Python. Pensez à SQLite, mais conçu spécialement pour l'analyse de données.
OLAP vs OLTP : c'est quoi la différence ?
Laissez-moi vous expliquer avec une analogie que j'aime bien. Imaginez deux types de restaurants. D'un côté, vous avez McDonald's : super efficace pour traiter plein de petites commandes rapidement. Une personne veut un burger, l'autre des nuggets, et hop, c'est servi en 2 minutes. Ça, c'est l'approche OLTP de PostgreSQL ou MySQL.
De l'autre côté, vous avez le traiteur qui prépare votre buffet de mariage. Il doit analyser les préférences de 200 invités, calculer les quantités, prévoir les options végétariennes... C'est une analyse globale, pas du cas par cas. Voilà l'approche OLAP de DuckDB.
Pourquoi Pandas nous lâche sur les gros volumes
Je vais être honnête : j'adore Pandas. Mais quand vous lui demandez de charger un CSV de 2 GB, que se passe-t-il réellement ?
df = pd.read_csv('sales_2024.csv') # Aïe...
Pandas charge absolument tout en mémoire. Les colonnes dont vous avez besoin, mais aussi toutes les autres. Si votre fichier contient 50 colonnes et que vous n'en utilisez que 3, tant pis, les 50 sont chargées. Votre RAM pleure, votre swap s'active, et vous partez vous faire un café en attendant.
L'intelligence de DuckDB
DuckDB aborde le problème différemment. Au lieu de tout charger bêtement, il analyse d'abord ce dont vous avez vraiment besoin :
import duckdb
result = duckdb.sql("""
SELECT region, SUM(amount) as total
FROM 'sales_2024.csv'
GROUP BY region
""").df()
Vous voyez la différence ? DuckDB lit directement le fichier et ne prend que les colonnes region
et amount
. Les 48 autres colonnes ? Il les ignore complètement. Résultat : une exécution beaucoup plus rapide et une consommation mémoire ridicule en comparaison.
Testez DuckDB directement dans votre navigateur !
Avant même d'installer quoi que ce soit, vous pouvez tester DuckDB directement sur https://shell.duckdb.org/. C'est l'environnement parfait pour vous faire la main avec quelques requêtes SQL et comprendre la puissance de l'outil. Pas d'excuse pour ne pas essayer !
Du CSV au Parquet : pourquoi j'ai changé
Depuis que j'ai découvert le format Parquet, je ne jure plus que par lui. Le CSV, c'est pratique pour échanger des données, mais pour l'analyse, c'est une catastrophe. Parquet stocke les données par colonne, pas par ligne. Quand DuckDB veut lire une colonne spécifique, il sait exactement où la trouver sans parcourir tout le fichier.
En plus, Parquet compresse intelligemment. Un CSV de 4 GB devient souvent un Parquet de 1 GB, sans perte d'information. Et la lecture est plus rapide. Que demander de plus ? :)
Un benchmark qui parle : NYC Yellow Taxi
Pour vous montrer la différence entre Pandas et DuckDB, j’ai réalisé un test simple avec le dataset bien connu des taxis new-yorkais : NYC Yellow Taxi – Janvier 2022.
Le fichier pèse environ 38 Mo au format Parquet et contient 2,4 millions de trajets.
from functools import wraps
import time
def mesure_temps(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
duree = time.time() - start
print(f"{func.__name__} : {duree:.2f} secondes")
return result
return wrapper
Avec Pandas, voici ce que ça donne :
import pandas as pd
@mesure_temps
def analyser_avec_pandas(df):
df['pickup_date'] = df['tpep_pickup_datetime'].dt.date
return df.groupby('pickup_date')['total_amount'].agg(['sum', 'mean', 'count'])
df = pd.read_parquet('yellow_taxi.parquet')
revenue_by_day = analyser_avec_pandas(df)
Sur mon laptop :
Lecture : 0.18 secondes
Agrégation : 0.37 secondes
Total : 0.55 secondes
Maintenant avec DuckDB :
import duckdb
@mesure_temps
def analyser_avec_duckdb():
return duckdb.sql("""
SELECT
DATE_TRUNC('day', tpep_pickup_datetime) AS pickup_date,
SUM(total_amount) AS revenue,
AVG(total_amount) AS avg_amount,
COUNT(*) AS trip_count
FROM 'yellow_taxi.parquet'
GROUP BY pickup_date
""").df()
result = analyser_avec_duckdb()
Temps mesuré :
Lecture + agrégation : 0.11 secondes
Soit une vitesse 4,8 fois supérieure à celle de Pandas pour cette tâche.
Requête plus complexe : top zones par jour
Objectif : classer les 5 zones les plus rentables chaque jour et suivre leur évolution avec ROW_NUMBER()
et LAG()
.
Pandas : 0.63 secondes
DuckDB : 0.16 secondes
DuckDB est ici 4 fois plus rapide, tout en conservant un code SQL concis.
Comparaison mémoire
Quand on lit le fichier Parquet avec Pandas, tout est chargé en mémoire : toutes les colonnes, toutes les lignes. Résultat : +380 Mo consommés.
Avec DuckDB, on exécute directement une requête SQL sur le fichier Parquet. Seules les colonnes utiles sont lues, sans chargement complet. Résultat : +0.4 Mo pour la même analyse.
👉 La comparaison n'est pas strictement équivalente : Pandas charge tout, DuckDB interroge à la volée. Mais c'est justement là que DuckDB brille.
Les erreurs classiques quand on débute
Après avoir aidé plusieurs collègues à adopter DuckDB, j'ai identifié quelques pièges récurrents. Ça vous évitera de perdre du temps :
Le piège des chemins de fichiers. Sur Windows, attention aux backslashes :
# ❌ Ne marche pas
duckdb.sql("SELECT * FROM 'C:\Users\data\file.csv'")
# ✅ Utilisez des slashes ou des raw strings
duckdb.sql("SELECT * FROM 'C:/Users/data/file.csv'")
duckdb.sql(r"SELECT * FROM 'C:\Users\data\file.csv'")
La gestion des dates peut surprendre. DuckDB est strict sur les formats :
# ❌ Format ambigu
WHERE date = '01-06-2024'
# ✅ Format ISO toujours
WHERE date = '2024-06-01'
# ✅ Ou utiliser des fonctions de parsing
WHERE strptime(date_column, '%d-%m-%Y') = DATE '2024-06-01'
La différence entre mémoire et persistance. Par défaut, DuckDB travaille en mémoire :
# Base en mémoire (perdue à la fermeture)
conn = duckdb.connect()
# Base persistante sur disque
conn = duckdb.connect('ma_base.db')
Attention au type de résultat. Le .df()
à la fin n'est pas optionnel si vous voulez un DataFrame :
# Retourne un objet DuckDBPyRelation
result = duckdb.sql("SELECT * FROM 'data.csv'")
# Retourne un DataFrame pandas
df = duckdb.sql("SELECT * FROM 'data.csv'").df()
L'installation la plus simple du monde
Si vous êtes habitué aux galères d'installation de bases de données, DuckDB va vous surprendre :
pip install duckdb
C'est tout. Pas de serveur à lancer, pas de port à configurer, pas de mot de passe root à définir. DuckDB s'exécute directement dans votre processus Python, comme une bibliothèque normale.
Pour les développeurs seniors : sous le capot
Si vous êtes curieux de comprendre pourquoi DuckDB est si rapide, voici les secrets de sa performance.
Architecture columnar et vectorisation. Contrairement aux bases orientées lignes, DuckDB stocke et traite les données par colonne. Mais le vrai secret, c'est la vectorisation : au lieu de traiter les données ligne par ligne, DuckDB traite des "vecteurs" de milliers de valeurs d'un coup. Le CPU moderne adore ça, et les performances s'envolent.
Le query optimizer intelligent. Avant d'exécuter votre requête, DuckDB l'analyse et la réorganise pour minimiser le travail. Par exemple, il va automatiquement pousser les filtres le plus tôt possible dans le plan d'exécution pour réduire le volume de données à traiter.
Parallélisation automatique. DuckDB découpe automatiquement le travail entre tous les cœurs de votre CPU. Une agrégation sur 10 millions de lignes ? Chaque cœur traite sa portion, puis les résultats sont fusionnés. Vous n'avez rien à configurer.
Gestion out-of-core. Quand les données dépassent la RAM disponible, DuckDB bascule intelligemment sur le disque, mais seulement pour les parties nécessaires. C'est transparent et reste performant grâce à des algorithmes optimisés pour minimiser les I/O.
Une question pour vous
Je réfléchis à créer une mini-formation sur DuckDB et le modern data stack. Est-ce que ça vous intéresserait ? J'aimerais vraiment avoir votre avis pour adapter le contenu à vos besoins.
Les livres pour aller plus loin
Si vous voulez vraiment maîtriser DuckDB, voici trois excellentes ressources :
DuckDB in Action de Mark Needham, Michael Hunger et Michael Simons est LA référence. Ce livre couvre tout : des bases de DuckDB à son intégration dans des pipelines complexes, en passant par l'utilisation avancée de SQL avec les window functions et les CTE. Les chapitres sur l'intégration avec Python et Streamlit sont particulièrement réussis. Bonus : MotherDuck offre une version PDF gratuite !
Getting Started with DuckDB de Simon Aubury et Ned Letcher (Packt, 2024) est parfait pour une approche pratique. Les exemples concrets en SQL, Python et R sont directement applicables. J'ai particulièrement apprécié les sections sur l'intégration avec Apache Arrow et Polars. Le repository GitHub qui accompagne le livre est une mine d'or.
DuckDB: Up and Running de Wei-Meng Lee sortira en janvier 2025 chez O'Reilly. J'ai eu la chance de lire une version preview. Le chapitre sur l'analyse spatiale avec l'extension spatial de DuckDB est fascinant. Si vous travaillez avec des données géographiques, ce livre sera indispensable.
Un projet complet pour vous lancer
J'ai préparé un repository GitHub avec tout ce qu'il faut pour démarrer : Ce repo contient une application Streamlit qui analyse les données des taxis jaunes de NYC en utilisant DuckDB. Il comprend une structure claire avec des scripts pour télécharger et analyser les données, ainsi qu'une interface utilisateur interactive. L'application offre des visualisations avec Plotly, permet le filtrage par date et distance (avec des paliers de 10 miles pour améliorer les performances), affiche des statistiques par heure et par jour, et analyse les méthodes de paiement. Elle inclut également une section pour exécuter des requêtes SQL personnalisées.
Projet Github disponible ici.
Votre mission cette semaine
Je vous lance un défi. Prenez un de vos gros fichiers CSV, celui qui fait ramer votre script. Installez DuckDB, convertissez votre fichier en Parquet, et comparez les performances avec votre approche actuelle. Je suis prêt à parier que vous serez bluffé par la différence.
N'hésitez pas à partager vos résultats en commentaire. Les meilleures comparaisons seront featured dans ma prochaine newsletter !
À très vite, Gaël
Bonus lecture : DuckLake, le Lakehouse simplifié façon DuckDB
Si vous avez aimé découvrir la puissance de DuckDB, vous allez adorer sa dernière innovation : DuckLake.
Imaginez un format Lakehouse capable de gérer vos Parquet avec toute la souplesse du SQL. Fini les systèmes complexes à base de fichiers JSON ou de catalogues obscurs. DuckLake centralise tout dans une base relationnelle (PostgreSQL, DuckDB ou autre) et rend la gestion des métadonnées enfin… humaine.
💡 À la clé :
des transactions ACID vraiment simples à implémenter
une gestion native des snapshots et du time travel
et surtout : une vitesse d’exécution bluffante, même en multi-utilisateur
C’est une vraie réinvention du Lakehouse, avec la philosophie minimaliste de DuckDB : tout faire en SQL, sans dépendances inutiles.
👉 L’article complet (par les créateurs de DuckDB) est une pépite technique : il explique la genèse de DuckLake, son architecture, ses promesses, et surtout comment l’utiliser dès aujourd’hui.
Je vous recommande vivement la lecture : lire l'article complet
Merci Gaël pour ce partage très triche. Je testerai DuckD, et je vous ferai un retour.