Polars - Guide de la bibliothèque DataFrame rapide
Polars est une bibliothèque DataFrame haute performance écrite en Rust avec des bindings Python, R, et Node. Elle est multi-threadée par défaut, construite sur la mémoire columnar Apache Arrow, et offre une API lazy avec un optimiseur de requête qui réordonne et élague le travail avant exécution. Sur les grands datasets elle est typiquement plusieurs fois plus rapide que pandas et utilise moins de mémoire, tandis que son API expression se compose plus proprement. Elle est devenue la recommandation par défaut pour les nouveaux travaux de data en 2026.
Installation
| Méthode | Commande |
|---|
| pip | pip install polars |
| uv | uv add polars |
| Avec extras | pip install "polars[all]" (excel, database, plotting) |
| Vérifier | python -c "import polars as pl; print(pl.__version__)" |
Lire & écrire
| Opération | Code |
|---|
| CSV | pl.read_csv("data.csv") |
| Parquet | pl.read_parquet("data.parquet") |
| JSON | pl.read_json("data.json") |
| Base de données | pl.read_database(query, connection) |
| Lazy CSV | pl.scan_csv("data.csv") (différé) |
| Lazy Parquet | pl.scan_parquet("*.parquet") |
| Écrire | df.write_parquet("out.parquet") |
Eager vs Lazy (l’idée clé)
import polars as pl
# Eager : s'exécute immédiatement
df = pl.read_csv("sales.csv")
result = df.filter(pl.col("amount") > 100).group_by("region").agg(pl.col("amount").sum())
# Lazy : construit un plan, optimise, puis exécute sur .collect()
result = (
pl.scan_csv("sales.csv")
.filter(pl.col("amount") > 100)
.group_by("region")
.agg(pl.col("amount").sum())
.collect()
)
Le mode lazy permet à l’optimiseur de pousser les filtres vers le scan et de ne lire que les colonnes nécessaires — souvent une grande accélération sur les gros fichiers. Préférez scan_* + .collect() pour les vrais workloads.
Expressions (l’API core)
| Expression | Fait |
|---|
pl.col("x") | Référencer une colonne |
pl.col("x").sum() | Agréger |
pl.col("x").alias("y") | Renommer la sortie |
pl.col("*") | Toutes les colonnes |
pl.lit(5) | Valeur littérale |
pl.when(cond).then(a).otherwise(b) | Conditionnel |
Sélectionner & filtrer
| Tâche | Code |
|---|
| Sélectionner des colonnes | df.select("a", "b") |
| Colonne calculée | df.with_columns((pl.col("a") * 2).alias("a2")) |
| Filtrer les lignes | df.filter(pl.col("a") > 10) |
| Plusieurs conditions | df.filter((pl.col("a") > 10) & (pl.col("b") == "x")) |
| Head/tail | df.head(10), df.tail(10) |
| Trier | df.sort("a", descending=True) |
| Unique | df.unique(subset=["a"]) |
Agrégation et groupement
(df.group_by("region")
.agg(
pl.col("amount").sum().alias("total"),
pl.col("amount").mean().alias("avg"),
pl.col("order_id").n_unique().alias("orders"),
))
| Agrégation | Expression |
|---|
| Sum/mean/min/max | .sum(), .mean(), .min(), .max() |
| Count | .count(), .n_unique() |
| First/last | .first(), .last() |
| Quantile | .quantile(0.95) |
| List collect | .implode() |
Joins
| Type | Code |
|---|
| Inner | a.join(b, on="id") |
| Left | a.join(b, on="id", how="left") |
| Outer | a.join(b, on="id", how="full") |
| Anti | a.join(b, on="id", how="anti") |
| Semi | a.join(b, on="id", how="semi") |
| As-of (time) | a.join_asof(b, on="ts") |
Strings, dates, listes
| Namespace | Exemple |
|---|
.str | pl.col("s").str.to_uppercase(), .str.contains("x") |
.dt | pl.col("t").dt.year(), .dt.truncate("1h") |
.list | pl.col("l").list.len(), .list.first() |
.cast | pl.col("a").cast(pl.Int64) |
Interop
| Cible | Code |
|---|
| pandas | df.to_pandas() / pl.from_pandas(pdf) |
| Arrow | df.to_arrow() |
| NumPy | df.to_numpy() |
| DuckDB | Interroger les frames Polars directement depuis DuckDB |
Polars vs pandas
| Aspect | Polars | pandas |
|---|
| Moteur | Rust, multi-threadé | Python/C, surtout single-threaded |
| Mémoire | Arrow columnar | NumPy-backed |
| Optimiseur lazy | Oui | Non |
| API | Expression-based, composable | Centré sur l’index |
| Écosystème | Croissance rapide | Vaste, mature |
Pour l’analytics SQL-first sur fichiers, associez-la à DuckDB ; pandas reste valide pour la largeur de son écosystème.
Ressources