C.W.K.
Stream
Lesson 05 of 06 · published

Pandas vs Polars vs DuckDB vs SQL — When to Reach for Each

~13 min · foundation, tools, decision-framework

Level 0Curious Reader
0 XP0/47 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

The number-one beginner mistake is picking the most-talked-about tool and forcing every problem to fit. The number-one professional skill is picking the right tool for the shape of the work in front of you. Here's a working decision framework.

SituationReach forWhy
Interactive exploration, <100M rows, mixed messy typesPandasLargest ecosystem, every analytics library speaks it, REPL-friendly.
Same shape but 100M10B rows on a laptopPolars (lazy)Streaming engine, query optimizer, parallel execution. Pandas would OOM.
Analytics over Parquet/Arrow with familiar SQLDuckDBIn-process, no server, queries files directly, gorgeous performance per dollar.
The data is already in Postgres/BigQuery/SnowflakeSQL (in the warehouse)Don't pull it out and back in. Compute where the data lives. Use dbt for organization.
Numerical heavy lifting, no labels neededNumPyLess overhead than Pandas; vectorization is direct.
Truly distributed at petabyte scaleSpark / BigQuery / SnowflakeNot in this quest. The principles transfer.

The unspoken default

If you're starting from scratch in 2026 and the data lives in files: store as Parquet, query with DuckDB, manipulate with Pandas or Polars depending on size, validate with Pandera. That stack will carry the vast majority of practitioners for the vast majority of problems, on a single machine, for years before you need anything heavier.

The point isn't to memorize a table. It's to develop the reflex of asking what shape is this work? before you start typing import.

Code

Same task, four tools — pick the one that matches your situation·python
# Goal: monthly revenue from a 5GB Parquet file. Four legitimate ways:

# 1. Pandas — fits in memory, easiest for someone who knows Pandas
import pandas as pd
df = pd.read_parquet('orders.parquet', columns=['order_date', 'amount_usd'])
pandas_result = (df.assign(month=lambda d: d['order_date'].dt.to_period('M'))
                   .groupby('month', as_index=False)['amount_usd'].sum())

# 2. Polars (lazy) — streams, doesn't load whole file
import polars as pl
polars_result = (pl.scan_parquet('orders.parquet')
                   .group_by(pl.col('order_date').dt.truncate('1mo').alias('month'))
                   .agg(pl.col('amount_usd').sum())
                   .sort('month')
                   .collect())

# 3. DuckDB — SQL directly on the file, no DataFrame loaded at all
import duckdb
duck_result = duckdb.sql('''
    SELECT date_trunc('month', order_date) AS month,
           SUM(amount_usd) AS revenue
    FROM 'orders.parquet'
    GROUP BY 1 ORDER BY 1
''').to_df()

# 4. Pure SQL in a real warehouse — leave the data where it lives
warehouse_sql = '''
    SELECT date_trunc('month', order_date) AS month, SUM(amount_usd) AS revenue
    FROM analytics.orders
    GROUP BY 1 ORDER BY 1
'''  # run via your warehouse client; let dbt own the file.

External links

Exercise

For one project you've worked on (school, work, side), write a one-paragraph rationale: which of the four tools (Pandas / Polars / DuckDB / SQL-in-warehouse) you'd pick if you redid it today, and why. The goal is to practice the reflex, not to be right.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.