Polars - Fast DataFrame Library Cheatsheet
Polars is a high-performance DataFrame library written in Rust with Python, R, and Node bindings. It is multi-threaded by default, built on Apache Arrow columnar memory, and offers a lazy API with a query optimizer that reorders and prunes work before executing. On large datasets it is typically many times faster than pandas and uses less memory, while its expression API composes more cleanly. It has become the default recommendation for new data work in 2026.
Installation
| Method | Command |
|---|
| pip | pip install polars |
| uv | uv add polars |
| With extras | pip install "polars[all]" (excel, database, plotting) |
| Verify | python -c "import polars as pl; print(pl.__version__)" |
Reading & Writing
| Operation | Code |
|---|
| CSV | pl.read_csv("data.csv") |
| Parquet | pl.read_parquet("data.parquet") |
| JSON | pl.read_json("data.json") |
| Database | pl.read_database(query, connection) |
| Lazy CSV | pl.scan_csv("data.csv") (deferred) |
| Lazy Parquet | pl.scan_parquet("*.parquet") |
| Write | df.write_parquet("out.parquet") |
Eager vs Lazy (the key idea)
import polars as pl
# Eager: executes immediately
df = pl.read_csv("sales.csv")
result = df.filter(pl.col("amount") > 100).group_by("region").agg(pl.col("amount").sum())
# Lazy: builds a plan, optimizes, then runs on .collect()
result = (
pl.scan_csv("sales.csv")
.filter(pl.col("amount") > 100)
.group_by("region")
.agg(pl.col("amount").sum())
.collect()
)
Lazy mode lets the optimizer push filters down to the scan and read only needed columns — often a large speedup on big files. Prefer scan_* + .collect() for real workloads.
Expressions (the core API)
| Expression | Does |
|---|
pl.col("x") | Reference a column |
pl.col("x").sum() | Aggregate |
pl.col("x").alias("y") | Rename output |
pl.col("*") | All columns |
pl.lit(5) | Literal value |
pl.when(cond).then(a).otherwise(b) | Conditional |
Selecting & Filtering
| Task | Code |
|---|
| Select columns | df.select("a", "b") |
| Computed column | df.with_columns((pl.col("a") * 2).alias("a2")) |
| Filter rows | df.filter(pl.col("a") > 10) |
| Multiple conditions | df.filter((pl.col("a") > 10) & (pl.col("b") == "x")) |
| Head/tail | df.head(10), df.tail(10) |
| Sort | df.sort("a", descending=True) |
| Unique | df.unique(subset=["a"]) |
Aggregation & Grouping
(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"),
))
| Aggregation | 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, Lists
| Namespace | Example |
|---|
.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
| Target | Code |
|---|
| pandas | df.to_pandas() / pl.from_pandas(pdf) |
| Arrow | df.to_arrow() |
| NumPy | df.to_numpy() |
| DuckDB | Query Polars frames directly from DuckDB |
Polars vs pandas
| Aspect | Polars | pandas |
|---|
| Engine | Rust, multi-threaded | Python/C, mostly single-threaded |
| Memory | Arrow columnar | NumPy-backed |
| Lazy optimizer | Yes | No |
| API | Expression-based, composable | Index-centric |
| Ecosystem | Growing fast | Vast, mature |
For SQL-first analytics over files, pair with DuckDB; pandas remains valuable for its ecosystem breadth.
Resources