Learn  /  What's new

What's new in Polars 2.1: out-of-core group-by, recursive SQL and Iceberg writes

Three weeks after Polars 2.0, version 2.1 lets huge group-bys spill to disk, makes Polars SQL far more complete and adds Iceberg table management. We tested the headline features.

What's new7 min readUpdated 28 October 2026
PythonWhat's new
All guides and cheat sheets

Polars 2.0 made the streaming engine the default and promoted SQL to a first-class feature. Polars 2.1, released on 25 October 2026, builds on both: group-bys and window functions can now spill to disk when data does not fit in memory, the SQL interface gains recursive queries, ASOF joins and temporary tables, and Polars can now create, overwrite and delete Apache Iceberg tables. The Polars team counts close to 90 performance improvements. We installed it and ran the examples below.

90Performance improvements"close to 90" pull requests
8MGroups on a 50 MB budgetour test, finished in 2.3 s
3.11Minimum Python3.10 support dropped

Group-bys that spill to disk

With the streaming engine, a group-by with millions of distinct keys used to need all those groups in memory. In 2.1 the group-by and the over window function can run out of core: when memory runs short, Polars writes partial results to disk and reads them back. You can set a memory budget with an environment variable and turn on metrics to see what happened.

Python
import os
os.environ["POLARS_OOC_MEMORY_BUDGET_MB"] = "50"   # set before importing Polars
os.environ["POLARS_OOC_LOG_METRICS"] = "1"

import polars as pl

n = 8_000_000
lf = pl.LazyFrame({
    "k": pl.int_range(n, eager=True).shuffle(seed=0),
    "v": pl.int_range(n, eager=True),
})
result = lf.group_by("k").agg(pl.col("v").sum()).collect()

On our 2-core test machine, 8 million unique groups with a 50 MB budget finished in 2.3 seconds. The metrics showed about 35 MB written to disk and read back, with every spill succeeding. For analysts this means big aggregations on a laptop are far less likely to crash with an out-of-memory error.

Polars SQL catches up

If you prefer SQL to expressions, 2.1 closes many gaps. Recursive CTEs, ASOF joins, temporary tables and common date and text functions all work now.

Python
import polars as pl

# Recursive CTE: count from 1 to 5
pl.SQLContext().execute("""
    WITH RECURSIVE nums(n) AS (
        SELECT 1 AS n
        UNION ALL
        SELECT n + 1 FROM nums WHERE n < 5
    )
    SELECT n FROM nums
""").collect()

# ASOF join: the latest quote at or before each trade
trades = pl.DataFrame({"t": [1, 5, 10], "sym": ["A", "A", "A"], "qty": [100, 200, 300]})
quotes = pl.DataFrame({"t": [0, 4, 9], "sym": ["A", "A", "A"], "price": [10.0, 10.5, 11.0]})
ctx = pl.SQLContext(trades=trades, quotes=quotes)
ctx.execute("""
    SELECT trades.t, qty, price
    FROM trades
    ASOF JOIN quotes MATCH_CONDITION (trades.t >= quotes.t)
    ON trades.sym = quotes.sym
""").collect()
# prices 10.0, 10.5 and 11.0
WITH RECURSIVE
Recursive CTEs for hierarchies, such as org charts or bills of materials, and number sequences.
ASOF JOIN
Match each row to the nearest earlier row, as in Snowflake and DuckDB, using MATCH_CONDITION.
CREATE TEMP TABLE
Save an intermediate result inside an SQLContext and query it later.
generate_series and range
Build sequences of numbers directly in SQL.
DATE_TRUNC and CURRENT_DATE
Truncate dates to a month or week and get today's date. DATE_TRUNC returns a datetime.
REGEXP_REPLACE
Replace text with a pattern. In our test it replaced the first match only, like PostgreSQL without the g flag.
Subqueries in JOIN ... ON
Correlated and uncorrelated subqueries now work inside join conditions.

Apache Iceberg: from reading to managing tables

Polars could already read and append to Iceberg tables. 2.1 adds most of what you need to manage them:

Writing

sink_iceberg
  • create_if_not_exists and create_namespace_if_not_exists
  • overwrite_filter and dynamic partition overwrite
  • Tables with bucket-partitioned fields

Rebuild only the partitions you changed.

Catalogs

SQL, unstable
  • register_catalog attaches an Iceberg catalog to an SQLContext
  • CREATE TABLE with column definitions and DROP TABLE [PURGE]
  • pl.delete_iceberg to delete rows
  • Marked unstable, so the API may still change

Manage Iceberg tables without Spark for small and medium workloads.

New to Iceberg? Read Apache Iceberg v3, explained.

Smaller additions

  • Encrypted Parquet files can be read and written (unstable), and GeoParquet read and write is experimental.
  • join_where supports full joins, so inequality joins can keep unmatched rows on both sides.
  • Expr.truncdiv divides and rounds towards zero: -7 divided by 2 gives -3, where // gives -4.
  • Faster creation of wide struct columns and much lower memory use in from_rows and from_dicts.
  • Filters using str.contains with a literal, and SQL LIKE, can now skip Parquet data early.

Breaking changes

Only two. First, Python 3.10 is no longer supported. Second, adding a number to a string in eager mode now raises an error instead of joining them as text:

Python
pl.Series([1, 2]) + "4"
# Polars 2.0: ["14", "24"]
# Polars 2.1: InvalidOperationError: arithmetic on dtypes i64 and str is not allowed

If you relied on that, cast explicitly with .cast(pl.String) and use pl.concat_str.

How to upgrade

  1. 1

    Check your Python version

    Polars 2.1 needs Python 3.11 or newer.

  2. 2

    Upgrade

    Run pip install -U polars, or uv add polars.

  3. 3

    Search for string arithmetic

    Look for code that adds numbers and strings, and make the cast explicit.

  4. 4

    Try a memory budget

    For large group-bys, set POLARS_OOC_MEMORY_BUDGET_MB and compare speed and stability.

  5. 5

    Avoid unstable APIs in production

    Encryption, register_catalog and delete_iceberg may change in later releases.

Check your understanding

1/3What does out-of-core group-by let Polars do?

2/3Which SQL feature was added in Polars 2.1?

3/3What happens to pl.Series([1, 2]) + "4" in Polars 2.1?

More on Python data tools: what's new in Polars 2.0, what's new in pandas 3.1, pandas vs Polars in 2026 and the Polars cheat sheet.

Sources: Polars blog, "Announcing Polars 2.1" (27 October 2026), the Polars 2.1.0 release on PyPI (25 October 2026), and our own tests with Polars 2.0.0 and 2.1.0 on Python 3.13. Timings are from one 2-core machine and are indicative only.

Alessandro Ecclesie AgazziWritten by Alessandro Ecclesie Agazzi, analytics engineer in London. Updated 28 October 2026. About me

Found this useful?

New guides and "what's new" explainers on data and AI land here regularly. Follow along on LinkedIn, or share this one with a colleague.